NEAR is the hub of the Omni Bridge — all cross-chain transfers route through it. This guide covers bridging tokens from NEAR to other chains.
Setup
import { createBridge } from "@omni-bridge/core"
import { createNearBuilder , toNearKitTransaction } from "@omni-bridge/near"
import { Near } from "near-kit"
const bridge = createBridge ({ network: "mainnet" })
const nearBuilder = createNearBuilder ({ network: "mainnet" })
const near = new Near ({
network: "mainnet" ,
privateKey: "ed25519:..." ,
})
Complete Transfer
import { createBridge } from "@omni-bridge/core"
import { createNearBuilder , toNearKitTransaction } from "@omni-bridge/near"
import { Near } from "near-kit"
const bridge = createBridge ({ network: "mainnet" })
const nearBuilder = createNearBuilder ({ network: "mainnet" })
const near = new Near ({
network: "mainnet" ,
privateKey: "ed25519:..." ,
})
const signerId = "alice.near"
// 1. Check storage deposit (required before first transfer)
const deposit = await nearBuilder . getRequiredStorageDeposit ( signerId )
if ( deposit > 0 n ) {
const depositTx = nearBuilder . buildStorageDeposit ( signerId , deposit )
await toNearKitTransaction ( near , depositTx ). send ()
console . log ( "Storage deposit paid" )
}
// 2. Validate
const validated = await bridge . validateTransfer ({
token: "near:wrap.near" , // wNEAR
amount: 1_000_000_000_000_000_000_000_000 n , // 1 NEAR (24 decimals)
sender: `near: ${ signerId } ` ,
recipient: "eth:0x1234567890123456789012345678901234567890" ,
fee: 0 n ,
nativeFee: 0 n ,
})
// 3. Transfer
const tx = nearBuilder . buildTransfer ( validated , signerId )
const result = await toNearKitTransaction ( near , tx ). send ()
console . log ( "Transfer initiated:" , result . transaction . hash )
Using @near-js/* instead of near-kit
import { createBridge } from "@omni-bridge/core"
import { createNearBuilder , sendWithNearApiJs } from "@omni-bridge/near"
import { Account } from "@near-js/accounts"
import { JsonRpcProvider } from "@near-js/providers"
import { InMemoryKeyStore } from "@near-js/keystores"
import { KeyPair } from "@near-js/crypto"
import { InMemorySigner } from "@near-js/signers"
const bridge = createBridge ({ network: "mainnet" })
const nearBuilder = createNearBuilder ({ network: "mainnet" })
const keyStore = new InMemoryKeyStore ()
await keyStore . setKey ( "mainnet" , "alice.near" , KeyPair . fromString ( "ed25519:..." ))
const provider = new JsonRpcProvider ({ url: "https://rpc.mainnet.near.org" })
const signer = new InMemorySigner ( keyStore )
const account = new Account ({ accountId: "alice.near" , provider , signer })
const signerId = "alice.near"
// 1. Storage deposit
const deposit = await nearBuilder . getRequiredStorageDeposit ( signerId )
if ( deposit > 0 n ) {
const depositTx = nearBuilder . buildStorageDeposit ( signerId , deposit )
await sendWithNearApiJs ( account , depositTx )
}
// 2. Validate
const validated = await bridge . validateTransfer ({
token: "near:wrap.near" ,
amount: 1_000_000_000_000_000_000_000_000 n ,
sender: `near: ${ signerId } ` ,
recipient: "eth:0x1234567890123456789012345678901234567890" ,
fee: 0 n ,
nativeFee: 0 n ,
})
// 3. Transfer
const tx = nearBuilder . buildTransfer ( validated , signerId )
const result = await sendWithNearApiJs ( account , tx )
console . log ( "Transfer initiated:" , result . transaction . hash )
Storage Deposits
NEAR requires storage deposits before interacting with contracts. Always check before the first transfer.
// Check if deposit is needed
const required = await nearBuilder . getRequiredStorageDeposit ( signerId )
if ( required > 0 n ) {
const depositTx = nearBuilder . buildStorageDeposit ( signerId , required )
await toNearKitTransaction ( near , depositTx ). send ()
}
This is typically ~0.00125 NEAR and only needed once per account.
Token Storage
Some NEP-141 tokens also require storage registration:
const isRegistered = await nearBuilder . isTokenStorageRegistered ( "wrap.near" )
if ( ! isRegistered ) {
const tokenDepositTx = await nearBuilder . buildTokenStorageDeposit (
"wrap.near" , // Token contract
signerId // Account to register
)
await toNearKitTransaction ( near , tokenDepositTx ). send ()
}
Shims Explained
NEAR transactions need runtime context (nonce, block hash) that the SDK doesn’t fetch. Shims bridge this gap:
Function Library What it does toNearKitTransaction()near-kit Returns a chainable TransactionBuilder sendWithNearApiJs()@near-js/* Signs and sends in one call toNearApiJsActions()@near-js/* Returns raw Actions for manual handling
The SDK returns a plain object:
interface NearUnsignedTransaction {
type : "near"
signerId : string
receiverId : string
actions : NearAction []
}
Shims convert this to the format your library expects and handle nonce/blockhash automatically.
Receiving on NEAR (From Other Chains)
When tokens arrive on NEAR from another chain, they land in your account automatically (if relayer fees were paid). Bridged tokens from EVM chains use the format <eth_address>.factory.bridge.near — for example, USDC from Ethereum becomes a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near.
For manual finalization, see Manual Finalization .
Common Token Addresses
Token NEAR Address wNEAR wrap.nearBridged USDC (from Eth) a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.nearBridged USDT (from Eth) dac17f958d2ee523a2206206994597c13d831ec7.factory.bridge.nearBridged WETH (from Eth) c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.factory.bridge.nearnBTC (bridged Bitcoin) nbtc.bridge.near
buildTransfer() returns a NearUnsignedTransaction:
interface NearUnsignedTransaction {
type : "near"
signerId : string
receiverId : string
actions : Array <{
type : "FunctionCall"
methodName : string
args : Uint8Array
gas : bigint
deposit : bigint
}>
}
Error Handling
import { ValidationError } from "@omni-bridge/core"
try {
const deposit = await nearBuilder . getRequiredStorageDeposit ( signerId )
if ( deposit > 0 n ) {
const tx = nearBuilder . buildStorageDeposit ( signerId , deposit )
await toNearKitTransaction ( near , tx ). send ()
}
const validated = await bridge . validateTransfer ( params )
const tx = nearBuilder . buildTransfer ( validated , signerId )
await toNearKitTransaction ( near , tx ). send ()
} catch ( error ) {
if ( error instanceof ValidationError ) {
console . error ( `Validation failed: ${ error . code } ` )
} else if ( error . message ?. includes ( "insufficient storage" )) {
console . error ( "Storage deposit required" )
} else if ( error . message ?. includes ( "insufficient balance" )) {
console . error ( "Not enough tokens" )
}
}
Finalization Timeline
NEAR → EVM transfers typically take:
Stage Time NEAR confirmation ~1-2 seconds MPC signature ~1-5 minutes EVM finalization ~2-15 minutes
Total: 5-20 minutes depending on destination chain and network conditions.
Next Steps