Set Up EVM Keyring

Overview

The Ethereum Virtual Machine (EVM) keyring system enables you to create unified wallets and addresses across multiple EVM-compatible blockchains using a single set of cryptographic keys (user, backup, and BitGo). The base address of the wallet and the derived receive addresses are all cryptographically identical on every supported EVM blockchain.

EVM keyrings use a reference model for creating linked wallets and addresses. A reference wallet is the foundational MPC wallet that acts as the cryptographic source for all other wallets linked in the EVM keyring. The keys to the reference wallet are the source keys that you use across all linked wallets. In addition, all linked addresses match their reference addresses.

Use Cases

The main use cases for EVM keyrings are:

  • Cross-Chain Recovery - If you accidentally send assets to the wrong EVM blockchain, you can recover them by linking the receiving wallets and addresses to your EVM keyring. Once linked, the stuck assets automatically flush to a wallet on the correct EVM blockchain.
  • Unified Identity - You can have the same addresses across all supported EVM chains.
  • Simplified Management - Manage only a single set of keys (user, backup, and BitGo) for wallets across multiple EVM blockchains.

Creating a unified identity and simplifying your key management are proactive measures you can take to utilize EVM keyrings. In contrast, cross-chain recoveries are reactive measures that you only have to take if a transaction accidentally sends assets to the wrong blockchain.

Prerequisites

  • Get Started
  • Create Wallets
    • MPC hot wallet
    • walletVersion: 5
    • EVM blockchains
  • Enterprise admin and reference wallet admin privileges

Note: The following EVM blockchains don't support MPC wallets, and therefore can't use EVM keyrings:

  • Avalanche C-Chain (AVAXC)
  • Celo (CELO)
  • Ethereum Classic (ETC)

1. Select Reference Wallet

Choose an existing EVM wallet to serve as your reference wallet.

Endpoints:

export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"

curl -X GET \
  https://app.bitgo-test.com/api/v2/wallets \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN"
export COIN="<ASSET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"

curl -X GET \
  https://app.bitgo-test.com/api/v2/$COIN/wallets \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Step Result

Select the wallet you want to use as the reference wallet and save the wallet id.

{
  "wallets": [
    {
      "id": "63bd84ea4512c10007ce54a47743f93d",
      "users": [
        {
          "user": "62ab90e06dfda30007974f0a52a12995",
          "permissions": ["admin", "view", "spend"]
        }
      ],
      "coin": "hteth",
      "label": "My Ethereum Testnet Wallet",
      "m": 2,
      "n": 3,
      "keys": [
        "63bd84ea4b81a4000748b540f63580d9",
        "63bd84ea77fe2100070aef91ce0e2b47",
        "62c5ae7f74ac860007aff0cb9bcf93a6"
      ],
      "keySignatures": {
        "backupPub": "20c89b7e0da32c8e3060b548d47e309f0cb8629a4563a564996421fd57c60a3aa751f4d265551520bf92e1e4328f5584456c815b9a46ea5180c655032757524764",
        "bitgoPub": "2087ffd8dfad9b53cf1fe1ccf563cff90899424743d761ec6b727122e7493cb0a946424eeb3edac624270542035239b66757d608c8885ff0830f47a7d3e5598163"
      },
      "enterprise": "62c5ae8174ac860007aff138a2d74df7",
      ...
    }
  ]
}
{
  "coin": "hteth",
  "wallets": [
    {
      "id": "63b894ef02529700075b4d31d04b1ca5",
      "users": [
        {
          "user": "62ab90e06dfda30007974f0a52a12995",
          "permissions": ["admin", "view", "spend"]
        }
      ],
      "coin": "hteth",
      "label": "My Ethereum Testnet Wallet",
      "m": 2,
      "n": 3,
      "keys": [
        "63b894dac02b1d0008b2cdbc1ff66cd1",
        "63b894db43cde50007358e25ed1cedda",
        "62c5ae8074ac860007aff0dfbcb98c21"
      ],
      "keySignatures": {},
      "enterprise": "62c5ae8174ac860007aff138a2d74df7",
      "tags": [
        "63b894ef02529700075b4d31d04b1ca5",
        "62c5ae8174ac860007aff138a2d74df7"
      ]
      ...
    }
  ]
}

