CREATE3 for deterministic addresses and
Circle’s forwarding service to cover remote
execution, so you only need gas on Arc Testnet.
Use the same wallet and salt on every blockchain. The
tokenId is derived
from (deployer address, salt). A different wallet or salt produces a
different tokenId with no onchain error, and the bridge connection is
silently broken.Prerequisites
Before you begin, ensure that you’ve:- Installed Node.js v22+
- Prepared an EVM testnet wallet whose private key you have available. This is
the deployment wallet, and it must be the same wallet on every blockchain
- Added Arc Testnet to your wallet using the Testnet tab of Add Arc to a browser wallet in the Arc documentation
- Funded the wallet with Arc Testnet USDC for gas from the Circle Faucet
Step 1: Set up the project
1.1. Create the project and install dependencies
1.2. Configure TypeScript (optional)
Create atsconfig.json file:
tsconfig.json file:
1.3. Set environment variables
Open.env in your editor and add:
PRIVATE_KEY is the private key for the wallet that deploys the token. You must
use this same wallet on every blockchain where you deploy.
The npm run start command loads variables from .env using Node.js native
env-file support.
Step 2: Configure the script
Createindex.ts and build it in three parts: clients, token params, then the
service ABI used by later steps.
2.1. Set up clients and constants
FORWARD submits the
remote deploy and the destination mint, so no Base wallet is required.
2.2. Define token metadata and DeploymentParams
The salt and deployer address determine your tokenId and they must be
identical on every blockchain. This example sets rateLimit and
maxTransferAmount to type(uint256).max so transfers work once the token
exists. For production caps, see
Configure rate limits and transfer caps.
- Sandbox omits
initialSupply: Encoding a field the live ABI does not expect produces an empty revert. Some production layouts include it—verify the contract ABI for your target. See Contract reference. rateLimit: 0ormaxTransferAmount: 0blocks transfers: Defaults are zero if you omit non-zero values.- Pack addresses with
encodePacked:encodeAbiParameterspads to 32 bytes and breaks the contract’s address decoding.
2.3. Add the CrossChainTokenService ABI
Add the service fragments this script calls:
deployCrossChainToken/deployRemoteCrossChainToken: home CREATE3 deploy and CCTP remote deployCrossChainTokenIdClaimed: source oftokenIdafter home deployresolveTokenAddress/resolveTokenManager: look up deployed addresses bytokenIdcrossChainTransfer: initiate the Arc → Base transfer in step 7
Step 3: Deploy on Arc Testnet
CalldeployCrossChainToken on Arc Testnet. No fee quote is needed—this is a
local CREATE3 deploy. The protocol creates the CrossChainToken ERC-20 and a
NATIVE_CROSSCHAIN_TOKEN TokenManager in the same transaction. Read tokenId
from the CrossChainTokenIdClaimed event on the receipt.
Append steps 3–7 to index.ts in order. They form one try/catch block.
- For native deploys,
CrossChainTokenIdClaimedemitstokenAddressasaddress(0). That is expected. UsetokenIdfrom the event, thenresolveTokenAddressfor the real token address. - If home deploy already succeeded and you re-run this step with the same salt,
the call reverts. Change the salt for a new token, or continue from the remote
deploy using the existing
tokenId.
Step 4: Quote and deploy remotely to Base Sepolia
Fetch an Iris fee quote withmsgType: "DeployTokenMessage", then call
deployRemoteCrossChainToken on Arc targeting Base Sepolia (domain 6). Pass the
same salt, metadata, and DeploymentParams as the home deploy.
name, symbol, and decimals must match the home deploy. The protocol
does not enforce consistency. A mismatch breaks user experience and indexers
with no onchain error.deployCrossChainToken there with the same wallet, salt, and metadata instead
of remote deploy, then continue from mint. That path skips the Iris deploy
quote.
Step 5: Wait for the token on Base Sepolia
PollresolveTokenAddress on Base until it returns a non-zero address. Iris
typically delivers in a few minutes after the Arc transaction confirms.
Step 6: Mint on Arc Testnet
Mint supply to the deployer.tokenMinter in DeploymentParams must be your
address (as in this example).
Step 7: Transfer Arc → Base with Iris FORWARD
Approve the ArcTokenManager, quote a TransferMessage with FORWARD, then
call crossChainTransfer. Iris submits the destination mint so you do not need
to call receiveMessage on Base, and you do not need Base ETH for this step.
FINALITY = 2000 (standard) until that is configured.
To transfer other configured tokens with this same pattern, see
Transfer EURC between Base and Arc
and substitute your tokenId.