Skip to main content
This guide demonstrates how to perform cross-chain swaps involving native assets (ETH, BNB, AVAX, SOL, etc.). Native asset swaps require special handling compared to standard ERC-20 or SPL token swaps.

Overview

Native assets are the native currencies of blockchains (ETH on Ethereum, BNB on BSC, AVAX on Avalanche, SOL on Solana, etc.). When swapping native assets, you must:
  • Use the special address 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee for EVM chains
  • Use SolanaAddress.NATIVE for Solana
  • Use submitNativeOrder() instead of submitOrder() for EVM source chains
  • Use NativeOrdersFactory to create the on-chain transaction
  • Send native currency value with the transaction
Native asset orders require a different submission method (submitNativeOrder) and manual transaction broadcasting with the native asset value.

Complete Example: AVAX to USDC

1

Install and Import Dependencies

Install the required packages:
npm install @1inch/cross-chain-sdk ethers
import {
    EvmCrossChainOrder,
    HashLock,
    NetworkEnum,
    OrderStatus,
    PrivateKeyProviderConnector,
    SDK,
    EvmAddress,
    NativeOrdersFactory,
    Address
} from '@1inch/cross-chain-sdk'
import { JsonRpcProvider, Wallet } from 'ethers'
import { randomBytes } from 'node:crypto'
import assert from 'node:assert'
2

Setup SDK with Ethers Provider

Initialize the SDK with an ethers.js provider:
const PRIVATE_KEY = '0x...'
const WEB3_NODE_URL = 'https://...'
const AUTH_KEY = 'auth-key'

const ethersRpcProvider = new JsonRpcProvider(WEB3_NODE_URL)

// Create provider connector for ethers
const ethersProviderConnector = {
    eth: {
        call(transactionConfig): Promise<string> {
            return ethersRpcProvider.call(transactionConfig)
        }
    },
    extend(): void {}
}

const connector = new PrivateKeyProviderConnector(
    PRIVATE_KEY,
    ethersProviderConnector
)

const wallet = new Wallet(PRIVATE_KEY, ethersRpcProvider)

const sdk = new SDK({
    url: 'https://api.1inch.com/fusion-plus',
    authKey: AUTH_KEY,
    blockchainProvider: connector
})
3

Get Quote for Native Asset

Request a quote using the native asset address:
// 0.4 AVAX -> USDC (BSC)
const quote = await sdk.getQuote({
    amount: '400000000000000000', // 0.4 AVAX
    srcChainId: NetworkEnum.AVALANCHE,
    dstChainId: NetworkEnum.BINANCE,
    enableEstimate: true,
    srcTokenAddress: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', // Native AVAX
    dstTokenAddress: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC
    walletAddress: wallet.address
})
Use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee (42 ‘e’s) to represent native assets on any EVM chain.
4

Generate Secrets and Create Order

Generate secrets and create the order (same as standard swaps):
const preset = quote.recommendedPreset

const secrets = Array.from({
    length: quote.presets[preset].secretsCount
}).map(() => '0x' + randomBytes(32).toString('hex'))

const hashLock =
    secrets.length === 1
        ? HashLock.forSingleFill(secrets[0])
        : HashLock.forMultipleFills(HashLock.getMerkleLeaves(secrets))

const secretHashes = secrets.map((s) => HashLock.hashSecret(s))

const { hash, quoteId, order } = sdk.createOrder(quote, {
    walletAddress: wallet.address,
    hashLock,
    preset,
    source: 'sdk-tutorial',
    secretHashes
})

assert(order instanceof EvmCrossChainOrder)
console.log({ hash }, 'order created')
5

Submit Native Order

Use submitNativeOrder() instead of submitOrder():
const orderInfo = await sdk.submitNativeOrder(
    quote.srcChainId,
    order,
    EvmAddress.fromString(wallet.address),
    quoteId,
    secretHashes
)
console.log({ hash }, 'order submitted')
submitNativeOrder() prepares the order for native asset handling but doesn’t broadcast the transaction yet.
6

Create and Broadcast Transaction

Use NativeOrdersFactory to create the transaction with native value:
const factory = NativeOrdersFactory.default(NetworkEnum.AVALANCHE)
const call = factory.create(
    new Address(wallet.address),
    orderInfo.order
)

const txRes = await wallet.sendTransaction({
    to: call.to.toString(),
    data: call.data,
    value: call.value // Native asset amount
})

console.log({ txHash: txRes.hash }, 'transaction broadcasted')

// Wait for confirmation
await wallet.provider.waitForTransaction(txRes.hash, 3)
You must include the value field from call.value in the transaction. This is the native asset amount being swapped.
7

Monitor and Submit Secrets

Monitor the order and submit secrets (same as standard swaps):
async function sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms))
}