2. Link Wallets to Keyring

Once you have a reference wallet ID, you can link wallets to the EVM keyring by creating wallets on other blockchains. Pass the wallet id of the reference wallet in the evmKeyRingReferenceWalletId field.

Repeat this step for each EVM-compatible blockchain you want to add to your keyring.

Endpoint: Generate Wallet

export BITGO_EXPRESS_HOST="<YOUR_LOCAL_HOST>"
export COIN="<ASSET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export LABEL="<YOUR_WALLET_NAME>"
export REFERENCE_WALLET_ID="<YOUR_REFERENCE_WALLET_ID>"

curl -X POST \
  http://$BITGO_EXPRESS_HOST/api/v2/$COIN/wallet/generate \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "label": "'"$LABEL"'",  # (Optional) If absent, uses the label of the reference wallet
    "evmKeyRingReferenceWalletId": "'"$REFERENCE_WALLET_ID"'",
    ...
}'
const BitGoJS = require('bitgo');
const bitgo = new BitGoJS.BitGo({ env: 'test' }); // Use 'prod' for mainnet

async function createLinkedWallet() {
  await bitgo.authenticateWithAccessToken({ accessToken: 'your-access-token' });

  const wallet = await bitgo.coin('tpolygon').wallets().generateWallet({
    label: '<YOUR_WALLET_NAME>', // (Optional) If absent, uses the label of the reference wallet
    evmKeyRingReferenceWalletId: '<YOUR_REFERENCE_WALLET_ID>',
  });

  console.log('Linked Wallet:', JSON.stringify(wallet, null, 2));
  return wallet;
}

createLinkedWallet().catch(console.error);

Step Result

Your new EVM wallets have the same base address as the reference wallet, enabling cross-chain operations.

{
  "id": "68c3f553f62546c8e364ad785896b81f",
  "users": [
    {
      "user": "5cae3130f8f4561d51dfcbfdaafba9b9",
      "permissions": [
        "admin",
        "spend",
        "view"
      ]
    }
  ],
  "coin": "tpolygon",
  "label": "POLYGON EVM MPC W1",
  "m": 2,
  "n": 3,
  "keys": [
    "689a571fc2a2ca9dc5fb114f8708be32",
    "689a571f3a7b477d8dff80535499dc61",
    "689a571ec4e260bc28136c2f66666c2b"
  ],
  "keySignatures": {},
  "enterprise": "65a66817a7f6736078ab85afed6033d3",
  "organization": "65a66818a7f6736078ab864f0042ff3d",
  "bitgoOrg": "BitGo Trust",
  "tags": [
    "68c3f553f62546c8e364ad785896b81f",
    "65a66817a7f6736078ab85afed6033d3"
  ],
  "disableTransactionNotifications": false,
  "freeze": {},
  "deleted": false,
  "approvalsRequired": 1,
  "isCold": false,
  "coinSpecific": {
    "deployedInBlock": false,
    "lastChainIndex": {
      "0": -1,
      "1": -1
    },
    "baseAddress": "0x67000269cdddf7e3220d9e81b0b2aa54ccc4b008",
    "feeAddress": "0xff45beb151901ef6cde8166fa025d595905f7e39",
    "pendingChainInitialization": false,
    "pendingEcdsaTssInitialization": false,
    "creationFailure": [],
    "gasPriceTier": "fast",
    "tokenFlushThresholds": {},
    "lowPriorityFeeAddress": "0xff45beb151901ef6cde8166fa025d595905f7e39",
    "salt": "0xf",
    "walletVersion": 5,
    "pendingDeployment": false,
    "deployForwardersManually": false,
    "flushForwardersManually": false,
    "enableMMI": false,
    "enableNFT": false,
    "sendManyTokens": []
  },
  "admin": {
    "policy": {
      "date": "2025-09-12T10:26:27.263Z",
      "id": "68c3f553f62546c8e364ad89bcc3e662",
      "label": "default",
      "rules": [],
      "version": 0,
      "latest": true
    }
  },
  "clientFlags": [],
  "walletFlags": [],
  "allowBackupKeySigning": false,
  "evmKeyRingevmKeyRingReferenceWalletId": "689a575960b27cc44f82733b8a26cf1d",
  "recoverable": true,
  "startDate": "2025-09-12T10:26:27.000Z",
  "type": "hot",
  "buildDefaults": {},
  "customChangeKeySignatures": {},
  "hasLargeNumberOfAddresses": false,
  "multisigType": "tss",
  "multisigTypeVersion": "MPCv2",
  "hasReceiveTransferPolicy": false,
  "creator": "5cae3130f8f4561d51dfcbfdaafba9b9",
  "walletFullyCreated": true,
  "config": {},
  "balanceString": "0",
  "confirmedBalanceString": "0",
  "spendableBalanceString": "0",
  "lockedBalanceString": "0",
  "stakedBalanceString": "0",
  "receiveAddress": {
    "id": "68c3f553f62546c8e364ad93a8811c8e",
    "address": "0x67000269cdddf7e3220d9e81b0b2aa54ccc4b008",
    "chain": 0,
    "index": 0,
    "coin": "tpolygon",
    "wallet": "68c3f553f62546c8e364ad785896b81f",
    "coinSpecific": {
      "nonce": -1,
      "txCount": 0,
      "pendingChainInitialization": false,
      "creationFailure": [],
      "pendingDeployment": false,
      "forwarderVersion": 4,
      "isTss": true,
      "isNonceLocked": false,
      "updateTime": "2025-09-12T10:26:27.394Z"
    }
  },
  "pendingApprovals": []
}

