> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerswap.io/llms.txt
> Use this file to discover all available pages before exploring further.

# EVM chains

> Submit native-token and ERC-20 deposit actions on EVM-compatible source networks.

On EVM networks, `call_data` is the hex-encoded transaction `data`. Submit it with the returned `to_address` and exact `amount_in_base_units`.

## How the action differs by token

* For a native token, `to_address` is the deposit recipient, `amount_in_base_units` is the transaction value, and `call_data` contains the matching memo.
* For an ERC-20 token, `to_address` is the token contract, `amount_in_base_units` is normally `0`, and `call_data` contains the encoded `transfer(address,uint256)` call plus Layerswap's matching data. The token amount is already encoded in the calldata.
* For `manual_transfer`, `call_data` is `null`; send the exact source asset to the generated address.

Do not replace the recipient, token amount, or memo encoded in `call_data`.

## Transaction construction

<Steps>
  <Step title="Parse the deposit action">
    Extract `call_data`, `to_address`, `amount_in_base_units`, and `network.chain_id` from the deposit action.
  </Step>

  <Step title="Build the transaction">
    Construct a transaction object with the deposit action fields mapped to standard EVM transaction parameters.
  </Step>

  <Step title="Estimate gas (optional)">
    Call `eth_estimateGas` for a more accurate gas limit. If estimation fails, the transaction can still be sent without an explicit gas limit — the wallet or node will estimate it.
  </Step>

  <Step title="Send the transaction">
    Sign and broadcast the transaction, then return the hash.
  </Step>
</Steps>

## Full example

Pick the library that fits your stack. The viem and ethers.js tabs cover server-side or Node.js use; the wagmi tab is for browser use with a connected wallet.

<CodeGroup>
  ```ts viem theme={null}
  import { createWalletClient, createPublicClient, http, parseAbi } from 'viem';
  import { privateKeyToAccount } from 'viem/accounts';
  import { mainnet, arbitrum, optimism, base, polygon } from 'viem/chains';

  const CHAIN_MAP = {
    1: mainnet,
    42161: arbitrum,
    10: optimism,
    8453: base,
    137: polygon,
    // Add other EVM chains as needed
  };

  async function executeEvmDeposit(action, privateKey) {
    const chainId = Number(action.network.chain_id);
    const chain = CHAIN_MAP[chainId];
    if (!chain) {
      throw new Error(`Unsupported chain ID: ${chainId}`);
    }

    const account = privateKeyToAccount(privateKey);

    const walletClient = createWalletClient({
      account,
      chain,
      transport: http(action.network.node_url),
    });
    const publicClient = createPublicClient({
      chain,
      transport: http(action.network.node_url),
    });

    // A manual ERC-20 transfer has no prepared calldata, so build transfer().
    if (!action.call_data && action.token.contract) {
      return walletClient.writeContract({
        address: action.token.contract,
        abi: parseAbi(['function transfer(address to, uint256 amount) returns (bool)']),
        functionName: 'transfer',
        args: [action.to_address, BigInt(action.amount_in_base_units)],
      });
    }

    // Estimate gas; on failure, send without an explicit limit and let the node estimate.
    let gas;
    try {
      gas = await publicClient.estimateGas({
        account: account.address,
        to: action.to_address,
        value: BigInt(action.amount_in_base_units),
        data: action.call_data || undefined,
      });
    } catch {
      // Proceed without an explicit gas limit.
    }

    return walletClient.sendTransaction({
      to: action.to_address,
      value: BigInt(action.amount_in_base_units),
      data: action.call_data || undefined,
      gas,
    });
  }
  ```

  ```ts ethers.js theme={null}
  import { ethers } from 'ethers';

  async function executeEvmDeposit(action, privateKey) {
    const provider = new ethers.JsonRpcProvider(action.network.node_url);
    const wallet = new ethers.Wallet(privateKey, provider);

    // A manual ERC-20 transfer has no prepared calldata, so build transfer().
    if (!action.call_data && action.token.contract) {
      const token = new ethers.Contract(
        action.token.contract,
        ['function transfer(address to, uint256 amount) returns (bool)'],
        wallet,
      );
      const tx = await token.transfer(action.to_address, BigInt(action.amount_in_base_units));
      return tx.hash;
    }

    // Estimate gas; on failure, send without an explicit limit and let the node estimate.
    let gasLimit;
    try {
      gasLimit = await provider.estimateGas({
        from: wallet.address,
        to: action.to_address,
        value: BigInt(action.amount_in_base_units),
        data: action.call_data || '0x',
      });
    } catch {
      // Proceed without an explicit gas limit.
    }

    const tx = await wallet.sendTransaction({
      to: action.to_address,
      value: BigInt(action.amount_in_base_units),
      data: action.call_data || '0x',
      gasLimit,
    });
    return tx.hash;
  }
  ```

  ```ts wagmi theme={null}
  import { sendTransaction } from '@wagmi/core';

  const hash = await sendTransaction(wagmiConfig, {
    chainId: Number(action.network.chain_id),
    to: action.to_address,
    value: BigInt(action.amount_in_base_units),
    data: action.call_data || undefined,
  });
  ```
</CodeGroup>

Using the integer `amount_in_base_units` avoids float rounding and works for both native-token actions and ERC-20 actions whose native transaction value is zero.

When using a browser wallet, confirm the user is connected to `action.network.chain_id` and prompt a network switch before sending. For a manual ERC-20 action, call the token contract's `transfer` function as in the viem and ethers.js examples instead of sending a transaction with empty data.

## Funding via the Depository

The examples above cover the direct-transfer and deposit-address methods. If the swap was created with `use_depository: true`, the deposit action targets the on-chain [Depository](/api/funding/depository) contract instead — submit its `call_data`, and for ERC-20 approve the contract first:

```ts viem theme={null}
import { createWalletClient, custom, parseAbi } from 'viem';

// `swap` is the POST /api/v2/swaps response
const action = swap.deposit_actions[0];
const wallet = createWalletClient({ transport: custom(window.ethereum) });

// ERC-20 only: approve the Depository (to_address) to pull the tokens.
// The amount is the 4th depositERC20 argument, returned in encoded_args.
if (action.token.contract) {
  await wallet.writeContract({
    address: action.token.contract,
    abi: parseAbi(['function approve(address spender, uint256 amount) returns (bool)']),
    functionName: 'approve',
    args: [action.to_address, BigInt(action.encoded_args[3])],
  });
}

// Submit the prepared depositNative / depositERC20 call exactly as returned
await wallet.sendTransaction({
  to: action.to_address, // the Depository contract
  data: action.call_data,
  value: BigInt(action.amount_in_base_units), // 0 for ERC-20, the deposit amount for native
});
```

See [Funding methods](/concepts/funding-methods) for when to use each.

After broadcasting, store the transaction hash and [track the swap](/api/track-swaps).
