Skip to main content
This guide explains the complete lifecycle of a cross-chain swap order, from creation to final settlement.

Order States

Orders transition through multiple states during execution:
enum OrderStatus {
  Pending = 'pending',
  InProgress = 'in_progress',
  Executed = 'executed',
  Expired = 'expired',
  Refunded = 'refunded',
  Cancelled = 'cancelled'
}

State Descriptions

Initial state after order submission.
  • Order is visible to resolvers
  • Waiting for resolver to accept
  • No on-chain activity yet
  • Can be cancelled by maker
const { hash } = await sdk.submitOrder(srcChainId, order, quoteId, secretHashes)
const { status } = await sdk.getOrderStatus(hash)
console.log(status) // 'pending'
Active execution - resolver has accepted and deployed escrows.
  • Resolver is deploying source and destination escrows
  • Secrets need to be shared as escrows are deployed
  • Funds are locked in escrows
  • Cannot be cancelled
const secretsToShare = await sdk.getReadyToAcceptSecretFills(hash)
if (secretsToShare.fills.length > 0) {
  console.log('Order in progress, sharing secrets')
}
Successfully completed - all escrows settled.
  • User received destination tokens
  • Resolver received source tokens
  • Fees collected (if applicable)
  • Final state (terminal)
const { status } = await sdk.getOrderStatus(hash)
if (status === OrderStatus.Executed) {
  console.log('Swap completed successfully!')
}
Time limit exceeded - order not filled in time.
  • Escrow timelocks expired
  • Funds can be refunded
  • May require manual refund transaction
  • Final state (terminal)
const { status } = await sdk.getOrderStatus(hash)
if (status === OrderStatus.Expired) {
  console.log('Order expired, request refund')
  // Handle refund process
}
Funds returned to maker after expiration or cancellation.
  • Original tokens returned to maker
  • All escrows settled or cancelled
  • Final state (terminal)
const { status } = await sdk.getOrderStatus(hash)
if (status === OrderStatus.Refunded) {
  console.log('Funds have been refunded')
}
Cancelled by maker before resolver acceptance.
  • Only possible in Pending state
  • No on-chain activity occurred
  • Order removed from relayer
  • Final state (terminal)
// Cancel a pending order
await sdk.cancelOrder(hash)
const { status } = await sdk.getOrderStatus(hash)
console.log(status) // 'cancelled'

State Transitions

Monitoring Order Status

1

Get Current Status

Check the current state of an order.
const statusResponse = await sdk.getOrderStatus(orderHash)

console.log('Status:', statusResponse.status)
console.log('Details:', statusResponse)
The response includes:
  • status - Current OrderStatus
  • fills - Array of fill events
  • srcChainId / dstChainId - Chain information
  • Additional metadata
2

Poll for Status Changes

Continuously monitor status until completion.
async function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

async function waitForCompletion(orderHash: string): Promise<OrderStatus> {
  while (true) {
    const { status } = await sdk.getOrderStatus(orderHash)

    // Check for terminal states
    if (
      status === OrderStatus.Executed ||
      status === OrderStatus.Expired ||
      status === OrderStatus.Refunded ||
      status === OrderStatus.Cancelled
    ) {
      return status
    }

    console.log('Current status:', status)
    await sleep(2000) // Poll every 2 seconds
  }
}

const finalStatus = await waitForCompletion(orderHash)
console.log('Order finished with status:', finalStatus)
Adjust polling interval based on expected execution time. For faster presets, use shorter intervals (1-2s). For slower presets, use longer intervals (5-10s).
3

Check Ready Secrets

Monitor when secrets need to be shared.
const secretsToShare = await sdk.getReadyToAcceptSecretFills(orderHash)

console.log('Fills ready for secrets:', secretsToShare.fills)
// [{ idx: 0 }, { idx: 1 }, ...]

for (const { idx } of secretsToShare.fills) {
  console.log(`Secret ${idx} is ready to be shared`)
}

Complete Monitoring Example

Implement comprehensive order monitoring:
import { SDK, OrderStatus } from '@1inch/cross-chain-sdk'

async function monitorOrder(
  sdk: SDK,
  orderHash: string,
  secrets: string[]
): Promise<void> {
  const alreadyShared = new Set<number>()
  let lastStatus = ''

  while (true) {
    try {
      // Check for secrets to share
      const secretsToShare = await sdk.getReadyToAcceptSecretFills(orderHash)

      if (secretsToShare.fills.length > 0) {
        for (const { idx } of secretsToShare.fills) {
          if (!alreadyShared.has(idx)) {
            console.log(`Sharing secret ${idx}...`)
            
            // In production, verify escrow addresses here
            await sdk.submitSecret(orderHash, secrets[idx])
            alreadyShared.add(idx)
            
            console.log(`✓ Secret ${idx} shared successfully`)
          }
        }
      }

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

      // Log status changes
      if (status !== lastStatus) {
        console.log(`Status changed: ${lastStatus}${status}`)
        lastStatus = status
      }

      // Handle terminal states
      if (status === OrderStatus.Executed) {
        console.log('✓ Order executed successfully!')
        const finalStatus = await sdk.getOrderStatus(orderHash)
        console.log('Final details:', finalStatus)
        break
      }

      if (status === OrderStatus.Expired) {
        console.log('⚠ Order expired - initiating refund')
        // Handle refund process
        break
      }

      if (status === OrderStatus.Refunded) {
        console.log('✓ Funds refunded')
        break
      }

      if (status === OrderStatus.Cancelled) {
        console.log('Order was cancelled')
        break
      }

      // Wait before next check
      await sleep(2000)
    } catch (error) {
      console.error('Error monitoring order:', error)
      // Implement retry logic or error handling
      await sleep(5000)
    }
  }
}

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