3. Fund Gas Tank

The new Evm keyring wallet uses the gas tank address of reference wallet and is therefore the same fee address across all blockchains in your EVM keyring. Fund your gas tank with the native asset of each blockchain in your keyring. During a cross-chain recovery, the gas tank pays for the deployment of a forwarder smart contract and token recovery contract using the native asset of the target blockchain.

You must maintain a sufficient balance of each native asset in your gas tank. To learn more, see Fund Gas Tanks.

Note: You must maintain sufficient balances of native assets in your gas tank at all times. If your gas tank has an insufficient balance, token recovery contracts can't deploy when tokens are sent to the wrong blockchain.

Don't proceed to step 4 until you fund your gas tank with the native assets of each blockchain in your keyring.

4. Link Addresses to Keyring

Once you have wallets in the EVM keyring that share the same base address, you can create linked addresses. Use a reference address from any existing wallet in your keyring to create an identical address in a linked wallet on different blockchain.

What Happens at Address Creation

When you create an EVM keyring address, BitGo immediately performs the following deployments using funds from your gas tank:

  1. Forwarder Contract Deployment - BitGo deploys a forwarder smart contract on the target blockchain. This contract is required to receive and forward assets.

  2. Token Recovery Contract Deployment - If tokens are already present at the reference address on the wrong blockchain, BitGo also deploys a token recovery contract to flush those tokens to your wallet.

Important: Both deployments require gas. If your gas tank does not have sufficient native asset for the target blockchain, the contract deployments will fail, and assets will not appear in your wallet. Ensure you complete Step 3 (Fund Gas Tank) before creating addresses.

Address Initialization Differences

  • Reference Address - The smart contract deploys only upon receiving the first transaction.
  • EVM Keyring Address - The smart contract deploys upon creation, enabling immediate cross-chain recovery.

Endpoint: Create Address

export COIN="<ASSET_ID>"
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export REFERENCE_ADDRESS="<YOUR_REFERENCE_ADDRESS>"

curl -X POST \
  "https://app.bitgo.com/api/v2/$COIN/wallet/$WALLET_ID/address" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "evmKeyRingReferenceAddress": "'"$REFERENCE_ADDRESS"'"
  }'
