Skip to main content

Bridge-CLI: moving tokens with Omni Bridge

· 8 min read
Slava Karkunov
Slava Karkunov
DevRel

In this short tutorial we will show how by using Omni Bridge you can bring your custom token from other chains (e.g., Solana, Base, Ethereum) to NEAR ecosystem.

To do this we have created a custom token called VGAS on Base already in previous tutorial.

Now, we want to bridge it to NEAR and use corresponding fungible token (NEP-141 on NEAR) as a gas token for a virtual chain in the next article.

Quick plan

We're going to:

  1. Install Rust
  2. Install bridge-cli
  3. Configure .env variables for bridge-cli
  4. Deploy VGAS via Omni Bridge to NEAR to get NEP-141 token there.
  5. Bridge some of your tokens from Base to NEAR.

Requirements

Please, create or prepare any ERC-20 token from Base, Arbitrum or Ethereum to be used. You can also try this out with SPL tokens on Solana. We will use the VGAS token from the thirdweb ERC-20 tutorial.

We're expecting you to have your MetaMask installed and your wallet already created there. You can use any other EVM wallet too. E.g., Rabby, Brave or Coinbase Wallet.

You also will need your NEAR wallet prepared. We recommend using MyNearWallet, but any other will suffice. You will just need to take your private key out of it and use it in bridge-cli.

Also, please get some real funds there to pay for gas and token deployments. You will need 3+ NEAR to be on your NEAR wallet and some ETH present on your Base wallet. We recommend using NEAR Intents to swap and bridge tokens between different chains.

Please contact us on Discord if you will need any help with getting funds for gas fees.

Installing Rust

We will use a Rust library called bridge-sdk-rs of which bridge-cli is a part of. So, let's install Rust first by using the next command on Mac or Linux:

curl https://sh.rustup.rs -sSf | sh

If you're using Windows, please follow instructions on rust-lang.org.

Installing bridge-cli

Download binary

Visit releases page to download a binary for your platform

Manual compilation

# Clone the repository
git clone https://github.com/near-one/bridge-sdk-rs.git
cd bridge-sdk-rs

# Build the CLI
cargo build --release

# The binary will be available at
./target/release/bridge-cli

Put that binary on your $PATH

note

The PATH variable is an environment variable that specifies a list of directories that the operating system searches when you enter a command. It allows you to run commands and programs without specifying their full path, making it easier to use the command line.

You can read more about what is $PATH variable on Wiki. And a detailed tutorial on how to change it.

Overall, you can just open your favorite terminal, I will use iTerm2 and enter:

echo $PATH

You will see something like:

/Users/aurora/Library/pnpm:/Applications/Sublime Text.app/Contents/SharedSupport/bin:/Users/aurora/bin:/Users/aurora/.cargo/bin:/Users/aurora/.foundry/bin

It is just a bunch of folders concateneted by ':' symbol. So you can take one of these, e.g., /Users/aurora/.foundry/bin in my case, and put bridge-cli binary there.

After that you will able to execute the next command in your terminal:

bridge-cli --help

and get

Usage: bridge-cli <NETWORK> <COMMAND>

Commands:
log-metadata Log metadata for a token
deploy-token Deploy a token
is-transfer-finalised Check if transfer is finalised
near-storage-deposit Deposit storage for a token on NEAR
near-sign-transfer Sign a transfer on NEAR
near-init-transfer Initialize a transfer on NEAR
near-fin-transfer-with-evm-proof Finalize a transfer on NEAR using EVM proof
near-fin-transfer-with-vaa Finalize a transfer on NEAR using VAA
near-fast-fin-transfer Finalize a transfer on NEAR using fast transfer
...

If bridge-cli is not on your PATH, you will see something like that:

zsh: command not found: bridge-cli

Configuring .env file

Security warning: protect your private keys

Storing raw private keys in a plaintext .env file is dangerous. Never commit this file to version control and prefer secret managers (e.g., 1Password CLI, pass, Bitwarden CLI, or environment-injection in CI like GitHub Actions secrets).

At minimum, add .env to your .gitignore:

.env

Rotate any keys that might have been exposed. Only store the keys you're willing to lose as a plaintext in the .env file.

Let's configure environment variables now. To do this, create the .env file in your current folder:

touch .env

Open it in your favorite text editor, I will use Sublime Text, and enter the next info:

# e.g. karkunow.near
NEAR_SIGNER=<signer-account-id>
NEAR_PRIVATE_KEY=<signer-private-key>
BASE_PRIVATE_KEY=<base-private-key>

# mainnet
TOKEN_LOCKER_ID=omni.bridge.near

Optionally, you can provide also:

# testnet
# TOKEN_LOCKER_ID=omni-locker.testnet

