Skip to main content
A Chipi wallet lives on Starknet, but a user’s USDC often doesn’t. With Circle’s Cross-Chain Transfer Protocol (CCTP V2) they can bring native USDC from Base, Arbitrum, Optimism, Polygon or Ethereum straight into their Chipi wallet, and send it back out, with no bridge token and no custodian in between: USDC is burned on one chain and minted on the other by Circle.
Who pays what. Depositing, the user pays gas on the source chain (cents on an L2) and chooses the speed: standard is free and takes about 15–20 minutes from an L2 (Circle waits for Ethereum finality); fast takes about a minute for Circle’s fee (1.3 bps from Base, so $0.0325 on $25), taken from the amount. Chipi pays the Starknet mint. Withdrawing, the user pays no gas (Chipi’s paymaster), Circle takes a fast fee (12 bps out of Starknet), and Chipi pays it back to the recipient under its sponsorship policy (quoteCrossChainWithdrawal says whether it does for this user).

How a deposit works

  1. The user’s EVM wallet signs approve and depositForBurn on the source chain (one prompt when the wallet batches them with EIP-5792, two otherwise). The burn names the Chipi wallet as the 32-byte mintRecipient.
  2. The SDK registers the burn with Chipi as soon as its hash exists. From here the deposit completes even if the page closes: Chipi’s delivery service polls Circle and mints from its own account.
  3. Circle attests the burn once the source chain reaches the chosen finality.
  4. Chipi calls receive_message on Starknet’s MessageTransmitter; the USDC appears in the Chipi wallet.
  5. The SDK records the deposit so it shows in the wallet’s history with its source chain.
A wrong mintRecipient would mint USDC to an address nobody controls, so the recipient is never typed: it is the Chipi wallet, and assertMintRecipient decodes the encoded value back and compares it before anything is signed. Every other failure leaves the money in flight, not lost: a burn with destinationCaller = 0 can be minted by anyone at any time, and a fast attestation that expires is simply requested again.

Fund from Base in one hook

useCrossChainDeposit quotes both speeds, burns through any EIP-1193 wallet, registers, waits for the mint and records it. discoverEvmWallets lists the wallets installed in the browser (EIP-6963).
onStatus reports each step (switching-chain, approving, burning, registering, attesting, minting, recording, done) with how long it usually takes, so you can show a countdown instead of a spinner. The attestation is the long step and follows the route: cctpStepEta("attesting", quote). If depositAsync rejects with a CrossChainDepositError, the burn already happened: the error carries burnTxHash and sourceDomain. Keep them, and either call registerCctpDeposit again or follow the status with useCctpDepositStatus.

Withdraw to Base

useCrossChainWithdraw signs the Starknet burn with the wallet’s own key (passkey or encryptKey, as useCallAnyContract), waits for it, records it and asks Chipi’s relayer to deliver on the destination chain. Withdrawals are always fast: a standard transfer out of Starknet waits hours for the L1 proof.
The burn transaction carries approve and deposit_for_burn with maxFee set to the quoted fee (cctpMaxFee), so Circle can attest fast; Circle only ever takes the real fee. If the relayer step fails after the burn, the error is a CrossChainWithdrawError with the burnTxHash: call finalizeCrossChainWithdrawal again, the relayer is idempotent.

From a server

The same calls exist on ChipiSDK, for a backend that drives deposits itself (a treasury topping up users, a job that resumes a deposit whose browser is gone).
Withdrawals: quoteCrossChainWithdrawal, recordCrossChainWithdrawal, finalizeCrossChainWithdrawal. Chipi reads the burn’s own DepositForBurn event for the amount, destination and recipient, so the values a client records only need to agree with the chain.

Building blocks

Everything the hooks use is exported from @chipi-stack/backend, for apps that need a different flow:

Solana (optional entry point)

Solana is served by @chipi-stack/backend/solana, a separate entry point that needs @solana/web3.js (an optional peer dependency): apps that never touch Solana install nothing new. Standard finality on Solana is about 25 seconds and free, so a deposit from Solana has no fast fee to weigh. @solana/web3.js v1 wants a global Buffer in the browser; polyfill it before importing this entry point. Deposit. The owner’s wallet (Phantom, Solflare, Backpack…) signs Circle’s depositForBurn; the recipient is the Chipi wallet, checked before the transaction exists. depositFromSolana waits until the burn has landed and names a failure instead of leaving the UI polling for a burn that never happened: solana_insufficient_sol (the burn creates a message account whose rent the owner pays, about 0.004 SOL), solana_insufficient_usdc, solana_burn_failed, solana_burn_not_landed. Then register the burn as for an EVM deposit.
Withdraw. The burn on Starknet must name the owner’s USDC token account, never the owner’s key: receiveMessage mints to the account the message names, and a wrong one strands the USDC. buildCctpWithdrawToSolanaCalls derives it, isSolanaWalletAddress refuses a token account or program address typed as the owner, and solanaUsdcAtaExists checks the account exists (Circle does not create it). Record and finalize with recordCrossChainWithdrawal (recipientOwner = the owner) and finalizeCrossChainWithdrawal, as for an EVM destination.
For a relayer, buildSolanaReceiveMessageTx is Circle’s receiveMessage with the source domain, nonce and burn token read from the attested message, and isSolanaCctpMessageUsed says whether a message was already received (the used-nonce account).

What is not here yet

  • A React hook for Solana deposits. useCrossChainDeposit takes EVM wallets; from Solana, call depositFromSolana and registerCctpDeposit as above, then follow the burn with useCctpDepositStatus.
  • Expo: depositFromEvm needs a browser EIP-1193 provider.
  • Arbitrum, Optimism, Polygon and Ethereum are wired and listed, but Chipi has not run a real transfer on them yet (verified: false). Ethereum deposits cost mainnet gas.
✅ Verified against the live API on 2026-10-24.