Documentation Index Fetch the complete documentation index at: https://mintlify.com/1inch/cross-chain-sdk/llms.txt
Use this file to discover all available pages before exploring further.
This guide demonstrates how to create atomic swaps from EVM-compatible chains to Solana. This flow is simpler than Solana-to-EVM as it doesn’t require on-chain order publishing.
Overview
EVM to Solana swaps combine elements of both EVM and Solana workflows:
Orders are submitted to the relayer (like EVM-to-EVM)
No on-chain publishing required (unlike Solana-to-EVM)
Destination uses Solana addresses
Secret management follows the standard pattern
Prerequisites
EVM wallet with sufficient token balance
Token allowance approved for Limit Order Protocol
Solana destination wallet address
Dev Portal API key from portal.1inch.dev
Complete Example
This example swaps 10 USDT from Ethereum to USDT on Solana.
Setup and Import Dependencies
Import required modules and configure providers. import {
NetworkEnum ,
SDK ,
SolanaAddress ,
HashLock ,
EvmAddress ,
PrivateKeyProviderConnector ,
OrderStatus
} from '@1inch/cross-chain-sdk'
import { JsonRpcProvider , TransactionRequest , computeAddress } from 'ethers'
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 . EVM_PRIVATE_KEY
assert ( signerPrivateKey , 'Please provide EVM_PRIVATE_KEY' )
Initialize SDK with Provider
Set up the ethers provider and SDK with blockchain provider. const NODE_URL = 'https://web3.1inch.io/1'
const ethersRpcProvider = new JsonRpcProvider ( NODE_URL )
const ethersProviderConnector = {
eth: {
call ( transactionConfig : TransactionRequest ) : Promise < string > {
return ethersRpcProvider . call ( transactionConfig )
}
},
extend () : void {}
}
const connector = new PrivateKeyProviderConnector (
signerPrivateKey ,
ethersProviderConnector
)
const sdk = new SDK ({
url: 'https://api.1inch.com/fusion-plus' ,
authKey ,
blockchainProvider: connector
})
The blockchainProvider is required for order creation as it signs transactions.
Configure Addresses and Tokens
Set up the maker, receiver, and token addresses. const maker = computeAddress ( signerPrivateKey )
const receiver = '93FP8NG2JrScb9xzNsJrzAze8gJJtr1TgQWUCHDgP3BW'
const USDT_SOL = 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'
const USDT_ETHEREUM = '0xdac17f958d2ee523a2206206994597c13d831ec7'
const amount = 10_000_000 n // 10 USDT
const srcToken = EvmAddress . fromString ( USDT_ETHEREUM )
const dstToken = SolanaAddress . fromString ( USDT_SOL )
const srcChainId = NetworkEnum . ETHEREUM
const dstChainId = NetworkEnum . SOLANA
For native SOL on the destination, use SolanaAddress.NATIVE instead of a token address.
Get Quote
Request a quote for the EVM to Solana swap. const quote = await sdk . getQuote ({
amount: amount . toString (),
srcChainId ,
dstChainId ,
srcTokenAddress: srcToken . toString (),
dstTokenAddress: dstToken . toString (),
enableEstimate: true ,
walletAddress: maker
})
const preset = quote . getPreset ( quote . recommendedPreset )
assert ( quote . quoteId )
console . log ( 'Got preset' , preset )
Generate Secrets and Create Order
Generate secrets and create an EVM order with Solana receiver. 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 EVM order with Solana receiver
const order = quote . createEvmOrder ({
hashLock ,
receiver: SolanaAddress . fromString ( receiver ),
preset: quote . recommendedPreset
})
The key difference is using SolanaAddress.fromString() for the receiver.
Submit Order
Submit the order to the relayer (no on-chain publishing needed). const { orderHash } = await sdk . submitOrder (
srcChainId ,
order ,
quote . quoteId ,
secretHashes
)
console . log ( 'Submitted order to relayer' , orderHash )
Unlike Solana-to-EVM, EVM-to-Solana orders don’t require separate on-chain publishing.
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 ])
. catch (( err ) => console . error ( 'Failed to submit secret' , err ))
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
The main difference is the receiver address type: // EVM-to-EVM
const order = quote . createEvmOrder ({
receiver: EvmAddress . fromString ( receiver ),
// ...
})
// EVM-to-Solana
const order = quote . createEvmOrder ({
receiver: SolanaAddress . fromString ( receiver ),
// ...
})
Everything else follows the standard EVM flow.
EVM-to-Solana is simpler: No announceOrder required: // Solana-to-EVM requires announcement
const orderHash = await sdk . announceOrder ( order , quoteId , secretHashes )
// EVM-to-Solana uses direct submission
const { orderHash } = await sdk . submitOrder ( srcChainId , order , quoteId , secretHashes )
No on-chain publishing: // Solana-to-EVM requires on-chain publishing
const ix = SvmSrcEscrowFactory . DEFAULT . createOrder ( order , { ... })
await connection . sendTransaction ( tx , [ makerSigner ])
// EVM-to-Solana doesn't need this step
Error Handling
Implement robust error handling for production use:
try {
const { orderHash } = await sdk . submitOrder (
srcChainId ,
order ,
quote . quoteId ,
secretHashes
)
} catch ( error ) {
if ( error . message . includes ( 'insufficient allowance' )) {
console . error ( 'Token allowance not approved' )
// Guide user to approve token
} else if ( error . message . includes ( 'insufficient balance' )) {
console . error ( 'Insufficient token balance' )
} else if ( error . message . includes ( 'invalid receiver' )) {
console . error ( 'Invalid Solana address format' )
} else {
console . error ( 'Order submission failed:' , error )
}
throw error
}
Secret Sharing Error Handling
Handle errors during secret submission gracefully:
for ( const idx of idxes ) {
if ( ! alreadyShared . has ( idx )) {
try {
await sdk . submitSecret ( orderHash , secrets [ idx ])
alreadyShared . add ( idx )
console . log ( 'Submitted secret' , idx )
} catch ( err ) {
console . error ( 'Failed to submit secret' , idx , err )
// Don't add to alreadyShared - retry on next iteration
}
}
}
Best Practices
Validate Addresses Verify Solana receiver addresses are valid before creating orders.
Handle Retries Implement retry logic for secret submission failures.
Monitor Status Continuously monitor order status with appropriate intervals.
Log Everything Log all steps for debugging and tracking purposes.
Receiver Address Validation
Validate Solana addresses before using them:
import { PublicKey } from '@solana/web3.js'
function isValidSolanaAddress ( address : string ) : boolean {
try {
new PublicKey ( address )
return true
} catch {
return false
}
}
const receiver = '93FP8NG2JrScb9xzNsJrzAze8gJJtr1TgQWUCHDgP3BW'
if ( ! isValidSolanaAddress ( receiver )) {
throw new Error ( 'Invalid Solana receiver address' )
}
Next Steps
Solana to EVM Learn about swaps from Solana to EVM
Order Lifecycle Understand order states and monitoring