Skip to main content
Everything you need before going live with your stablecoin integration.

Best practices

Handle blocklist and pause

Your stablecoin has two safety controls managed by Coinbase that your integration must handle gracefully.
Check both before sending a transaction to avoid unnecessary gas costs on a revert:
const PAUSE_ABI = ["function paused() view returns (bool)"];
const pausable  = new ethers.Contract(TOKEN_ADDRESS, PAUSE_ABI, provider);

const paused      = await pausable.paused();
const fromBlocked = await token.isBlocklisted(fromAddress);
const toBlocked   = await token.isBlocklisted(toAddress);

if (paused)       throw new Error("transfers are paused");
if (fromBlocked)  throw new Error("sender is blocklisted");
if (toBlocked)    throw new Error("recipient is blocklisted");

Token decimals

Always read decimals from the contract rather than hardcoding. Your stablecoin’s decimals are set at deployment and can be between 6 and 18.
const decimals = await token.decimals();
const amount   = ethers.parseUnits("100", decimals);

ERC-20 approvals (Base only)

When building a contract that pulls tokens from users (for example, a payment processor), prefer permit or ERC-3009 over requiring a prior approve transaction — this reduces friction and the number of on-chain steps for your users. If you do use approve, avoid approving MaxUint256 in production:
// Prefer: approve exact amounts
await token.approve(spenderAddress, exactAmount);

// Avoid in production: unlimited approval
await token.approve(spenderAddress, ethers.MaxUint256);

Memo security

Memo values are permanently visible on the public blockchain. Only store non-sensitive references such as hashed or opaque IDs. If you must attach sensitive data, encrypt it off-chain before encoding as bytes32 and decrypt off-chain when reading the event.

Signed transfer security

When accepting signed ERC-3009 authorizations from users:
  1. Always verify authorizationState(from, nonce) is false before presenting a transaction for signature — do not show users a “sign” prompt for a nonce that is already used
  2. Set a reasonable validBefore (minutes to hours, not days) to limit exposure if a signature is compromised
  3. Use receiveWithAuthorization instead of transferWithAuthorization when only the recipient should be able to submit the transaction

Security considerations

  1. Confirm the contract or mint address against Key Addresses before sending transactions
  2. Validate all user-supplied addresses before using them as transfer recipients
  3. Check isBlocklisted on user addresses before building transactions that include them — this prevents predictable reverts
  1. Test on testnet first with small amounts
  2. Cover error paths — blocklisted address, paused contract, expired permit, insufficient balance
  3. Verify memo encoding/decoding end-to-end if your integration uses memos
  1. Never assume success — always wait for confirmation and check the transaction receipt
  2. Distinguish validation errors (do not retry) from transient network errors (safe to retry with backoff)
  3. Provide clear error messages to users — the Troubleshooting guide maps on-chain errors to user-facing messages
Subscribe to relevant events to keep your off-chain systems in sync:
EventAction
TransferUpdate balance records
MemoCorrelate with off-chain orders
PausedHalt outgoing transfers, surface status to users
UnpausedResume transfers
BlocklistStatusUpdatedRe-check affected addresses

Pre-flight checklist

Before sending a transaction in production, verify:
  • Sender has sufficient token balance
  • Sender has sufficient ETH for gas
  • Neither the sender nor recipient is blocklisted (isBlocklisted)
  • Transfers are not paused
  • If using transferFrom: the spender’s allowance covers the amount
  • If using permit: the nonce matches nonces(owner) and the deadline is in the future
  • If using ERC-3009: the nonce has not been used (authorizationState) and validBefore is in the future
  • Contract address matches Key Addresses

Troubleshooting

Common errors and solutions

Examples

Code samples for common scenarios

Reference

Full function and program reference

Quickstart

Back to quickstart