# Transfer Tokens between EOAs
Source: https://docs.chain.link/ccip/v1/evm/tutorials/offchain/transfer-tokens-from-eoa
Last Updated: 2025-05-19

> For the complete documentation index, see [llms.txt](/llms.txt).

In this tutorial, you will use Chainlink CCIP to transfer tokens directly from your [EOA (Externally Owned Account)](https://ethereum.org/en/developers/docs/accounts/#types-of-account) to an account on a different blockchain. First, you will pay for CCIP fees on the source blockchain using LINK. Then, you will run the same example paying for CCIP fees in native gas, such as ETH on Ethereum or AVAX on Avalanche.

## Before you begin

1. [Install Node.js 22](https://nodejs.org/en/download/). Optionally, you can use the [nvm package](https://www.npmjs.com/package/nvm) to switch between Node.js versions with `nvm use 22`.

   ```shell
   node -v
   ```

   ```shell
   $ node -v
   v22.15.0
   ```

2. Your [EOA (Externally Owned Account)](https://ethereum.org/en/developers/docs/accounts/#types-of-account) must have both AVAX and LINK tokens on *Avalanche Fuji* to pay for the gas fees and CCIP fees.
   - [Configure MetaMask to use LINK tokens](/resources/acquire-link#configure-metamask-to-use-link-tokens)
   - Acquire testnet AVAX and LINK from [faucets.chain.link/fuji](https://faucets.chain.link/fuji)

3. Check the [CCIP Directory](/ccip/directory) to confirm that the tokens you will transfer are supported for your lane. In this example, you will transfer tokens from *Avalanche Fuji* to *Ethereum Sepolia* so check the list of supported tokens [here](/ccip/directory/testnet/chain/avalanche-fuji-testnet).

4. Learn how to [acquire CCIP test tokens](/ccip/v1/test-tokens#evm-chains). After following this guide, your [EOA (Externally Owned Account)](https://ethereum.org/en/developers/docs/accounts/#types-of-account) should have CCIP-BnM tokens, and CCIP-BnM should appear in the list of your tokens in MetaMask.

5. In a terminal, clone the [smart-contract-examples repository](https://github.com/smartcontractkit/smart-contract-examples) and change to the `smart-contract-examples/ccip/v1/offchain/javascript` directory.

   ```shell
   git clone https://github.com/smartcontractkit/smart-contract-examples.git && \
   cd smart-contract-examples/ccip/v1/offchain/javascript
   ```

6. Run `npm install` to install the dependencies.

   ```shell
   npm install
   ```

7. For higher security, the examples repository imports [@chainlink/env-enc](https://www.npmjs.com/package/@chainlink/env-enc). Use this tool to encrypt your environment variables at rest.
   1. Set an encryption password for your environment variables.

      ```shell
      npx env-enc set-pw
      ```

   2. Run `npx env-enc set` to configure a `.env.enc` file with the basic variables that you need to send your requests to *Ethereum Sepolia*.

      - `AVALANCHE_FUJI_RPC_URL`: Set a URL for the *Avalanche Fuji* testnet. You can sign up for a personal endpoint from [Alchemy](https://www.alchemy.com/), [Infura](https://www.infura.io/), or another node provider service.

      - `ETHEREUM_SEPOLIA_RPC_URL`: Set a URL for the *Ethereum Sepolia* testnet. You can sign up for a personal endpoint from [Alchemy](https://www.alchemy.com/), [Infura](https://www.infura.io/), or another node provider service.

      - `PRIVATE_KEY`: Find the private key for your testnet wallet. If you use MetaMask, follow the instructions to [Export a Private Key](https://support.metamask.io/managing-my-wallet/secret-recovery-phrase-and-private-keys/how-to-export-an-accounts-private-key/).**Note**: The offchain script uses your private key to sign any transactions you make such as transferring tokens.

      ```shell
      npx env-enc set
      ```

## Tutorial

### Transfer tokens and pay in LINK

In this example, you will transfer CCIP-BnM tokens from your EOA on *Avalanche Fuji* to an account on *Ethereum Sepolia*. The destination account could be an [EOA (Externally Owned Account)](https://ethereum.org/en/developers/docs/accounts/#types-of-account) or a smart contract. The example shows how to transfer CCIP-BnM tokens, but you can reuse the same example to transfer other tokens as long as they are supported for your [lane](/ccip/v1/concepts/architecture/key-concepts#lane).

For this example, CCIP fees are paid in LINK tokens. To learn how to pay CCIP fees in native AVAX, read the [Pay in native](#transfer-tokens-and-pay-in-native) section. To see a detailed description of the example code, read the [code explanation](#code-explanation) section.

To transfer tokens and pay in LINK, use the following command:

```
node src/transfer-tokens.js sourceChain destinationChain destinationAccount tokenAddress amount feeTokenAddress
```

The `feeTokenAddress` parameter specifies the token address for paying CCIP fees. The supported tokens for paying fees include LINK, the native gas token of the source blockchain (ETH for Ethereum), and the wrapped native gas token (WETH for Ethereum).

Complete the following steps in your terminal:

1. Send 1,000,000,000,000,000 (0.001 CCIP-BnM ) from your EOA on *Avalanche Fuji* to another account on *Ethereum Sepolia*:

   ```
   node src/transfer-tokens.js avalancheFuji ethereumSepolia YOUR_ACCOUNT 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 1000000000000000 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846
   ```

   Command arguments:

   | Argument                                   | Explanation                                                                                                                                                                                                                    |
   | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
   | node src/transfer-tokens.js                | Node.js will execute the JavaScript code inside the `transfer-tokens.js` file.                                                                                                                                                 |
   | avalancheFuji                              | This specifies the source blockchain, in this case, *Avalanche Fuji*.                                                                                                                                                          |
   | ethereumSepolia                            | This specifies the destination blockchain, which is *Ethereum Sepolia* in this case.                                                                                                                                           |
   | `YOUR_ACCOUNT`                             | This is the account address on the destination blockchain. You can replace this with your account address.                                                                                                                     |
   | 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 | This is the CCIP-BnM token contract address on Avalanche Fuji. The contract addresses for each network can be found on the [CCIP Directory](/ccip/directory).                                                                  |
   | 1000000000000000                           | This is the amount of CCIP-BnM tokens to be transferred. In this example, 0.001 CCIP-BnM are transferred.                                                                                                                      |
   | 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846 | Since you will pay for CCIP fees in LINK, this is the LINK token contract address on *Avalanche Fuji*. The LINK contract address can be found on the [Link Token contracts page](/resources/link-token-contracts?parent=ccip). |

2. Once you execute the command, you should see the following logs:

   ```
   $ node src/transfer-tokens.js avalancheFuji ethereumSepolia 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A  0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 1000000000000000 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846
   Estimated fees (LINK): 0.020020889739492

   Approving router 0xF694E193200268f9a4868e4Aa017A0118C9a8177 to spend 1000000000000000 of token 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4

   Approval done. Transaction: 0x103c20b95183380aa7c04edd0cc8b5cd6137f0b36eda931bdd23e66fd0d21251

   Approving router 0xF694E193200268f9a4868e4Aa017A0118C9a8177 to spend fees 20020889739492000 of feeToken 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846

   Approval done. Transaction: 0x1b6737bbf12f1ba0391ae9ba38c46c72a1118f4d20767c4c67729cf3acc0ae8b

   Calling the router to send 1000000000000000 of token 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 to account 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A on destination chain ethereumSepolia using CCIP


   ✅ 1000000000000000 of Tokens(0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4) Sent to account 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A on destination chain ethereumSepolia using CCIP. Transaction hash 0x96b5b645f4f442131fc9466ff459e2211d408058be4d9a72a8fb057ca0f4723f -  Message id is 0x64be9f0e67af707d6203184adf30e86b3f0edd024c868ee0f2c57992d69609fd

   Wait for message 0x64be9f0e67af707d6203184adf30e86b3f0edd024c868ee0f2c57992d69609fd to be executed on the destination chain - Check the explorer https://ccip.chain.link/msg/0x64be9f0e67af707d6203184adf30e86b3f0edd024c868ee0f2c57992d69609fd
   ...
   ```

3. Analyze the logs:
   - The script communicates with the router to calculate the transaction fees required to transfer tokens, which amounts to 20,020,889,739,492,000 Juels (equivalent to 0.02 LINK).
   - The script engages with the Link token contract, authorizing the router contract to spend 20,020,889,739,492,000 Juels for the fees and 1000000000000000 (0.001 CCIP-BnM) from your Externally Owned Account (EOA) balance.
   - The script initiates a transaction through the router to transfer 1000000000000000 (0.001 CCIP-BnM) to your account on *Ethereum Sepolia*. It also returns the CCIP message ID.
   - The script continuously monitors the destination blockchain (*Ethereum Sepolia*) to track the progress and completion of the cross-chain transaction.

4. While the script is waiting for the cross-chain transaction to proceed, open the [CCIP explorer](https://ccip.chain.link/) and search your cross-chain transaction using the message ID. Notice that the status is not finalized yet.

5. After several minutes (the waiting time depends on the [finality of the source blockchain](/ccip/v1/ccip-execution-latency)), the script will complete the polling process, and the following logs will be displayed:

   ```
   Message 0x64be9f0e67af707d6203184adf30e86b3f0edd024c868ee0f2c57992d69609fd has not been processed yet on the destination chain.Try again in 60sec - Check the explorer https://ccip.chain.link/msg/0x64be9f0e67af707d6203184adf30e86b3f0edd024c868ee0f2c57992d69609fd

   ✅Status of message 0x64be9f0e67af707d6203184adf30e86b3f0edd024c868ee0f2c57992d69609fd is SUCCESS - Check the explorer https://ccip.chain.link/msg/0x64be9f0e67af707d6203184adf30e86b3f0edd024c868ee0f2c57992d69609fd
   ```

6. Open the [CCIP explorer](https://ccip.chain.link/) and use the message ID to find your cross-chain transaction.

   ![Chainlink CCIP Explorer transaction details](/images/ccip/tutorials/ccip-explorer-offchain-send-tokens-paylink-tx-details.jpg)

7. The data field is empty because only tokens are transferred. The gas limit is set to 0 because the transaction is directed to an Externally Owned Account (EOA). With an empty data field, no function calls on a smart contract are expected on the destination chain.

### Transfer tokens and pay in native

In this example, you will transfer CCIP-BnM tokens from your EOA on *Avalanche Fuji* to an account on *Ethereum Sepolia*. The destination account could be an [EOA (Externally Owned Account)](https://ethereum.org/en/developers/docs/accounts/#types-of-account) or a smart contract. The example shows how to transfer LINK tokens, but you can reuse the same example to transfer other tokens as long as they are supported for your [lane](/ccip/v1/concepts/architecture/key-concepts#lane).

For this example, CCIP fees are paid in Sepolia's native ETH. To learn how to pay CCIP fees in LINK, read the [Pay in LINK](#transfer-tokens-and-pay-in-link) section. To see a detailed description of the example code, read the [code explanation](#code-explanation) section.

To transfer tokens and pay in native, use the following command:

```
node src/transfer-tokens.js sourceChain destinationChain destinationAccount tokenAddress amount
```

Complete the following steps in your terminal:

1. Send 1,000,000,000,000,000 (0.001 CCIP-BnM ) from your EOA on *Avalanche Fuji* to another account on *Ethereum Sepolia*:

   ```
   node src/transfer-tokens.js avalancheFuji ethereumSepolia YOUR_ACCOUNT 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 1000000000000000
   ```

   Command arguments:

   | Argument                                   | Explanation                                                                                                                                                     |
   | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | node src/transfer-tokens.js                | Node.js will execute the JavaScript code inside the `transfer-tokens.js` file.                                                                                  |
   | avalancheFuji                              | This specifies the source blockchain, in this case, *Avalanche Fuji*.                                                                                           |
   | ethereumSepolia                            | This specifies the destination blockchain, which is *Ethereum Sepolia* in this case.                                                                            |
   | `YOUR_ACCOUNT`                             | This is the account address on the destination blockchain. Replace this with your account address.                                                              |
   | 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 | This is the CCIP-BnM token contract address on *Avalanche Fuji*. The contract addresses for each network can be found on the [CCIP Directory](/ccip/directory). |
   | 1000000000000000                           | This is the amount of CCIP-BnM tokens to be transferred. In this example, 0.001 CCIP-BnM are transferred.                                                       |

2. After you execute the command, you should see the following logs:

   ```
   $ node src/transfer-tokens.js avalancheFuji ethereumSepolia 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A  0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 1000000000000000
   Estimated fees (native): 80139264929946666

   Approving router 0xF694E193200268f9a4868e4Aa017A0118C9a8177 to spend 1000000000000000 of token 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4

   Approval done. Transaction: 0x23fd23e7df77ef6619ed108f507e85108e5e8592bc754a85b1264f8cf15e3221

   Calling the router to send 1000000000000000 of token 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 to account 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A on destination chain ethereumSepolia using CCIP


   ✅ 1000000000000000 of Tokens(0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4) Sent to account 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A on destination chain ethereumSepolia using CCIP. Transaction hash 0xe4b2226a55a6eb27f5e5ecf497af932578bbdc1009f412a8b7a855a5dbd00ffa -  Message id is 0x137481a149a892f9b555d9f0c934b67fd85354af0292b82eff2f0eafd8686b9d

   Wait for message 0x137481a149a892f9b555d9f0c934b67fd85354af0292b82eff2f0eafd8686b9d to be executed on the destination chain - Check the explorer https://ccip.chain.link/msg/0x137481a149a892f9b555d9f0c934b67fd85354af0292b82eff2f0eafd8686b9d
   ...
   ```

3. Analyze the logs:
   - The script communicates with the router to calculate the transaction fees required to transfer tokens, which amounts to 80,139,264,929,946,666 wei (equivalent to 0.08 AVAX).
   - The script interacts with the CCIP-BnM token contract, authorizing the router contract to deduct 0.001 CCIP-BnM from your Externally Owned Account (EOA) balance.
   - The script initiates a transaction through the router to transfer 0.001 CCIP-BnM tokens to your destination account on *Ethereum Sepolia*. It also returns the CCIP message ID.
   - The script continuously monitors the destination blockchain (*Ethereum Sepolia*) to track the progress and completion of the cross-chain transaction.

4. The transaction time depends on the [finality of the source blockchain](/ccip/v1/ccip-execution-latency). After several minutes, the script will complete the polling process and the following logs will be displayed:

```
    Message 0x137481a149a892f9b555d9f0c934b67fd85354af0292b82eff2f0eafd8686b9d is not processed yet on destination chain.Try again in 60sec - Check the explorer https://ccip.chain.link/msg/0x137481a149a892f9b555d9f0c934b67fd85354af0292b82eff2f0eafd8686b9d

    ✅Status of message 0x137481a149a892f9b555d9f0c934b67fd85354af0292b82eff2f0eafd8686b9d is SUCCESS - Check the explorer https://ccip.chain.link/msg/0x137481a149a892f9b555d9f0c934b67fd85354af0292b82eff2f0eafd8686b9d
```

1. Open the [CCIP explorer](https://ccip.chain.link/) and use the message ID to find your cross-chain transaction.

   ![Chainlink CCIP Explorer transaction details](/images/ccip/tutorials/ccip-explorer-offchain-send-tokens-tx-details.jpg)

2. The data field is empty because only tokens are transferred. The gas limit is set to 0 because the transaction is directed to an Externally Owned Account (EOA), so no function calls are expected on the destination chain.

## Code explanation

The Javascript featured in this tutorial is designed to interact with CCIP to transfer tokens. The contract code includes several code comments to clarify each step, but this section explains the key elements.

### Imports

The script starts by importing the necessary modules and data. It imports ethers.js and ABIs (Application Binary Interface) from a config file for different contracts and configurations.

### Handling arguments

The `handleArguments` function validates and parses the command line arguments passed to the script.

### Main function: transferTokens

This asynchronous function, `transferTokens` performs the token transfer.

#### Initialization

The script initializes ethers providers to communicate with the blockchains in this section. It parses source and destination router addresses and blockchain selectors. A signer is created to sign transactions.

#### Token validity check

The script fetches a list of supported tokens for the destination chain and checks if the token you want to transfer is supported.

#### Building the CCIP message

A Cross-Chain Interoperability Protocol (CCIP) message is built, which will be sent to the router contract. It includes the destination account, amount, token address, and additional parameters.

#### Fee calculation

The script calls the router to estimate the fees for transferring tokens.

#### Transferring tokens

This section handles the actual transferring of tokens. It covers three cases:

- **Fees paid using the native gas token:** The contract makes one approval for the transfer amount. The fees are included in the `value` transaction field.
- **Fees paid using an asset different from the native gas token and the token being transferred:** The contracts makes two approvals. The first approval is for the transfer amount and the second approval is for the fees.
- **Fees paid using the same asset that is being transferred, but not the native gas token:** The contract makes a single approval for the sum of the transfer amount and fees.

The script waits for the transaction to be validated and stores the transaction receipt.

#### Fetching message ID

The router's `ccipSend` function returns a message ID. The script simulates a call to the blockchain to fetch the message ID that the router returned.

#### Checking the status on the destination chain

The script polls the off-ramp contracts on the destination chain to wait for the message to be executed. If the message is executed, it returns the status. Otherwise, the message times out after 40 minutes.

> **CAUTION: Educational Example Disclaimer**
>
> Please note, this page contains community examples only — these are not Chainlink products or services and are not
> supported or maintained by Chainlink. This code represents an example of using a Chainlink product or service, and is
> intended for demonstration and educational purposes only. It is provided "AS IS" and "AS AVAILABLE" without warranties
> of any kind, may not have been audited, and may omit checks or error handling. Each party intending to use this
> example code does so entirely at their own risk and must perform its own audits, security and code review, key
> management, and testing before any production deployment and ensure the operation and performance of such code matches
> expectations. Neither Chainlink Labs nor the Chainlink Foundation deploys, operates, monitors, maintains or endorses
> any deployment of this code. Note that this is not a Chainlink product, feature or service, and there are no
> commitments made with respect to the code, including compatibility with future Chainlink releases. You should not rely
> on this code without first conducting your own technical, engineering, and security review. This code is also outside
> the scope of any Chainlink bug bounty programs. Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node
> operators are responsible for outcomes due to errors in this example or how it is deployed or operated, or liable for
> any resulting claims or damages. Use of the Chainlink Network is subject to the Chainlink Foundation [Terms of
> Service](https://chain.link/terms), which provides important information and disclosures. By using this code, you
> acknowledge and agree to these terms.