Sender MultiSend — Documentation
Complete guide to setting up, deploying, and using Sender MultiSend on Arc Testnet. Batch-distribute USDC, EURC, and NFTs to hundreds of wallets in a single transaction.
Overview
Sender MultiSend is a gas-optimised batch token and NFT distribution dApp built on Arc Testnet. It supports Circle USDC & EURC (ERC-20), ERC-721, and ERC-1155 NFTs.
Batch Transfers
Send to 200 recipients in one transaction
Pre-flight Checks
Balance & address validation before execution
NFT Support
ERC-721 and ERC-1155 bulk send
1. Quick Start
Clone the repo and install dependencies:
git clone https://github.com/your-org/sender-multisend.git
cd sender-multisend
npm install --legacy-peer-depsCopy the environment file and fill in your values:
cp .env.example .env.localRun the development server:
npm run dev
# Open http://localhost:30002. Environment Setup
Copy .env.example to .env.local and fill in:
| Variable | Description | Required |
|---|---|---|
| CIRCLE_API_KEY | Circle developer API key — from console.circle.com | |
| CIRCLE_WALLET_SET_ID | Circle wallet set UUID for developer-controlled wallets | |
| CIRCLE_ENTITY_SECRET | 32-byte hex entity secret for signing (never expose to browser) | |
| NEXT_PUBLIC_CIRCLE_APP_ID | Public Circle App ID for browser SDK integration | |
| NEXT_PUBLIC_ARC_TESTNET_RPC_URL | Arc Testnet JSON-RPC endpoint URL | |
| NEXT_PUBLIC_ARC_TESTNET_CHAIN_ID | Arc Testnet EVM chain ID (verify from docs.arc.network) | |
| NEXT_PUBLIC_USDC_CONTRACT_ADDRESS | USDC contract on Arc Testnet (native gas token) | |
| NEXT_PUBLIC_EURC_CONTRACT_ADDRESS | EURC ERC-20 contract on Arc Testnet | |
| NEXT_PUBLIC_MULTISEND_CONTRACT_ADDRESS | Deployed MultiSend.sol contract address | Optional |
| NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID | WalletConnect Cloud project ID |
CIRCLE_API_KEY, CIRCLE_ENTITY_SECRET, or CIRCLE_WALLET_SET_ID to the browser. These are server-side only.3. Arc Testnet Configuration
Sender runs on Arc Testnet — an EVM-compatible chain. Confirm the correct values from docs.arc.network.
NEXT_PUBLIC_ARC_TESTNET_RPC_URL=https://rpc.testnet.arc.network
NEXT_PUBLIC_ARC_TESTNET_CHAIN_ID=5042002Add Arc Testnet to MetaMask manually:
| Network Name | Arc Testnet |
| RPC URL | https://rpc.testnet.arc.network |
| Chain ID | 5042002 |
| Currency Symbol | USDC |
| Block Explorer | https://testnet.arcscan.app |
4. Circle SDK Setup
- 1Sign up at console.circle.com and create an API key.
- 2Create a Developer-Controlled Wallet Set and note the Wallet Set ID.
- 3Generate a 32-byte entity secret:
openssl rand -hex 32— store it securely. - 4Upload your entity secret's public key ciphertext to Circle Console.
- 5Get your App ID from Circle Console → App Settings → set as
NEXT_PUBLIC_CIRCLE_APP_ID. - 6Fetch Circle token IDs for Arc Testnet via
GET /v1/w3s/token/catalogand set them incircle-adapter.ts.
src/lib/blockchain/circle-adapter.ts uses raw fetch calls. Replace with the official SDK once configured.Upgrade to official SDK:
npm install @circle-fin/developer-controlled-wallets5. MultiSend Contract Deployment
Deploy contracts/MultiSend.sol to Arc Testnet. The contract supports ERC-20, ERC-721, and ERC-1155 batch transfers. Max batch size: 200 recipients.
# Install Hardhat deps
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox dotenv
# Deploy to Arc Testnet
npx hardhat run scripts/deploy.ts --network arc-testnetAfter deployment, set the contract address in your .env.local:
NEXT_PUBLIC_MULTISEND_CONTRACT_ADDRESS=0xYourDeployedAddress6. Token Contract Addresses
Set the USDC and EURC contract addresses for Arc Testnet:
NEXT_PUBLIC_USDC_CONTRACT_ADDRESS=0x3600000000000000000000000000000000000000
NEXT_PUBLIC_EURC_CONTRACT_ADDRESS=0x89B50855Aa3bE2F677cD6303Cec089B5F319D72aisNative: true). EURC is a standard ERC-20. Both use 6 decimals.7. NFT Holdings Feature
The NFT tab shows all ERC-721 and ERC-1155 tokens held by the connected wallet. Data is fetched from the Blockscout API:
GET https://testnet.arcscan.app/api/v2/addresses/{address}/nft/collections
?type=ERC-721,ERC-1155Features:
- Shows NFT name, symbol, and ERC standard badge
- Copy contract address to clipboard with one click
- Lists all owned token IDs (expandable for large collections)
- Auto-refreshes every 60 seconds
- Supports pagination for wallets with many NFTs
8. Arc Name Hub (.arc Names)
Connected wallets with a registered .arc name (via arcnamehub.xyz) display their name instead of the raw address in the wallet button.
// src/lib/hooks/use-arc-name.ts
// Calls reverseLookup(address) on the ArcNameHub contract
// Returns "yourname.arc" or falls back to truncated address9. CSV Format
Upload or paste CSV in this format for token transfers:
wallet_address,amount,token
0x1234...abcd,10.50,USDC
0xabcd...1234,5.00,EURC
0x9876...dcba,1.00,USDCFor NFT bulk send:
contract_address,token_id,amount,standard,recipient_address
0xNFTContract,1,1,ERC721,0xRecipient1
0xNFTContract,42,5,ERC1155,0xRecipient2- Header row is optional — auto-detected
- Token defaults to USDC if not specified
- Max 200 rows per batch
- Download the Excel template from the Dashboard for a pre-formatted file
10. Deployment to Vercel
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel --prod
# Add server-side env vars
vercel env add CIRCLE_API_KEY production
vercel env add CIRCLE_WALLET_SET_ID production
vercel env add CIRCLE_ENTITY_SECRET productionNEXT_PUBLIC_ variables in the Vercel dashboard under Project → Settings → Environment Variables.11. Security Checklist
- CIRCLE_API_KEY and CIRCLE_ENTITY_SECRET are server-side only — never sent to browser
- All wallet addresses validated with isAddress() from viem
- Amounts sanitized — only digits and single decimal point allowed
- CSV paste input sanitized against XSS (strips HTML tags, JS URIs, event handlers)
- Max batch size: 200 recipients — enforced in UI and smart contract
- Balance check before execution — insufficient tokens flagged pre-flight
- Smart contract uses custom errors for cheaper gas than require strings
- Excess native token refunded automatically in multisendNative
12. Project Architecture
src/ ├── app/ │ ├── layout.tsx # Root layout + Providers │ ├── page.tsx # Landing page │ └── app/ │ ├── layout.tsx # Dashboard shell + AppHeader │ ├── page.tsx # Dashboard (Token + NFT tabs) │ ├── history/page.tsx # Transaction history │ ├── address-book/ # Saved addresses │ └── docs/page.tsx # This page │ ├── components/ │ ├── ui/ # Button, Input, Badge, TokenLogo │ ├── layout/ # AppHeader, WalletConnectButton │ └── dashboard/ │ ├── token-balance-cards # USDC/EURC balances │ ├── recipients-table # Batch recipient input │ ├── summary-panel # Totals + Execute button │ └── wallet-nft-holdings # NFT holdings panel │ ├── lib/ │ ├── blockchain/ │ │ ├── provider.ts # Arc Testnet viem client │ │ ├── tokens.ts # Token registry + formatters │ │ ├── multisend.ts # Batch execution logic │ │ └── circle-adapter.ts # Circle API adapter │ ├── hooks/ │ │ ├── use-token-balances # On-chain token balances │ │ ├── use-batch-execution # Validate + execute batch │ │ ├── use-arc-name # .arc name resolution │ │ └── use-wallet-nfts # NFT holdings via Blockscout │ ├── store/ │ │ └── batch-store.ts # Zustand state │ └── utils/ │ ├── csv.ts # CSV/XLSX parse + export │ └── validation.ts # Address + amount validators │ └── types/index.ts # Domain TypeScript types