# ARB_PRIVATE_KEY=<arbitrum-private-key>
# ETH_PRIVATE_KEY=<eth-private-key>

# you can provide solana's keypair as base58 string
# SOLANA_KEYPAIR=<solana-keypair-bs58>
# or by providing an absolute path to the file where keypair is stored
# SOLANA_KEYPAIR=/Users/.../solana-wallet.json

But we won't need Solana, Eth or Arbitrum keys in this tutorial.

As you can see, we will need to get our EVM wallet and NEAR wallet private keys and put them to the .env file.Here is a tutorial on how to do it in MetaMask.

For the MyNearWallet:

  • go to your account page
  • scroll down to the bottom and click "Export Local Private Key" button
  • enter your account id in the popup
  • click "View Private Key"
  • copy it your .env file

"Export Local Private Key" button is located here: export local private key button in MyNearWallet

Now that your .env file is ready, go to your terminal and execute:

source .env

Deploying token to NEAR

Now, let's take our VGAS token and try to deploy it to NEAR. But before doing that note the next information.

note
  • You have to wait for around 20 minutes for transaction confirmation after calling any method on EVM chain. Otherwise, you'll get ERR_INVALID_BLOCK_HASH meaning that light client or wormhole is not yet synced with the block that transaction was included in
  • Replace placeholder values (addresses, amounts, hashes) with actual values
  • Token amounts are specified in their smallest units (e.g., wei for ETH, yoctoNEAR for NEAR)
  • Always test with small amounts on testnet first
  • Ensure you have sufficient funds for gas fees and storage deposits

Log metadata

First, we will need to log the token's metadata. To do that we will use log-metadata command and VGAS token address 0xF3dFcfFc520811D76236892e88d112D2A4a69D0d with base: prefix in front of it:

# Log VGAS token metadata on Base
bridge-cli mainnet log-metadata --token base:0xF3dFcfFc520811D76236892e88d112D2A4a69D0d

After that, we will get:

2025-08-05T22:24:45.499822Z LOG METADATA: Sent new bridge token transaction
tx_hash="0xa7c4284c84a847ec8938b15dd14c4ca83da8d49439615561c229b7411b809e4c"

If we examine the transaction via Basescan we will see that it took only $0.001 from our account.

Deploying token to NEAR

To deploy your token to NEAR you will need to wait now around 20 minutes for transfer to be catched up by the Omnibridge services. So you can relax, and get you some tea at this moment. Meditate or just gaze around, relax a bit :)

note

EVM part, except Ethereum, is covered right now by the Wormhole bridge (in future, it will secured by Chain Signatures on NEAR). So, you can also monitor your transactions via WormholeScan. When available there, you can try executing deploy-token method.

After that, be ready to execute:

bridge-cli mainnet deploy-token \
--source-chain Base \
--chain Near \
--tx-hash 0xa7c4284c84a847ec8938b15dd14c4ca83da8d49439615561c229b7411b809e4c

As you can see, I have used the log-metadata transaction hash in the command above. You will see an output similar to the next one:

2025-08-05T22:48:51.107287Z DEPLOY TOKEN: Sent deploy token transaction
tx_hash="EPJWeaHRZWZBf49YgYDj55oHefSZPgQmRye2WsnnbxRv"

Now, if you take a look at the details of that transaction on Nearblocks, you will see that:

  1. It spent 3.00771 NEAR from karkunow.near account
  2. It called deploy_token method on omni.bridge.near contract.
  3. In the Interacted With field, you can notice a new account created there and 3 NEAR got deposited into it.

Here is how it looks like in the Interacted With field:

NEP-141 token account on NearBlocks

This is exactly the NEP-141 token account which is a bridged version of the VGAS token, but now on NEAR!

As you can see, its name mirrors the Base ERC-20 name, but has a prefix base- and suffix .omdep.near added, with lowercased ERC-20 address in-between these.

Bridging your tokens

Now, I want to move 100 VGAS from Base to NEAR.

To initialize the transfer we'll need to estimate fees first by providing recipient, sender and token to the API:

curl 'https://mainnet.api.bridge.nearone.org/api/v1/transfer-fee?recipient=near:karkunow.near&sender=base:0xBe3e4Cb196bC9579c1C161cA5a2A4D47eD439469&token=base:0xF3dFcfFc520811D76236892e88d112D2A4a69D0d'

We have used the next arguments above:

  • Recipient: near:karkunow.near
  • Sender: base:0xBe3e4Cb196bC9579c1C161cA5a2A4D47eD439469
  • Token: base:0xF3dFcfFc520811D76236892e88d112D2A4a69D0d

The command will output us:

{"native_token_fee":"3273397399996","transferred_token_fee":null,"usd_fee":0.01}

