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-deps

Copy the environment file and fill in your values:

cp .env.example .env.local

Run the development server:

npm run dev
# Open http://localhost:3000

2. Environment Setup

Copy .env.example to .env.local and fill in:

VariableDescriptionRequired
CIRCLE_API_KEYCircle developer API key — from console.circle.com
CIRCLE_WALLET_SET_IDCircle wallet set UUID for developer-controlled wallets
CIRCLE_ENTITY_SECRET32-byte hex entity secret for signing (never expose to browser)
NEXT_PUBLIC_CIRCLE_APP_IDPublic Circle App ID for browser SDK integration
NEXT_PUBLIC_ARC_TESTNET_RPC_URLArc Testnet JSON-RPC endpoint URL
NEXT_PUBLIC_ARC_TESTNET_CHAIN_IDArc Testnet EVM chain ID (verify from docs.arc.network)
NEXT_PUBLIC_USDC_CONTRACT_ADDRESSUSDC contract on Arc Testnet (native gas token)
NEXT_PUBLIC_EURC_CONTRACT_ADDRESSEURC ERC-20 contract on Arc Testnet
NEXT_PUBLIC_MULTISEND_CONTRACT_ADDRESSDeployed MultiSend.sol contract addressOptional
NEXT_PUBLIC_WALLETCONNECT_PROJECT_IDWalletConnect Cloud project ID
Never expose 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=5042002
Verify the chain ID from the official Arc docs before deploying to production.

Add Arc Testnet to MetaMask manually:

Network NameArc Testnet
RPC URLhttps://rpc.testnet.arc.network
Chain ID5042002
Currency SymbolUSDC
Block Explorerhttps://testnet.arcscan.app

4. Circle SDK Setup

  1. 1Sign up at console.circle.com and create an API key.
  2. 2Create a Developer-Controlled Wallet Set and note the Wallet Set ID.
  3. 3Generate a 32-byte entity secret: openssl rand -hex 32 — store it securely.
  4. 4Upload your entity secret's public key ciphertext to Circle Console.
  5. 5Get your App ID from Circle Console → App Settings → set as NEXT_PUBLIC_CIRCLE_APP_ID.
  6. 6Fetch Circle token IDs for Arc Testnet via GET /v1/w3s/token/catalog and set them in circle-adapter.ts.
The Circle adapter at 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-wallets

5. 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-testnet

After deployment, set the contract address in your .env.local:

NEXT_PUBLIC_MULTISEND_CONTRACT_ADDRESS=0xYourDeployedAddress
If this variable is not set, the app falls back to sequential ERC-20 transfers automatically.

6. Token Contract Addresses

Set the USDC and EURC contract addresses for Arc Testnet:

NEXT_PUBLIC_USDC_CONTRACT_ADDRESS=0x3600000000000000000000000000000000000000
NEXT_PUBLIC_EURC_CONTRACT_ADDRESS=0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a
USDC is the native gas token on Arc Testnet (isNative: 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-1155

Features:

  • 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 address
No API key required — resolved directly on-chain via the ArcNameHub smart contract.

9. 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,USDC

For 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 production
Add all NEXT_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

Support & Resources