const BitGoJS = require('bitgo');
const bitgo = new BitGoJS.BitGo({ env: 'test' });

async function createLinkedAddress() {
  await bitgo.authenticateWithAccessToken({ accessToken: '<YOUR_ACCESS_TOKEN>' });

  const wallet = await bitgo.coin('<ASSET_ID>').wallets().get({ id: '<YOUR_WALLET_ID>' });
  const newAddress = await wallet.createAddress({
    evmKeyRingReferenceAddress: '<YOUR_REFERENCE_ADDRESS>'
  });

  console.log('Linked Address:', JSON.stringify(newAddress, null, 2));
  return newAddress;
}

createLinkedAddress().catch(console.error);

Step Result

Your new address is the same as the reference address, enabling cross-chain operations.

{
  "id": "68c42570b9c05fcdd3f0c53b5261259f",
  "address": "0x9eaa84c52cd8649395c62b3ac982f00ad317df2b",
  "chain": 0,
  "index": 3,
  "coin": "tpolygon",
  "lastNonce": 0,
  "wallet": "68c3fd5e9384c89efe35dda0eefc8402",
  "needsConsolidation": false,
  "coinSpecific": {
    "nonce": -1,
    "updateTime": "2025-09-12T13:51:44.694Z",
    "txCount": 0,
    "pendingChainInitialization": true,
    "creationFailure": [],
    "salt": "0x3",
    "pendingDeployment": false,
    "forwarderVersion": 4
  },
  "addressType": "p2sh",
  "keychains": [
    {
      "id": "68c3fd5bc20920edd2ceec194611dc7b",
      "source": "user",
      "type": "tss",
      "commonKeychain": "02f22ee8420903c0f57fca647c9c08da9e3ba48cebe43a0c4df8673369830d67dd5fe5ab4557e8447ebab88db4e9b56099549303ea14005dc9f78358986d987c85",
      "encryptedPrv": "..."
    },
    {
      "id": "68c3fd5c74f8b62d58168c51addb8e87",
      "source": "backup",
      "type": "tss",
      "commonKeychain": "02f22ee8420903c0f57fca647c9c08da9e3ba48cebe43a0c4df8673369830d67dd5fe5ab4557e8447ebab88db4e9b56099549303ea14005dc9f78358986d987c85",
      "encryptedPrv": "..."
    },
    {
      "id": "68c3fd5be4dbc6d15a832b89d62ff1f6",
      "source": "bitgo",
      "type": "tss",
      "commonKeychain": "02f22ee8420903c0f57fca647c9c08da9e3ba48cebe43a0c4df8673369830d67dd5fe5ab4557e8447ebab88db4e9b56099549303ea14005dc9f78358986d987c85",
      "isBitGo": true,
      "isTrust": false,
      "hsmType": "institutional"
    }
  ],
  "baseAddress": "0xaf34750bcf93ccbb22569c06af40558dfcd3e717",
  "format": "string"
}

When Token Flushing Happens

Token flushing occurs automatically after the contracts are deployed at address creation (Step 4). Here's the sequence:

  1. Address Creation - You create a linked address using the API (Step 4).
  2. Forwarder Contract Deployment - BitGo deploys the forwarder contract on the target blockchain (requires gas tank funds).
  3. Token Contract Deployment - If tokens exist at the address, BitGo deploys the token recovery contract (requires gas tank funds).
  4. Automatic Token Flush - BitGo automatically flushes tokens from the forwarder to your wallet's base address.
  5. Balance Update - Your wallet balance updates to reflect the recovered assets.

Note: If your gas tank is not funded, steps 2-3 will fail, and tokens will not flush. Fund your gas tank before creating addresses.

Next Steps

Any address in your EVM keyring is set up for automatic cross-chain recovery. If you have assets that were sent to an EVM wallet on the wrong blockchain, or if you receive future transactions as such, BitGo automatically:

  • Deploys the forwarder contract for the address on the target EVM network.
  • Automatically syncs the balance.
  • Makes the previously stuck assets available in your target EVM wallet, fully recovered and accessible for withdrawal.

See Also