Overview
Each custom stablecoin is an ERC-20 token that extends the standard with additional capabilities:| Standard | Description |
|---|
| ERC-20 | Fungible token interface |
| ERC-2612 | Permit — gasless approvals via EIP-712 signatures |
| ERC-3009 | Transfer with authorization — meta-transaction transfers |
| ERC-7572 | Contract-level metadata URI |
| ERC-165 | Interface detection |
Your stablecoin’s contract address is provided during onboarding. See Key Addresses for the testnet CBTUSD address.Coinbase may update the stablecoin implementation over time. Your contract address does not change on update, and the public interface described below remains stable.
ERC-20 functions
transfer
function transfer(address to, uint256 amount) external returns (bool)
Transfers amount tokens from the caller to to.Failure scenarios:| Condition | Error |
|---|
msg.sender or to is blocklisted | AddressBlocklisted(account) |
| Transfers are paused | EnforcedPause() |
| Caller has insufficient balance | ERC20InsufficientBalance |
transferFrom
function transferFrom(address from, address to, uint256 amount) external returns (bool)
Transfers amount tokens from from to to, deducting from the caller’s allowance.Failure scenarios:| Condition | Error |
|---|
msg.sender, from, or to is blocklisted | AddressBlocklisted(account) |
| Transfers are paused | EnforcedPause() |
| Caller’s allowance is insufficient | ERC20InsufficientAllowance |
from has insufficient balance | ERC20InsufficientBalance |
approve
function approve(address spender, uint256 amount) external returns (bool)
Sets the caller’s allowance for spender to amount.Failure scenarios:| Condition | Error |
|---|
msg.sender or spender is blocklisted | AddressBlocklisted(account) |
balanceOf
function balanceOf(address account) external view returns (uint256)
Returns the token balance of account.totalSupply
function totalSupply() external view returns (uint256)
Returns the total token supply currently in circulation.allowance
function allowance(address owner, address spender) external view returns (uint256)
Returns the remaining number of tokens spender is allowed to transfer on behalf of owner.decimals
function decimals() public view returns (uint8)
Returns the number of decimal places used for token amounts (between 6 and 18, set at deployment).name
function name() public view returns (string)
Returns the token name (set at deployment).symbol
function symbol() public view returns (string)
Returns the token symbol (set at deployment).
Memo functions
These functions attach a 32-byte memo to a transfer. The Memo event is emitted immediately after the ERC-20 Transfer event. Memos are useful for correlating on-chain transactions with off-chain order IDs, payment references, or other business records.Memo values are permanently visible on the public blockchain. Do not include sensitive information such as customer names, account numbers, or PII. If you need to attach sensitive data, encrypt it off-chain before encoding it as bytes32 and decrypt it off-chain when reading the event. Use only non-sensitive references (such as a hashed or opaque order ID) as the on-chain memo value.
transferWithMemo
function transferWithMemo(address to, uint256 amount, bytes32 memo) external
Transfers amount from the caller to to with a memo.transferFromWithMemo
function transferFromWithMemo(address from, address to, uint256 amount, bytes32 memo) external
Transfers amount from from to to with a memo, deducting from the caller’s allowance.
ERC-2612 Permit
Permit allows a token holder to authorize a spender using an off-chain EIP-712 signature, so the spender can pull tokens without the holder sending a prior approval transaction.permit
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external
Sets spender’s allowance for owner to value, validated by an EIP-712 signature signed by owner.Failure scenarios:| Condition | Error |
|---|
Signature is invalid or does not match owner | ERC2612InvalidSigner |
block.timestamp > deadline | ERC2612ExpiredSignature |
Nonce does not match nonces(owner) | ERC2612InvalidSigner |
nonces
function nonces(address owner) external view returns (uint256)
Returns the current ERC-2612 nonce for owner. This is a sequential counter — increment after each permit call.DOMAIN_SEPARATOR
function DOMAIN_SEPARATOR() external view returns (bytes32)
Returns the EIP-712 domain separator for this contract. Most signing libraries compute this automatically from name(), "1" (version), chainId, and the contract address — you rarely need to call this directly.
ERC-3009 Transfer with authorization
ERC-3009 meta-transactions let a token holder sign a time-bounded transfer authorization. Any party can submit the transaction on-chain. Nonces are random 32-byte values (not sequential) to support multiple concurrent authorizations.ERC-3009 vs ERC-2612 Permit — when to use each: | ERC-2612 Permit | ERC-3009 |
|---|
| What it authorizes | Approval (spending allowance) | A specific transfer |
| Who submits | Anyone | Anyone (transferWithAuthorization) or recipient only (receiveWithAuthorization) |
| Nonce type | Sequential — one active at a time | Random — multiple concurrent authorizations supported |
| Use case | Gasless approve before a transferFrom | Gasless direct transfer, pay-for-me flows |
| Front-running risk | Low | Use receiveWithAuthorization when front-running is a concern |
transferWithAuthorization
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external
Executes a transfer from from to to validated by a signed authorization. The authorization must be signed by from. Anyone may submit the transaction.| Parameter | Description |
|---|
from | Payer — must sign the authorization |
to | Recipient |
value | Amount to transfer |
validAfter | Earliest unix timestamp at which the authorization is valid (exclusive) |
validBefore | Latest unix timestamp at which the authorization is valid (exclusive) |
nonce | Random 32-byte nonce — must not have been used or canceled |
v, r, s | ECDSA signature components |
Failure scenarios:| Condition | Error |
|---|
block.timestamp <= validAfter | AuthorizationNotYetValid(validAfter) |
block.timestamp >= validBefore | AuthorizationExpired(validBefore) |
| Nonce already used or canceled | AuthorizationAlreadyUsed(authorizer, nonce) |
Signature does not match from | InvalidAuthorization() |
from, to, or msg.sender is blocklisted | AddressBlocklisted(account) |
| Transfers are paused | EnforcedPause() |
receiveWithAuthorization
Same parameters as transferWithAuthorization, but msg.sender must equal to. This prevents front-running by restricting submission to the intended recipient.function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external
cancelAuthorization
function cancelAuthorization(
address authorizer,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external
Voids an unused authorization nonce. The authorizer must sign a CancelAuthorization message. Once canceled, the nonce cannot be used for a transfer.authorizationState
function authorizationState(address authorizer, bytes32 nonce) external view returns (bool)
Returns true if the authorization nonce has been used or canceled.
View functions
isBlocklisted
function isBlocklisted(address account) external view returns (bool)
Returns true if account is on the blocklist. Blocklisted addresses cannot be the sender, recipient, or msg.sender of any transfer or approval.contractURI
function contractURI() external view returns (string)
Returns the contract-level metadata URI (ERC-7572). The URI typically points to a JSON file containing the token’s name, symbol, and image.supportsInterface
function supportsInterface(bytes4 interfaceId) external view returns (bool)
Returns true for ERC-20 (0x36372b07), ERC-2612 (0x9d8ff7da), and all inherited interfaces.
Safety controls
Custom stablecoins include two safety mechanisms managed by Coinbase. These are transparent on-chain — your integration should handle them gracefully.Blocklist
Specific addresses can be blocked from sending or receiving tokens. Any transfer involving a blocklisted address will revert with AddressBlocklisted(account). You can check blocklist status before sending with isBlocklisted(address).Pause
All token transfers can be paused in an emergency. Any transfer attempted while paused will revert with EnforcedPause(). Monitor the Paused and Unpaused events to detect state changes.
Events
| Event signature | Emitted when |
|---|
Transfer(address indexed from, address indexed to, uint256 amount) | Tokens are transferred, minted (from = 0x0), or burned (to = 0x0) |
Approval(address indexed owner, address indexed spender, uint256 amount) | An allowance is set |
Minted(address indexed minter, address indexed to, uint256 amount) | Tokens are minted by Coinbase |
Burned(address indexed burner, uint256 amount) | Tokens are burned by Coinbase |
Memo(bytes32 indexed memo) | A memo-bearing transfer is performed |
AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce) | An ERC-3009 authorization is consumed |
AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce) | An ERC-3009 authorization is canceled |
BlocklistStatusUpdated(address indexed account, bool blocklisted) | An address is added to or removed from the blocklist |
Paused(address account) | All transfers are paused |
Unpaused(address account) | Transfers are resumed |
Errors
| Error | Description |
|---|
AddressBlocklisted(address account) | A blocklisted address attempted a transfer or approval |
EnforcedPause() | Operation attempted while transfers are paused |
ERC20InsufficientBalance | Caller has insufficient balance for the transfer |
ERC20InsufficientAllowance | Caller’s allowance is insufficient for transferFrom |
ERC2612InvalidSigner | Permit signature is invalid or nonce does not match |
ERC2612ExpiredSignature | Permit deadline has passed |
AuthorizationAlreadyUsed(address authorizer, bytes32 nonce) | ERC-3009 nonce has already been consumed or canceled |
AuthorizationNotYetValid(uint256 validAfter) | ERC-3009 authorization submitted before validAfter |
AuthorizationExpired(uint256 validBefore) | ERC-3009 authorization submitted after validBefore |
CallerMustBePayee(address caller, address payee) | receiveWithAuthorization caller is not the to address |
InvalidAuthorization() | ERC-3009 signature is invalid for the given authorizer |