usd_fee just shows how much it will cost to us to execute the bridge transaction on Base in USD. But we won't need that field for now.

We should use native_token_fee as native_fee and transferred_token_fee as fee in the initialization command. Let's initialize transfer on Base by using these values:


bridge-cli mainnet evm-init-transfer \
--chain base \
--token 0xF3dFcfFc520811D76236892e88d112D2A4a69D0d \
--amount 100000000000000000000 \
--recipient near:karkunow.near \
--fee 0 \
--native-fee 3273397399996 \
--message ""
tip

Leave --message to be an empty string if you're making transfer to the regular NEAR account, it should be used only to call methods on the NEAR smart contracts.

I have used Wei converter to get 18 decimals for my 100 VGAS transfer to get the correct number with 18 decimals.

I get this as an output after execution of the command above:

2025-08-06T13:17:52.048469Z EVM INIT TRANSFER: Sent transfer transaction
tx_hash="0x7df8da588a7894ce666aa374b37ccb9c00bb454d97f92cc30be2578762c5237a"

If you'll go to the Basescan, you will see that inside that transaction initTransfer method was called on the bridge Base contract:

initTransfer transaction on Basescan

Then you will need to wait for around 20-25 minutes for that action to be accepted by Omni Bridge services.

Just a reminder, for Base and Arbitrum, you can also monitor your transactions via WormholeScan.

If you go to WormholeScan and enter your base EOA there, you will see a list of your bridge-related transactions there:

Wormhole VAA dashboard

The status will switch to COMPLETED in 20-25 minutes, the transaction info will look like this:

Omni relayer transactions list on NearBlocks

Transaction will be finalized automatically for you by omni-relayer.bridge.near account on the NEAR side.

Finding finalization transaction

Simple way

Just go by URL to call OmniBridge API, or curl it:

curl https://mainnet.api.bridge.nearone.org/api/v1/transfers/?transaction_id=0x7df8da588a7894ce666aa374b37ccb9c00bb454d97f92cc30be2578762c5237a&offset=0&limit=5

Put your transaction hash as transaction_id there. You will recieve the next data in response:

[
{
"id": {
"origin_chain": "Base",
"origin_nonce": 11
},
"initialized": {
"EVMLog": {
"block_height": 33848463,
"block_timestamp_seconds": 1754486273,
"transaction_hash": "0x7df8da588a7894ce666aa374b37ccb9c00bb454d97f92cc30be2578762c5237a"
}
},
"signed": null,
"updated_fee": [],
"fast_finalised_on_near": null,
"finalised_on_near": null,
"fast_finalised": null,
"finalised": {
"NearReceipt": {
"block_height": 158474326,
"block_timestamp_seconds": 1754487576,
"transaction_hash": "C2SsiJYnGdHXkDGm6SP7XxaBF2c9Z8MB8RL4kgXy4XtM"
}
},
"claimed": null,
"transfer_message": {
"token": "base:0xf3dfcffc520811d76236892e88d112d2a4a69d0d",
"amount": "100000000000000000000",
"sender": "base:0xbe3e4cb196bc9579c1c161ca5a2a4d47ed439469",
"recipient": "near:karkunow.near",
"fee": {
"fee": "0",
"native_fee": "3273397399996"
},
"msg": ""
}
}
]

Inside "NearReceipt" you will find "transaction_hash", which is the hash of your finalization transaction on the target blockchain.

Complex way

Now, let's take a look how you could have find it in NearBlocks explorer.

Note the time of transaction from WormholeScan or BaseScan, it was 2025-08-06 13:17:53 UTC in this case.

Now, if you want to find the finalization transaction, go to the Omni Bridge Relayer on NearBlocks, and find the transaction there with a timestamp around that time plus 20 minutes, so it should be 2025-08-06 13:37:53 UTC.

You could hover on the AGE field to see timestamps, like this:

mint event in finalization transaction

As you can see there is only one close enough to my time here. And that is exactly the transaction we have found via API in the last section.

Let's click on it, and then scroll Interacted With (To) list of event until you see mint functions on base-... contract:

execution plan details for finalization transaction

When you click on the small arrow near the mint event you will be redirected to the Execution Plan tab, and see more info about it:

execution plan details for finalization transaction

You should see the exact info you have passed to the evm-init-transfer command above!

That is it, you now have your tokens on your balance, which you can check in your NEAR Wallet, e.g., MyNearWallet.

Conclusions

That is it! We have bridged your first tokens via Omni Bridge CLI together!

You can take a look at the transactions which happened during this tutorial on block explorer:

Deployment cost: $0.033 Storage cost: $7.47 (3 NEAR)

If you are ready - let's start a new virtual chain with that token now in the next article!

If you have encountered any errors, problems or just have any questions or suggestions, please contact us on Discord.