Handling Different Outcomes

Successful Execution

const { status, fills } = await sdk.getOrderStatus(orderHash)

if (status === OrderStatus.Executed) {
  console.log('Swap completed!')
  console.log('Number of fills:', fills.length)
  
  // Get transaction details for each fill
  fills.forEach((fill, idx) => {
    console.log(`Fill ${idx}:`, fill)
  })
  
  // Update UI or notify user
  notifyUser('Your swap completed successfully!')
}

Expired Orders

if (status === OrderStatus.Expired) {
  console.log('Order expired - time limit exceeded')
  
  // Check if refund is needed
  const refundStatus = await checkRefundStatus(orderHash)
  
  if (refundStatus.needsRefund) {
    console.log('Initiating refund process...')
    // Trigger refund transaction
  } else {
    console.log('Refund already processed')
  }
  
  // Notify user
  notifyUser('Your order expired. Funds will be refunded.')
}

Refunded Orders

if (status === OrderStatus.Refunded) {
  console.log('Order refunded')
  
  const details = await sdk.getOrderStatus(orderHash)
  
  // Log refund details
  console.log('Refund details:', {
    srcChainId: details.srcChainId,
    amount: details.amount,
    token: details.srcTokenAddress
  })
  
  // Update user balance display
  notifyUser('Your funds have been refunded')
}

Advanced Monitoring

Event-Based Monitoring

Implement event handlers for state changes:
type OrderEventHandler = (orderHash: string, status: OrderStatus) => void

class OrderMonitor {
  private handlers: Map<OrderStatus, OrderEventHandler[]> = new Map()

  on(status: OrderStatus, handler: OrderEventHandler): void {
    if (!this.handlers.has(status)) {
      this.handlers.set(status, [])
    }
    this.handlers.get(status)!.push(handler)
  }

  async monitor(sdk: SDK, orderHash: string, secrets: string[]): Promise<void> {
    const alreadyShared = new Set<number>()
    let lastStatus: OrderStatus | null = null

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

      for (const { idx } of secretsToShare.fills) {
        if (!alreadyShared.has(idx)) {
          await sdk.submitSecret(orderHash, secrets[idx])
          alreadyShared.add(idx)
        }
      }

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

      // Trigger handlers on status change
      if (status !== lastStatus) {
        this.trigger(orderHash, status)
        lastStatus = status
      }

      // Exit on terminal states
      if (
        status === OrderStatus.Executed ||
        status === OrderStatus.Expired ||
        status === OrderStatus.Refunded ||
        status === OrderStatus.Cancelled
      ) {
        break
      }

      await sleep(2000)
    }
  }

  private trigger(orderHash: string, status: OrderStatus): void {
    const handlers = this.handlers.get(status) || []
    handlers.forEach((handler) => handler(orderHash, status))
  }
}

// Usage
const monitor = new OrderMonitor()

monitor.on(OrderStatus.InProgress, (hash, status) => {
  console.log('Order started:', hash)
})

monitor.on(OrderStatus.Executed, (hash, status) => {
  console.log('Order completed:', hash)
  notifyUser('Swap successful!')
})

monitor.on(OrderStatus.Expired, (hash, status) => {
  console.log('Order expired:', hash)
  handleExpiredOrder(hash)
})

await monitor.monitor(sdk, orderHash, secrets)

Timeout Handling

Implement maximum monitoring duration:
async function monitorWithTimeout(
  sdk: SDK,
  orderHash: string,
  secrets: string[],
  timeoutMs: number = 600000 // 10 minutes
): Promise<OrderStatus> {
  const startTime = Date.now()
  const alreadyShared = new Set<number>()

  while (true) {
    // Check timeout
    if (Date.now() - startTime > timeoutMs) {
      console.warn('Monitoring timeout exceeded')
      const { status } = await sdk.getOrderStatus(orderHash)
      return status
    }

    const secretsToShare = await sdk.getReadyToAcceptSecretFills(orderHash)

    for (const { idx } of secretsToShare.fills) {
      if (!alreadyShared.has(idx)) {
        await sdk.submitSecret(orderHash, secrets[idx])
        alreadyShared.add(idx)
      }
    }

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

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

    await sleep(2000)
  }
}

const finalStatus = await monitorWithTimeout(sdk, orderHash, secrets, 600000)
console.log('Final status:', finalStatus)

Best Practices

Verify Before Sharing

Always verify escrow addresses before sharing secrets to prevent unauthorized withdrawals.

Track Shared Secrets

Maintain a set of shared secret indices to avoid duplicate submissions.

Handle All States

Implement handlers for all possible terminal states: Executed, Expired, Refunded, Cancelled.

Set Timeouts

Implement maximum monitoring duration to prevent infinite loops.

Log Events

Log all state transitions for debugging and analytics.

Retry on Errors

Implement retry logic for network errors during monitoring.

Terminal States Summary

StateDescriptionUser Action Required
Executed✓ SuccessNone - tokens received
Expired⚠ Time limit exceededMay need to trigger refund
Refunded✓ Funds returnedNone - tokens returned
CancelledOrder cancelledNone - no fees incurred

Next Steps

EVM to EVM

Implement your first swap

WebSocket API

Real-time order monitoring

Build docs developers (and LLMs) love