Skip to main content
This guide demonstrates how to create atomic swaps from Solana to EVM-compatible chains. Solana swaps require both relayer announcement and on-chain order publishing.

Overview

Solana to EVM swaps differ from EVM-to-EVM swaps:
  • Orders must be announced to the relayer using announceOrder()
  • Orders must be published on-chain using Solana transactions
  • Address formats use SolanaAddress for Solana and EvmAddress for EVM chains
  • Native Solana tokens use SolanaAddress.NATIVE

Prerequisites

  • Solana wallet with sufficient SOL for transaction fees
  • Sufficient token balance for the swap
  • Dev Portal API key from portal.1inch.dev

Complete Example

This example swaps 10 USDT from Solana to USDT on Ethereum.
1

Setup Dependencies

Import required modules and initialize the SDK.
import {
  NetworkEnum,
  SDK,
  SolanaAddress,
  HashLock,
  EvmAddress,
  SvmSrcEscrowFactory,
  OrderStatus
} from '@1inch/cross-chain-sdk'
import { utils, web3 } from '@coral-xyz/anchor'
import { randomBytes } from 'node:crypto'
import { setTimeout } from 'node:timers/promises'
import assert from 'node:assert'

const authKey = process.env.DEV_PORTAL_API_TOKEN
assert(authKey, 'Please provide DEV_PORTAL_API_TOKEN')

const signerPrivateKey = process.env.SOLANA_PRIVATE_KEY
assert(signerPrivateKey, 'Please provide SOLANA_PRIVATE_KEY')

const makerSigner = web3.Keypair.fromSecretKey(
  utils.bytes.bs58.decode(signerPrivateKey)
)
2

Initialize SDK and Addresses

Configure the SDK and set up token addresses.
const SOLANA_RPC = 'https://api.mainnet-beta.solana.com'
const sdk = new SDK({
  url: 'https://api.1inch.com/fusion-plus',
  authKey
})

// Token addresses
const USDT_SOL = 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'
const USDT_ETHEREUM = '0xdac17f958d2ee523a2206206994597c13d831ec7'

const maker = makerSigner.publicKey.toBase58()
const receiver = '0x962a836519109e162754161000D65d9Dc027Fa0F'

const srcToken = SolanaAddress.fromString(USDT_SOL)
const dstToken = EvmAddress.fromString(USDT_ETHEREUM)
const amount = 10_000_000n // 10 USDT

const srcChainId = NetworkEnum.SOLANA
const dstChainId = NetworkEnum.ETHEREUM
For native SOL, use EvmAddress.NATIVE as the token address.
3

Get Quote

Request a quote for the Solana to EVM swap.
console.log(
  `Creating order from wallet ${maker} for [${srcChainId}] ${amount} ${srcToken} -> ${dstToken} [${dstChainId}]`
)

const quote = await sdk.getQuote({
  amount: amount.toString(),
  srcChainId,
  dstChainId,
  srcTokenAddress: srcToken.toString(),
  dstTokenAddress: dstToken.toString(),
  enableEstimate: true,
  walletAddress: maker
})

console.log('Got quote', quote)

const preset = quote.getPreset(quote.recommendedPreset)
assert(quote.quoteId)
4

Generate Secrets and Create Order

Generate secrets and create the Solana order.
function getSecret(): string {
  return '0x' + randomBytes(32).toString('hex')
}

const secrets = Array.from({ length: preset.secretsCount }).map(getSecret)
const secretHashes = secrets.map(HashLock.hashSecret)
const leaves = HashLock.getMerkleLeaves(secrets)

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

// Create Solana-specific order
const order = quote.createSolanaOrder({
  hashLock,
  receiver: EvmAddress.fromString(receiver),
  preset: quote.recommendedPreset
})
5

Announce Order to Relayer

First, announce the order to the 1inch relayer network.
const orderHash = await sdk.announceOrder(order, quote.quoteId, secretHashes)
console.log('Announced order to relayer', orderHash)
This registers your order with the relayer before publishing on-chain.
6

