How to transfer an L1 native token to C-Chain as an ERC-20 token?
Deploy L1 on AvaCloud and Enable Interoperability
In AvaCloud, choose “Interoperability” in left column. Activate it if not done so already.
Setup private key for deployment
1 $ cast wallet import my_wallet --interactive 2 3 $ cast wallet list 4 my_wallet (Local)
Clone the ICTT repo
1 $ git clone https://github.com/ava-labs/icm-contracts.git
Setup RPC URLs
1 $ export FUJI_RPC_URL=https://api.avax-test.network/ext/bc/C/rpc 2 $ export L1_RPC_URL=https://subnets.avax.network/qr0407m1ts/testnet/rpc
Deploy Wrapped Native Token Contract
Make sure your wallet has native tokens available on the L1 before doing this step.
1 export NATIVE_TOKEN_SYMBOL=TESTHUB 2 3 mkdir wtoken-foundry 4 cd wtoken-foundry 5 forge init 6 rm script/Counter.s.sol 7 rm src/Counter.sol 8 rm test/Counter.t.sol 9 curl https://raw.githubusercontent.com/ava-labs/wrapped-assets/refs/heads/main/WAVAX.sol -o src/WTOKEN.sol 10 sed -i "" "s/AVAX/$NATIVE_TOKEN_SYMBOL/g" src/WTOKEN.sol 11 12 forge create src/WTOKEN.sol:W$NATIVE_TOKEN_SYMBOL --rpc-url $L1_RPC_URL --account my_wallet --broadcast 13 14 # Deployer: 0xd1aA63B599E39ec6b076Ce5C052DF6609134210A 15 # Deployed to: 0x4C4b94adc6DD05F447c5b51A7FdB69E90dAbFe27 16 # Transaction hash: 0xe298ff6bf142a1ecab2bb6ff61e4eebff90a3dcd247763390297fc43d4afdf39 17 18 # Grab address from "Deployed to:" output of last command 19 export WRAPPED_TOKEN_ADDRESS=0x4C4b94adc6DD05F447c5b51A7FdB69E90dAbFe27 20 21 # Move back to project home directory 22 cd ..
Deploy TokenHome Contract on L1
1 $ cd icm-contracts 2 3 $ forge create \ 4 --broadcast \ 5 --account my_wallet \ 6 --rpc-url $L1_RPC_URL \ 7 contracts/ictt/TokenHome/NativeTokenHomeUpgradeable.sol:NativeTokenHomeUpgradeable --constructor-args 0 8 9 # Deployer: 0xd1aA63B599E39ec6b076Ce5C052DF6609134210A 10 # Deployed to: 0x20c5541eb19573F4C9044969DE818706F70adbc7 11 # Transaction hash: 0x829d6acc6787a80ab32a6a070f3a6a3beb0fda73e76f3099de7416c779b810d9 12 13 # Get addresss from "Deployed to:" output line in above command 14 $ export TOKEN_HOME_ADDRESS=0x20c5541eb19573F4C9044969DE818706F70adbc7
Initialize TokenHome Contract
1 # This is the address that is allowed to perform upgrades in the future, likely the user's address 2 $ export TELEPORTER_MANAGER=$(cast wallet address --account my_wallet) 3 $ echo $TELEPORTER_MANAGER 4 0xd1aA63B599E39ec6b076Ce5C052DF6609134210A 5 6 # Get from AvaCloud: Interoperability: AWS Relayers: Registry Contract Address 7 $ export L1_TELEPORTER_REGISTRY_ADDRESS=0x8488454c36BBB27856DC9F9F2B601FD4cAd26CB1 8 9 $ cast send \ 10 --account my_wallet \ 11 --rpc-url $L1_RPC_URL \ 12 $TOKEN_HOME_ADDRESS 'initialize(address,address,uint256,address)' \ 13 $L1_TELEPORTER_REGISTRY_ADDRESS $TELEPORTER_MANAGER 1 $WRAPPED_TOKEN_ADDRESS 14 15 status 1 (success) 16 transactionHash 0x28f0fe55c452262954bf02461c371451da9591058e0a57a4026852347883536f
Verify Wrapped Native Token ERC20 Contract Address Is Properly Set In TokenHome Contract
1 # This hex value with extra-zero padding matches the ERC20_ADDRESS shown below: 2 $ cast call --rpc-url $L1_RPC_URL $TOKEN_HOME_ADDRESS "getTokenAddress()" 3 0x0000000000000000000000004c4b94adc6dd05f447c5b51a7fdb69e90dabfe27 4 5 $ echo $WRAPPED_TOKEN_ADDRESS 6 0x4C4b94adc6DD05F447c5b51A7FdB69E90dAbFe27
Deploy TokenRemote Contract on C-Chain
1 $ forge create \ 2 --broadcast \ 3 --account my_wallet \ 4 --rpc-url $FUJI_RPC_URL \ 5 contracts/ictt/TokenRemote/ERC20TokenRemoteUpgradeable.sol:ERC20TokenRemoteUpgradeable --constructor-args 0 6 7 # Deployer: 0xd1aA63B599E39ec6b076Ce5C052DF6609134210A 8 # Deployed to: 0x9984F0B5CD6684408023E9db7c651f70A7d8893d 9 # Transaction hash: 0xe7ef8d0cb91cb039719915f1b15bcfb89d7197766f258686db534eac71fc22a2 10 11 # Get addresss from "Deployed to:" output line in above command 12 $ export TOKEN_REMOTE_ADDRESS=0x9984F0B5CD6684408023E9db7c651f70A7d8893d
Initialize TokenRemote Contract
1 $ export TOKEN_SYMBOL=TESTHUB 2 $ export TOKEN_NAME=TESTHUB 3 4 # Get teleporter registry address here: https://build.avax.network/docs/cross-chain/teleporter/addresses 5 $ export FUJI_TELEPORTER_REGISTRY_ADDRESS=0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228 6 7 # Get L1 "Blockchain ID (Hex)" from AvaCloud: Details: Network Details: Blockchain ID (Hex) 8 $ export TOKEN_HOME_BLOCKCHAIN_ID=0x00f91a24f9e592ceff7a8f9902264f6ec4ce7ae8fccf25469ce476242f4976d7 9 10 $ cast send \ 11 --account my_wallet \ 12 --rpc-url $FUJI_RPC_URL \ 13 $TOKEN_REMOTE_ADDRESS \ 14 'initialize((address,address,uint256,bytes32,address,uint8),string,string,uint8)' \ 15 "($FUJI_TELEPORTER_REGISTRY_ADDRESS,$TELEPORTER_MANAGER,1,$TOKEN_HOME_BLOCKCHAIN_ID,$TOKEN_HOME_ADDRESS,18)" \ 16 $TOKEN_SYMBOL $TOKEN_NAME 18 17 18 status 1 (success) 19 transactionHash 0x342ecce14ffef6ab6eddcca226db669095c16d4b086230afc3bd21928741f7b2
Register the remote contract with the home contract
1 $ cast send \ 2 --account my_wallet \ 3 --rpc-url $FUJI_RPC_URL \ 4 $TOKEN_REMOTE_ADDRESS \ 5 'registerWithHome((address, uint256) feeInfo)' \ 6 "($TOKEN_HOME_ADDRESS, 0)" 7 8 status 1 (success) 9 transactionHash 0x36c956822be3f35ef67f69ca6fa173950f6d7b21e70e1c0dab6931388f626233
Verify register transaction was successful if this output is non-zero
1 # Get "Blockchain ID (Hex)" from https://subnets-test.avax.network/c-chain 2 $ export TOKEN_REMOTE_BLOCKCHAIN_ID=0x7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5 3 4 $ cast call --account my_address \ 5 --rpc-url $L1_RPC_URL \ 6 $TOKEN_HOME_ADDRESS \ 7 'getRemoteTokenTransferrerSettings(bytes32,address)' \ 8 $TOKEN_REMOTE_BLOCKCHAIN_ID \ 9 $TOKEN_REMOTE_ADDRESS 10 11 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000
Send 0.1 TESTHUB (native token) from L1 to C-Chain (ERC20)
The following assumes USER_ADDRESS already been funded with native tokens.
1 $ cast wallet import test_user --interactive 2 3 $ cast wallet list 4 my_wallet (Local) 5 test_user (Local) 6 7 $ export USER_ADDRESS=$(cast wallet address --account test_user) 8 $ echo $USER_ADDRESS 9 0xd2BbD2C9da58EC0B329fB85c802aeE45FbCd523b 10 11 $ cast send \ 12 --account test_user \ 13 --rpc-url $L1_RPC_URL \ 14 --value $(cast to-wei 0.1) \ 15 $TOKEN_HOME_ADDRESS \ 16 'send((bytes32,address,address,address,uint256,uint256,uint256,address))' \ 17 "($TOKEN_REMOTE_BLOCKCHAIN_ID,$TOKEN_REMOTE_ADDRESS,$USER_ADDRESS,0x0000000000000000000000000000000000000000,0,0,10000000,0x0000000000000000000000000000000000000000)" 18 19 # status 1 (success) 20 # transactionHash 0x71ea2405c05bc84730593a9ca27169154961ecd2a3f17dc496451ca8659387b7 21 22 # https://subnets-test.avax.network/qr0407m1ts/tx/0x71ea2405c05bc84730593a9ca27169154961ecd2a3f17dc496451ca8659387b7 23 # https://subnets-test.avax.network/c-chain/tx/0x8f4e48f9ce885bf97049db77e75393884a0ecd7072c422f60112ff2c08db586e 24 25 # On remote end, the TestRemote contract IS the ERC20: 26 # https://testnet.snowtrace.io/token/0x9984F0B5CD6684408023E9db7c651f70A7d8893d?chainid=43113
Send 0.02 TESTHUB (ERC20) from C-Chain to L1 (native token)
1 # Note: The test user needs to have funds on C-Chain to make this transaction. 2 $ cast balance --ether $USER_ADDRESS --rpc-url $FUJI_RPC_URL 3 1.000000000000000000 4 5 $ cast send \ 6 --account test_user \ 7 --rpc-url $FUJI_RPC_URL \ 8 $TOKEN_REMOTE_ADDRESS \ 9 'approve(address,uint256)' \ 10 $TOKEN_REMOTE_ADDRESS \ 11 $(cast to-wei 0.02) 12 13 # status 1 (success) 14 # transactionHash 0x5c78c869b72251dcf4d97941b0c5ad02a7c3b3521821da4797b398e511679ed6 15 16 $ cast send \ 17 --account test_user \ 18 --rpc-url $FUJI_RPC_URL \ 19 $TOKEN_REMOTE_ADDRESS \ 20 'send((bytes32,address,address,address,uint256,uint256,uint256,address),uint256)' \ 21 "($TOKEN_HOME_BLOCKCHAIN_ID,$TOKEN_HOME_ADDRESS,$USER_ADDRESS,0x0000000000000000000000000000000000000000,0,0,10000000,0x0000000000000000000000000000000000000000)" \ 22 $(cast to-wei 0.02) 23 24 # status 1 (success) 25 # transactionHash 0x2105438725cd0584d3eb8a81dfd6d3946f968b2507f17ca25760366e42291fe8 26 27 # https://subnets-test.avax.network/c-chain/tx/0x2105438725cd0584d3eb8a81dfd6d3946f968b2507f17ca25760366e42291fe8 28 # https://subnets-test.avax.network/qr0407m1ts/tx/0xb4dd5c8105097f57c3b8e0d7f5b74ff48cf6674f454c0e11f10387c58c1b382d?tab=details
Verify return of native tokens to user address (internal transaction)
1 $ export DEVELOPER_RPC_URL=<PUT_YOUR_VALUE_HERE> 2 3 $ curl --silent -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"debug_traceTransaction","params":["0xb4dd5c8105097f57c3b8e0d7f5b74ff48cf6674f454c0e11f10387c58c1b382d", {"tracer": "callTracer"}],"id":1}' $DEVELOPER_RPC_URL | jq 4 5 # Look in "calls" section for a record that has "input": "0x" (TOKEN_HOME is sending native token to user address) 6 { 7 "from": "0x20c5541eb19573f4c9044969de818706f70adbc7", 8 "gas": "0x948d02", 9 "gasUsed": "0x0", 10 "to": "0xd2bbd2c9da58ec0b329fb85c802aee45fbcd523b", 11 "input": "0x", 12 "value": "0x470de4df820000", 13 "type": "CALL" 14 } 15 16 $ cast to-fixed-point 18 0x470de4df820000 17 0.020000000000000000
For any additional questions, please view our other knowledge base articles or contact a support team member via the chat button. Examples are for illustrative purposes only.
Learn More About AvaCloud | Download Case Studies | Schedule an AvaCloud Demo