Skip to main content
Learn how to send and receive tokens on the Spark network. This guide covers single transfers, batch transfers, airdrops, and monitoring token balances.
Token Transfers

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);

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);

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);

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);