Publish Order On-Chain

Create and submit the Solana transaction to publish the order on-chain.
// Create instruction for order creation
const ix = SvmSrcEscrowFactory.DEFAULT.createOrder(order, {
  srcTokenProgramId: SolanaAddress.TOKEN_PROGRAM_ID
})

// Build transaction
const tx = new web3.Transaction().add({
  data: ix.data,
  programId: new web3.PublicKey(ix.programId.toBuffer()),
  keys: ix.accounts.map((a) => ({
    isSigner: a.isSigner,
    isWritable: a.isWritable,
    pubkey: new web3.PublicKey(a.pubkey.toBuffer())
  }))
})

// Send transaction
const connection = new web3.Connection(SOLANA_RPC)
const result = await connection.sendTransaction(tx, [makerSigner])

console.log('Submitted order', result)
await setTimeout(5000) // Wait for transaction confirmation
Wait for transaction confirmation before proceeding to secret sharing.
7

Monitor and Share Secrets

Monitor escrow deployments and share secrets when ready.
const alreadyShared = new Set<number>()

while (true) {
  const readyToAcceptSecrets = await sdk.getReadyToAcceptSecretFills(orderHash)
  const idxes = readyToAcceptSecrets.fills.map((f) => f.idx)

  for (const idx of idxes) {
    if (!alreadyShared.has(idx)) {
      // Verify escrow addresses before sharing secrets
      await sdk.submitSecret(orderHash, secrets[idx])
      alreadyShared.add(idx)
      console.log('Submitted secret', secrets[idx])
    }
  }

  // Check if order finished
  const { status } = await sdk.getOrderStatus(orderHash)

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

  await setTimeout(5000)
}

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

Key Differences from EVM

Solana orders require calling sdk.announceOrder() instead of sdk.submitOrder(). This registers the order with the relayer before on-chain publication.
// Solana
const orderHash = await sdk.announceOrder(order, quote.quoteId, secretHashes)

// vs EVM
const { orderHash } = await sdk.submitOrder(srcChainId, order, quoteId, secretHashes)
After announcement, Solana orders must be published on-chain using SvmSrcEscrowFactory:
const ix = SvmSrcEscrowFactory.DEFAULT.createOrder(order, {
  srcTokenProgramId: SolanaAddress.TOKEN_PROGRAM_ID
})
EVM orders are automatically published when submitted to the relayer.
Use proper address types for each chain:
// Solana addresses
const srcToken = SolanaAddress.fromString(USDT_SOL)
const maker = makerSigner.publicKey.toBase58()

// EVM addresses
const dstToken = EvmAddress.fromString(USDT_ETHEREUM)
const receiver = EvmAddress.fromString('0x...')
Solana requires building and sending transactions manually:
const connection = new web3.Connection(SOLANA_RPC)
const result = await connection.sendTransaction(tx, [makerSigner])
await connection.waitForTransaction(result)

Error Handling

try {
  const result = await connection.sendTransaction(tx, [makerSigner])
  await setTimeout(5000)
} catch (error) {
  if (error.message.includes('insufficient funds')) {
    console.error('Insufficient SOL for transaction fees')
  } else if (error.message.includes('TokenAccountNotFound')) {
    console.error('Token account does not exist')
  } else {
    console.error('Transaction failed:', error)
  }
  throw error
}

Best Practices

Wait for Confirmation

Always wait for Solana transaction confirmation before sharing secrets.

Check Token Accounts

Verify token accounts exist before attempting swaps.

Use Recommended Preset

Use quote.recommendedPreset for optimal execution.

Track Shared Secrets

Maintain a set of shared secret indices to avoid duplicates.

Next Steps

EVM to Solana

Learn about swaps from EVM to Solana

Order Lifecycle

Understand order states and transitions

Build docs developers (and LLMs) love