Skip to content

Conversation

@hongbo-wei
Copy link

Description

Add a new TypeScript example for auto-mass payouts on BSC/opBNB. The example includes a CSV-driven payout CLI, ERC-20/native support, batching, dry-run validation, and documentation updates plus a sample CSV and env template. This aligns with the Cookbook Challenge wishlist and provides a practical, reproducible payout workflow.

Fixes #N/A

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

How Has This Been Tested?

  • cd typescript/auto-mass-payouts
  • npm install
  • PRIVATE_KEY= RPC_URL=https://bsc-testnet.publicnode.com npm run dry-run

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Copilot AI review requested due to automatic review settings December 28, 2025 19:14
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new TypeScript-based CLI example for automating mass payouts on BSC/opBNB networks. The implementation provides a CSV-driven workflow for distributing BNB or ERC-20 tokens to multiple recipients with safeguards including dry-run validation, configurable batching, and transaction delays.

Key changes:

  • CSV-based payout automation supporting both native BNB and ERC-20 token transfers
  • Comprehensive CLI with dry-run mode, batch processing, and configurable transaction parameters
  • Documentation including setup instructions, usage examples, and security best practices

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web/list.json Adds the auto-mass-payouts example to the web listing with appropriate tags and metadata
typescript/auto-mass-payouts/src/index.ts Main implementation with CSV parsing, argument handling, balance validation, and batch transaction processing
typescript/auto-mass-payouts/package.json Project configuration with dependencies for ethers, CSV parsing, and TypeScript execution
typescript/auto-mass-payouts/tsconfig.json TypeScript compiler configuration for ES2020/ES2022 with strict mode
typescript/auto-mass-payouts/README.md Comprehensive documentation covering features, setup, usage examples, and security notes
typescript/auto-mass-payouts/.env.example Environment template for RPC URL and private key configuration
typescript/auto-mass-payouts/payouts.csv Sample CSV file demonstrating the required address,amount format
typescript/auto-mass-payouts/package-lock.json Dependency lock file ensuring reproducible installs
README.md Updates root README to include the new auto-mass-payouts example in the project listing
package-lock.json Updates unrelated root dependencies (semantic-release ecosystem)
Files not reviewed (1)
  • typescript/auto-mass-payouts/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
} else {
const balance = await provider.getBalance(wallet.address);
if (balance <= total) {
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The balance check uses less than or equal to (balance <= total), which could cause insufficient gas issues. The check should ensure there's a sufficient margin for gas by using strict less than (balance < total) or adding a gas buffer. With the current logic, if balance equals total exactly, there would be no BNB left for gas fees, causing transaction failures.

Copilot uses AI. Check for mistakes.
Comment on lines +370 to +401
for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
const batch = batches[batchIndex];
console.log(`Batch ${batchIndex + 1}/${batches.length}`);

for (const recipient of batch) {
let tx: ethers.TransactionResponse;
if (token) {
tx = await token.transfer(recipient.address, recipient.amount);
} else {
tx = await wallet.sendTransaction({
to: recipient.address,
value: recipient.amount
});
}

console.log(
`Sent ${ethers.formatUnits(recipient.amount, decimals)} ${symbol} to ${
recipient.address
} | tx ${tx.hash}`
);

if (options.confirmations > 0) {
await tx.wait(options.confirmations);
}

if (options.delayMs > 0) {
await sleep(options.delayMs);
}
}
}

console.log("All payouts completed.");
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for transaction failures within the batch loop. If a transaction fails during batch processing, the script will exit without reporting which recipients were successfully paid and which remain unpaid. Consider adding try-catch blocks around individual transactions and implementing a failure tracking mechanism to allow users to resume from failed transactions or generate a report of completed vs. failed payouts.

Suggested change
for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
const batch = batches[batchIndex];
console.log(`Batch ${batchIndex + 1}/${batches.length}`);
for (const recipient of batch) {
let tx: ethers.TransactionResponse;
if (token) {
tx = await token.transfer(recipient.address, recipient.amount);
} else {
tx = await wallet.sendTransaction({
to: recipient.address,
value: recipient.amount
});
}
console.log(
`Sent ${ethers.formatUnits(recipient.amount, decimals)} ${symbol} to ${
recipient.address
} | tx ${tx.hash}`
);
if (options.confirmations > 0) {
await tx.wait(options.confirmations);
}
if (options.delayMs > 0) {
await sleep(options.delayMs);
}
}
}
console.log("All payouts completed.");
const successfulRecipients: typeof recipients = [];
const failedRecipients: { recipient: (typeof recipients)[number]; error: unknown }[] = [];
for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
const batch = batches[batchIndex];
console.log(`Batch ${batchIndex + 1}/${batches.length}`);
for (const recipient of batch) {
try {
let tx: ethers.TransactionResponse;
if (token) {
tx = await token.transfer(recipient.address, recipient.amount);
} else {
tx = await wallet.sendTransaction({
to: recipient.address,
value: recipient.amount
});
}
console.log(
`Sent ${ethers.formatUnits(recipient.amount, decimals)} ${symbol} to ${
recipient.address
} | tx ${tx.hash}`
);
if (options.confirmations > 0) {
await tx.wait(options.confirmations);
}
if (options.delayMs > 0) {
await sleep(options.delayMs);
}
successfulRecipients.push(recipient);
} catch (error) {
failedRecipients.push({ recipient, error });
console.error(
`Failed to send payout to ${recipient.address}: ${
error instanceof Error ? error.message : error
}`
);
continue;
}
}
}
if (failedRecipients.length === 0) {
console.log("All payouts completed.");
} else {
console.warn(
`Payouts completed with some failures. Successful: ${successfulRecipients.length}, Failed: ${failedRecipients.length}.`
);
console.warn("Failed recipients:");
for (const { recipient, error } of failedRecipients) {
console.warn(
` ${recipient.address}: ${
error instanceof Error ? error.message : error
}`
);
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +20
{
"name": "auto-mass-payouts",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"payout": "tsx src/index.ts",
"dry-run": "tsx src/index.ts --csv payouts.csv --dry-run"
},
"dependencies": {
"csv-parse": "^5.5.0",
"dotenv": "^16.4.5",
"ethers": "^6.12.1"
},
"devDependencies": {
"@types/node": "^22.10.2",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
}
}
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing .gitignore file for the auto-mass-payouts project. This is a security concern as sensitive files like .env, node_modules, and build artifacts could accidentally be committed. A .gitignore file should be added to exclude at minimum: node_modules/, .env, .env.*, dist/, build/, and *.log files.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant