Instantly share code, notes, and snippets.
Created
December 30, 2025 21:29
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save xescure/bcb9798f9ba34029aeb65ee8531aa8f7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as KeetaNet from "@keetanetwork/keetanet-client"; | |
| /** | |
| * The number of parallel spamming instances to run concurrently. | |
| */ | |
| const PARALLEL_INSTANCES = 1; | |
| /** | |
| * The passphrase for the main account. This will be used to generate the | |
| * account's seed. | |
| * | |
| * DO NOT USE THIS FOR ANY OTHER PURPOSES OTHER THAN DEMONSTRATION. | |
| */ | |
| const ANCHOR_PASSPHRASE = ""; | |
| /** | |
| * The number of addresses to generate and send to in each batch. | |
| */ | |
| const RECIPIENT_COUNT = 500; | |
| /** | |
| * The number of batches to send before creating a new token and re-minting. | |
| */ | |
| const BATCHES_PER_MINT = 10; | |
| /** | |
| * The amount of the new token to send to each recipient in each batch. | |
| */ | |
| const SEND_AMOUNT = 1n; | |
| /** | |
| * The total amount to mint for each new token. This is calculated to be | |
| * exactly enough for all the batches. | |
| */ | |
| const MINT_AMOUNT = | |
| BigInt(RECIPIENT_COUNT) * BigInt(BATCHES_PER_MINT) * SEND_AMOUNT; | |
| /** | |
| * A self-contained function that runs one full cycle of creating a token, | |
| * minting it, and then distributing it in batches. | |
| * @param {number} instanceId - A unique identifier for this parallel instance. | |
| */ | |
| async function runSpamCycle(instanceId) { | |
| // --- KeetaNet Client Setup for this instance --- | |
| const anchorSeed = | |
| await KeetaNet.lib.Account.seedFromPassphrase(ANCHOR_PASSPHRASE); | |
| // Each instance gets its own signer account from the seed to avoid conflicts | |
| const signerAccount = KeetaNet.lib.Account.fromSeed(anchorSeed, instanceId); | |
| console.log( | |
| `[Instance ${instanceId}] Signer account:`, | |
| signerAccount.publicKeyString.get(), | |
| ); | |
| const client = KeetaNet.UserClient.fromNetwork("test", signerAccount); | |
| // await client.recover(false); | |
| // --- Generate recipient addresses once at the start --- | |
| // This is quick, so doing it in each instance is fine. | |
| const recipient_addresses = []; | |
| for (let i = 1; i <= RECIPIENT_COUNT; i++) { | |
| const recipient = KeetaNet.lib.Account.fromSeed( | |
| anchorSeed, | |
| i + RECIPIENT_COUNT * instanceId, | |
| ); // Offset recipients for uniqueness | |
| recipient_addresses.push(recipient); | |
| } | |
| let tokenCreationCycle = 1; | |
| while (true) { | |
| console.log( | |
| `\n\n[Instance ${instanceId}] --- STARTING TOKEN CREATION CYCLE #${tokenCreationCycle} ---`, | |
| ); | |
| let tokenAccount; | |
| // --- 1. Create and Mint a New Token --- | |
| try { | |
| console.log( | |
| `[Instance ${instanceId}] Preparing to create and mint a new token...`, | |
| ); | |
| let creationBuilder = client.initBuilder(); | |
| const pendingTokenAccount = creationBuilder.generateIdentifier( | |
| KeetaNet.lib.Account.AccountKeyAlgorithm.TOKEN, | |
| ); | |
| await creationBuilder.computeBlocks(); | |
| tokenAccount = pendingTokenAccount.account; | |
| console.log( | |
| `[Instance ${instanceId}] Minting ${MINT_AMOUNT} of ${tokenAccount.publicKeyString.get()}`, | |
| ); | |
| creationBuilder.modifyTokenSupply(MINT_AMOUNT, { account: tokenAccount }); | |
| await creationBuilder.computeBlocks(); | |
| creationBuilder.send( | |
| signerAccount, | |
| MINT_AMOUNT, | |
| tokenAccount, | |
| undefined, | |
| { | |
| account: tokenAccount, | |
| }, | |
| ); | |
| // await client.recover(false); | |
| const creationTx = await creationBuilder.publish(); | |
| console.log( | |
| `[Instance ${instanceId}] Token creation successful. Transaction:`, | |
| creationTx, | |
| ); | |
| } catch (error) { | |
| console.error( | |
| `[Instance ${instanceId}] Failed to create token in cycle #${tokenCreationCycle}. Retrying...`, | |
| error, | |
| ); | |
| await new Promise((resolve) => setTimeout(resolve, 5000)); | |
| continue; | |
| } | |
| // --- 2. Inner Loop for Sending Batches --- | |
| console.log( | |
| `[Instance ${instanceId}] Starting to send ${BATCHES_PER_MINT} batches for the new token.`, | |
| ); | |
| for (let batchNum = 1; batchNum <= BATCHES_PER_MINT; batchNum++) { | |
| try { | |
| let batchBuilder = client.initBuilder(); | |
| console.log( | |
| `\n[Instance ${instanceId}] --- Starting Batch #${batchNum} of ${BATCHES_PER_MINT} ---`, | |
| ); | |
| for (const recipient of recipient_addresses) { | |
| batchBuilder.send(recipient, SEND_AMOUNT, tokenAccount); | |
| } | |
| await new Promise((r) => setTimeout(r, 500)); | |
| const batchTx = await batchBuilder.publish(); | |
| console.log( | |
| `[Instance ${instanceId}] Batch #${batchNum} submitted successfully. Transaction:`, | |
| batchTx, | |
| ); | |
| } catch (error) { | |
| console.error( | |
| `[Instance ${instanceId}] An error occurred in batch #${batchNum}. Skipping to next token cycle.`, | |
| error, | |
| ); | |
| break; | |
| } | |
| } | |
| console.log( | |
| `[Instance ${instanceId}] --- FINISHED ALL BATCHES FOR TOKEN CYCLE #${tokenCreationCycle} ---`, | |
| ); | |
| tokenCreationCycle++; | |
| } | |
| } | |
| async function main() { | |
| console.log(`Starting ${PARALLEL_INSTANCES} parallel spammer instances...`); | |
| const promises = []; | |
| for (let i = 0; i < PARALLEL_INSTANCES; i++) { | |
| // Each instance is an async function call that runs independently | |
| promises.push(runSpamCycle(i)); | |
| } | |
| // Promise.all will run all the runSpamCycle functions concurrently. | |
| // If one of them fails with an unhandled error, it will stop the whole script. | |
| await Promise.all(promises); | |
| } | |
| main().catch((err) => { | |
| console.error("A fatal error occurred in the main execution:", err); | |
| process.exit(1); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment