Learn how to send and receive tokens on the Spark network. This guide covers single transfers, batch transfers, airdrops, and monitoring token balances.
Transfer Overview
Token transfers on Spark allow you to send tokens to any Spark address. You can perform single transfers or batch multiple transfers in a single transaction.
Single Transfers Send tokens to one recipient at a time
Batch Transfers Send tokens to multiple recipients in one transaction
Single Token Transfers
Transfer tokens to a single recipient using the transferTokens() method.
transferTokens(params)
Transfers tokens to another Spark Address.
const transactionId = await wallet . transferTokens ({
tokenIdentifier: "btkn1qwertyuiopasdfghjklzxcvbnm1234567890abcdef" ,
tokenAmount: BigInt ( 100000 ),
receiverSparkAddress: "spark1s2h8nvdh4h7h43tcwjdcysf7v0fprz5uh6vshs4n0tvhgzz2xgcqpg8yqv7"
});
console . log ( "Spark Transaction ID:" , transactionId );
Bech32m token identifier (e.g., btkn1…) of the token to transfer
Amount of tokens to transfer in base units
Recipient’s Spark Address
selectedOutputs
OutputWithPreviousTransactionData[]
(Optional) Specific outputs to use for transfer
Transaction ID of the token transfer
Transfer Examples
Basic Transfer
// Transfer 100 tokens (6 decimals = 100,000,000 base units)
const transferTx = await wallet . transferTokens ({
tokenIdentifier: "btkn1..." ,
tokenAmount: BigInt ( 100000000 ), // 100.000000 tokens
receiverSparkAddress: "spark1..."
});
console . log ( "Transfer completed:" , transferTx );
Transfer with Specific Outputs
// Use specific outputs for the transfer
const transferTx = await wallet . transferTokens ({
tokenIdentifier: "btkn1..." ,
tokenAmount: BigInt ( 50000000 ), // 50.000000 tokens
receiverSparkAddress: "spark1..." ,
selectedOutputs: [ /* specific output data */ ]
});
console . log ( "Transfer with selected outputs:" , transferTx );
Batch Token Transfers
Transfer tokens to multiple recipients in a single transaction using batchTransferTokens().
batchTransferTokens(params)
Transfers tokens to multiple recipients in a single transaction.
const batchTx = await wallet . batchTransferTokens ({
tokenIdentifier: "btkn1qwertyuiopasdfghjklzxcvbnm1234567890abcdef" ,
transfers: [
{ tokenAmount: BigInt ( 100000 ), receiverSparkAddress: "spark1..." },
{ tokenAmount: BigInt ( 200000 ), receiverSparkAddress: "spark1..." },
{ tokenAmount: BigInt ( 50000 ), receiverSparkAddress: "spark1..." }
]
});
console . log ( "Batch transfer completed:" , batchTx );
Bech32m token identifier of the token to transfer
Array of transfer objects containing recipient addresses and amounts
selectedOutputs
OutputWithPreviousTransactionData[]
(Optional) Specific outputs to use for transfer
Transaction ID of the batch transfer
Batch Transfer Examples
Airdrop Distribution
// Airdrop tokens to multiple users
const airdropTx = await wallet . batchTransferTokens ({
tokenIdentifier: "btkn1..." ,
transfers: [
{ tokenAmount: BigInt ( 1000000 ), receiverSparkAddress: "spark1user1..." },
{ tokenAmount: BigInt ( 1000000 ), receiverSparkAddress: "spark1user2..." },
{ tokenAmount: BigInt ( 1000000 ), receiverSparkAddress: "spark1user3..." },
{ tokenAmount: BigInt ( 1000000 ), receiverSparkAddress: "spark1user4..." },
{ tokenAmount: BigInt ( 1000000 ), receiverSparkAddress: "spark1user5..." }
]
});
console . log ( "Airdrop completed:" , airdropTx );
Payroll Distribution
// Distribute different amounts to team members
const payrollTx = await wallet . batchTransferTokens ({
tokenIdentifier: "btkn1..." ,
transfers: [
{ tokenAmount: BigInt ( 5000000 ), receiverSparkAddress: "spark1manager..." }, // 5 tokens
{ tokenAmount: BigInt ( 3000000 ), receiverSparkAddress: "spark1senior..." }, // 3 tokens
{ tokenAmount: BigInt ( 2000000 ), receiverSparkAddress: "spark1junior..." }, // 2 tokens
{ tokenAmount: BigInt ( 1000000 ), receiverSparkAddress: "spark1intern..." } // 1 token
]
});
console . log ( "Payroll distributed:" , payrollTx );
Reward Distribution
// Distribute rewards based on user activity
const rewardsTx = await wallet . batchTransferTokens ({
tokenIdentifier: "btkn1..." ,
transfers: [
{ tokenAmount: BigInt ( 10000000 ), receiverSparkAddress: "spark1topuser..." }, // 10 tokens
{ tokenAmount: BigInt ( 5000000 ), receiverSparkAddress: "spark1activeuser..." }, // 5 tokens
{ tokenAmount: BigInt ( 2500000 ), receiverSparkAddress: "spark1regularuser..." } // 2.5 tokens
]
});
console . log ( "Rewards distributed:" , rewardsTx );
Receiving Tokens
Receiving tokens on Spark requires no action. They show up as soon as they’re sent to your address.
Check Token Balance
getBalance()
Gets the current balance of tokens and Bitcoin.
const balance = await wallet . getBalance ();
console . log ( "Bitcoin balance:" , balance . balance , "sats" );
console . log ( "Token balances:" , balance . tokenBalances );
Current Bitcoin balance in satoshis
tokenBalances
Map<string, { balance: bigint, tokenInfo: TokenInfo, bech32mTokenIdentifier: string }>
required
Map of token balances by token public key with token information and Bech32m token identifier
Monitor Incoming Transfers
// Set up event listeners for incoming transfers
wallet . on ( "transfer:claimed" , ( transferId , updatedBalance ) => {
console . log ( `Transfer ${ transferId } claimed. New balance: ${ updatedBalance } sats` );
});
// Check balance after receiving tokens
const balance = await wallet . getBalance ();
balance . tokenBalances . forEach (( tokenData , tokenPublicKey ) => {
console . log ( `Token ${ tokenData . tokenInfo . tokenName } : ${ tokenData . balance } base units` );
console . log ( `Token ID: ${ tokenData . bech32mTokenIdentifier } ` );
});
Token Amount Calculations
Understanding how to calculate token amounts based on decimals:
6 Decimals (USDC) 1 token = 1,000,000 base units
Example: 1.5 USDC = 1,500,000 base units
8 Decimals (Bitcoin) 1 token = 100,000,000 base units
Example: 0.5 BTC = 50,000,000 base units
18 Decimals (Ethereum) 1 token = 1,000,000,000,000,000,000 base units
Example: 0.1 ETH = 100,000,000,000,000,000 base units
Amount Calculation Examples
// 6 decimal token (like USDC)
const usdcAmount = BigInt ( 1500000 ); // 1.5 USDC
// 8 decimal token (like Bitcoin)
const btcAmount = BigInt ( 50000000 ); // 0.5 BTC
// 18 decimal token (like Ethereum)
const ethAmount = BigInt ( 100000000000000000 ); // 0.1 ETH
// Transfer examples
await wallet . transferTokens ({
tokenIdentifier: "btkn1usdc..." ,
tokenAmount: usdcAmount ,
receiverSparkAddress: "spark1..."
});
await wallet . transferTokens ({
tokenIdentifier: "btkn1btc..." ,
tokenAmount: btcAmount ,
receiverSparkAddress: "spark1..."
});
Transfer Limitations
Currently, only single-token transfers are supported. Sending multiple different tokens (e.g., token_a, token_b, token_c) in one transaction isn’t yet possible.
What This Means
Single Token Per Transaction : Each transfer can only include one type of token
Multiple Recipients : You can send the same token to multiple recipients using batch transfers
Different Tokens : To send different tokens, you need separate transactions
Workarounds
// To send multiple different tokens, use separate transactions
const tokenATx = await wallet . transferTokens ({
tokenIdentifier: "btkn1tokenA..." ,
tokenAmount: BigInt ( 1000000 ),
receiverSparkAddress: "spark1..."
});
const tokenBTx = await wallet . transferTokens ({
tokenIdentifier: "btkn1tokenB..." ,
tokenAmount: BigInt ( 2000000 ),
receiverSparkAddress: "spark1..."
});
console . log ( "Token A sent:" , tokenATx );
console . log ( "Token B sent:" , tokenBTx );
Error Handling
Implement robust error handling for token transfers:
async function safeTransfer ( tokenIdentifier , tokenAmount , receiverAddress ) {
try {
const txId = await wallet . transferTokens ({
tokenIdentifier ,
tokenAmount ,
receiverSparkAddress: receiverAddress
});
console . log ( "Transfer successful:" , txId );
return txId ;
} catch ( error ) {
console . error ( "Transfer failed:" , error );
// Handle specific error types
if ( error . message . includes ( "insufficient balance" )) {
console . error ( "Not enough tokens to complete transfer" );
} else if ( error . message . includes ( "invalid address" )) {
console . error ( "Invalid recipient address" );
} else if ( error . message . includes ( "frozen" )) {
console . error ( "Token is frozen for this address" );
}
throw error ;
}
}
Best Practices
Transfer Validation
Verify Addresses : Always validate recipient Spark addresses before transferring
Check Balances : Ensure sufficient token balance before initiating transfers
Handle Errors : Implement comprehensive error handling for all transfer operations
Test on Regtest : Always test transfer logic on regtest before mainnet deployment
Batch Transfer Optimization
Group Recipients : Use batch transfers for multiple recipients of the same token
Reasonable Batch Sizes : Don’t create batches that are too large (monitor transaction size)
Error Handling : Handle partial failures in batch transfers gracefully
Security Considerations
Private Key Protection : Keep your wallet’s private keys secure
Address Verification : Double-check recipient addresses to prevent fund loss
Amount Validation : Verify transfer amounts before confirming transactions
Complete Example
import { IssuerSparkWallet } from "@buildonspark/issuer-sdk" ;
async function demonstrateTokenTransfers () {
try {
// 1. Initialize issuer wallet
const { wallet } = await IssuerSparkWallet . initialize ({
options: { network: "REGTEST" }
});
console . log ( "Issuer wallet initialized for token transfers" );
// 2. Get token metadata
const metadata = await wallet . getIssuerTokenMetadata ();
console . log ( "Token:" , metadata . tokenName , "(" , metadata . tokenSymbol , ")" );
console . log ( "Token ID:" , metadata . bech32mTokenIdentifier );
// 3. Check current balance
const balance = await wallet . getIssuerTokenBalance ();
console . log ( "Current token balance:" , balance . balance , "base units" );
// 4. Single transfer
const singleTransferTx = await wallet . transferTokens ({
tokenIdentifier: metadata . bech32mTokenIdentifier ,
tokenAmount: BigInt ( 1000000 ), // 1.000000 tokens (6 decimals)
receiverSparkAddress: "spark1..." // Replace with actual address
});
console . log ( "Single transfer completed:" , singleTransferTx );
// 5. Batch transfer (airdrop)
const airdropTx = await wallet . batchTransferTokens ({
tokenIdentifier: metadata . bech32mTokenIdentifier ,
transfers: [
{ tokenAmount: BigInt ( 500000 ), receiverSparkAddress: "spark1user1..." },
{ tokenAmount: BigInt ( 500000 ), receiverSparkAddress: "spark1user2..." },
{ tokenAmount: BigInt ( 500000 ), receiverSparkAddress: "spark1user3..." }
]
});
console . log ( "Airdrop completed:" , airdropTx );
// 6. Check balance after transfers
const newBalance = await wallet . getIssuerTokenBalance ();
console . log ( "Balance after transfers:" , newBalance . balance , "base units" );
// 7. Set up event monitoring
wallet . on ( "transfer:claimed" , ( transferId , updatedBalance ) => {
console . log ( `Transfer ${ transferId } claimed. New balance: ${ updatedBalance } sats` );
});
} catch ( error ) {
console . error ( "Token transfer error:" , error );
}
}
demonstrateTokenTransfers (). catch ( console . error );