while (true) {
    const secretsToShare = await sdk.getReadyToAcceptSecretFills(hash)

    if (secretsToShare.fills.length) {
        for (const { idx } of secretsToShare.fills) {
            await sdk.submitSecret(hash, secrets[idx])
            console.log({ idx }, 'shared secret')
        }
    }

    const { status } = await sdk.getOrderStatus(hash)

    if (
        status === OrderStatus.Executed ||
        status === OrderStatus.Expired ||
        status === OrderStatus.Refunded
    ) {
        break
    }

    await sleep(1000)
}

const statusResponse = await sdk.getOrderStatus(hash)
console.log(statusResponse)

Full Working Example

import {
    EvmCrossChainOrder,
    HashLock,
    NetworkEnum,
    OrderStatus,
    PrivateKeyProviderConnector,
    SDK,
    EvmAddress,
    NativeOrdersFactory,
    Address
} from '@1inch/cross-chain-sdk'
import { JsonRpcProvider, Wallet } from 'ethers'
import { randomBytes } from 'node:crypto'
import assert from 'node:assert'

const PRIVATE_KEY = '0x...'
const WEB3_NODE_URL = 'https://...'
const AUTH_KEY = 'auth-key'

const ethersRpcProvider = new JsonRpcProvider(WEB3_NODE_URL)

const ethersProviderConnector = {
    eth: {
        call(transactionConfig): Promise<string> {
            return ethersRpcProvider.call(transactionConfig)
        }
    },
    extend(): void {}
}

const connector = new PrivateKeyProviderConnector(
    PRIVATE_KEY,
    ethersProviderConnector
)

const wallet = new Wallet(PRIVATE_KEY, ethersRpcProvider)

const sdk = new SDK({
    url: 'https://api.1inch.com/fusion-plus',
    authKey: AUTH_KEY,
    blockchainProvider: connector
})

async function sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms))
}

async function main(): Promise<void> {
    const quote = await sdk.getQuote({
        amount: '400000000000000000',
        srcChainId: NetworkEnum.AVALANCHE,
        dstChainId: NetworkEnum.BINANCE,
        enableEstimate: true,
        srcTokenAddress: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
        dstTokenAddress: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
        walletAddress: wallet.address
    })

    const preset = quote.recommendedPreset

    const secrets = Array.from({
        length: quote.presets[preset].secretsCount
    }).map(() => '0x' + randomBytes(32).toString('hex'))

    const hashLock =
        secrets.length === 1
            ? HashLock.forSingleFill(secrets[0])
            : HashLock.forMultipleFills(HashLock.getMerkleLeaves(secrets))

    const secretHashes = secrets.map((s) => HashLock.hashSecret(s))

    const { hash, quoteId, order } = sdk.createOrder(quote, {
        walletAddress: wallet.address,
        hashLock,
        preset,
        source: 'sdk-tutorial',
        secretHashes
    })

    assert(order instanceof EvmCrossChainOrder)
    console.log({ hash }, 'order created')

    const orderInfo = await sdk.submitNativeOrder(
        quote.srcChainId,
        order,
        EvmAddress.fromString(wallet.address),
        quoteId,
        secretHashes
    )
    console.log({ hash }, 'order submitted')

    const factory = NativeOrdersFactory.default(NetworkEnum.AVALANCHE)
    const call = factory.create(new Address(wallet.address), orderInfo.order)

    const txRes = await wallet.sendTransaction({
        to: call.to.toString(),
        data: call.data,
        value: call.value
    })

    console.log({ txHash: txRes.hash }, 'transaction broadcasted')
    await wallet.provider.waitForTransaction(txRes.hash, 3)

    while (true) {
        const secretsToShare = await sdk.getReadyToAcceptSecretFills(hash)

        if (secretsToShare.fills.length) {
            for (const { idx } of secretsToShare.fills) {
                await sdk.submitSecret(hash, secrets[idx])
                console.log({ idx }, 'shared secret')
            }
        }

        const { status } = await sdk.getOrderStatus(hash)

        if (
            status === OrderStatus.Executed ||
            status === OrderStatus.Expired ||
            status === OrderStatus.Refunded
        ) {
            break
        }

        await sleep(1000)
    }

    const statusResponse = await sdk.getOrderStatus(hash)
    console.log(statusResponse)
}

main()

Key Differences from Standard Swaps

Use special address for native assets:
// EVM native asset (ETH, BNB, AVAX, etc.)
srcTokenAddress: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'

// Solana native asset (SOL)
srcTokenAddress: SolanaAddress.NATIVE.toString()

Native Asset Examples

const quote = await sdk.getQuote({
    amount: '1000000000000000000', // 1 ETH
    srcChainId: NetworkEnum.ETHEREUM,
    dstChainId: NetworkEnum.POLYGON,
    srcTokenAddress: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
    dstTokenAddress: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
    walletAddress
})

Common Pitfalls

Using wrong submission method: Always use submitNativeOrder() when the source token is native. Use submitOrder() when only the destination is native.
Missing value field: Always include value: call.value when sending the transaction. Without it, the transaction will fail.
Incorrect native address: Make sure to use exactly 42 ‘e’s: 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee

Next Steps