This guide will help you integrate the Eco Routes SDK to enable one-click stablecoin transfers across chains. In this quickstart, you will:
1

Install SDK

Install the Eco SDK and peer dependency.
2

Create Transfer Intent

Create a Stable transfer intent to send USDC from Optimism to Base.
3

Request and Apply a Quote

Get quotes for the Eco Network to fulfill your intent.
4

Publish Intent

Publish your intent to the origin chain.
5

Track Fulfillment

Track the fulfillment of your intent on the destination chain.

Step 1: Installation

Install the SDK along with its peer dependencies:
npm install @eco-foundation/routes-sdk @eco-foundation/routes-ts

Step 2: Creating an Intent

An intent represents the desired transfer outcome on a destination chain. The below code shows creating an intent that sends USDC from Optimism (Chain ID 10) to Base (Chain ID 8453). To create a simple stable send intent, create an instance of the RoutesService and call createSimpleIntent with the required parameters:
// Import RoutesService
import { RoutesService } from '@eco-foundation/routes-sdk';

// Define transfer paramaters
const address = '0x1234567890123456789012345678901234567890';
const originChainID = 10;
const spendingToken = RoutesService.getStableAddress(originChainID, 'USDC');
const spendingTokenLimit = BigInt(10000000); // 10 USDC
const destinationChainID = 8453;
const receivingToken = RoutesService.getStableAddress(destinationChainID, 'USDC');

const amount = BigInt(1000000); // 1 USDC

const routesService = new RoutesService();

// Bridge: Create a simple stable transfer between two chains
const intent = routesService.createSimpleIntent({
  creator: address,
  originChainID,
  spendingToken,
  spendingTokenLimit,
  destinationChainID,
  receivingToken,
  amount,
  recipient: address, // optional, defaults to the creator if not passed
});

Step 3: Request and Apply a Quote

To request quotes for an intent and select the cheapest quote, use the OpenQuotingClient and selectCheapestQuote functions. Then, you can apply the quote by calling applyQuoteToIntent on the RoutesService instance:
import { OpenQuotingClient, selectCheapestQuote } from '@eco-foundation/routes-sdk';

const openQuotingClient = new OpenQuotingClient({ dAppID: 'my-dapp' });

try {
  const quotes = await openQuotingClient.requestQuotesForIntent(intent);

  // select quote
  const selectedQuote = selectCheapestQuote(quotes);

  // apply quote to intent
  const intentWithQuote = routesService.applyQuoteToIntent({ intent, quote: selectedQuote });
}
catch (error) {
  console.error('Quotes not available', error);
}
If you do not request a quote for your intent and you continue with publishing it, you risk the possibility of your intent not being fulfilled by any solvers (because of an insufficient token limit), or losing any surplus amount from your spendingTokenLimit that the solver didn’t need to fulfill your intent. This is why requesting a quote is strongly recommended.

Step 4: Publishing the Intent

Once the intent is prepared, publish it to the origin chain using your preferred web3 library. Here’s an example leveraging viem.
import { createWalletClient, privateKeyToAccount, webSocket, http, erc20Abi } from 'viem';
import { optimism } from 'viem/chains';
import { IntentSourceAbi } from '@eco-foundation/routes-ts';

const account = privateKeyToAccount('YOUR PRIVATE KEY HERE');
const originChain = optimism;

const rpcUrl = 'YOUR RPC URL';
const originWalletClient = createWalletClient({
  account,
  transport: webSocket(rpcUrl) // OR http(rpcUrl)
});
const originPublicClient = createPublicClient({
  chain: originChain,
  transport: webSocket(rpcUrl) // OR http(rpcUrl)
});

const intentSourceContract = routesService.getProtocolContractAddress(originChain.id, 'IntentSource');

try {
  // approve the quoted amount to account for fees
  await Promise.all(intentWithQuote.reward.tokens.map(async ({ token, amount }) => {
    const approveTxHash = await originWalletClient.writeContract({
      abi: erc20Abi,
      address: token,
      functionName: 'approve',
      args: [intentSourceContract, amount],
      chain: originChain,
      account
    });

    await originPublicClient.waitForTransactionReceipt({ hash: approveTxHash });
  }));

  const publishTxHash = await originWalletClient.writeContract({
    abi: IntentSourceAbi,
    address: intentSourceContract,
    functionName: 'publishAndFund',
    args: [intentWithQuote, false],
    chain: originChain,
    account
  });

  const publishReceipt = await originPublicClient.waitForTransactionReceipt({ hash: publishTxHash });
  
  const logs = parseEventLogs({
    abi: IntentSourceAbi,
    logs: publishReceipt.logs
  })
  const intentCreatedEvent = logs.find((log) => log.eventName === 'IntentCreated')

  if (!intentCreatedEvent) {
    throw new Error('IntentCreated event not found in logs')
  }

  const intentHash = intentCreatedEvent.args.hash;
}
catch (error) {
  console.error('Intent creation failed', error);
}

Step 5: Tracking the Intent fulfillment.

Track the intent by watching for the fulfillment event on the destination chain.
import { createPublicClient, webSocket, Hex } from 'viem';
import { base } from 'viem/chains';
import { InboxAbi } from '@eco-foundation/routes-ts';

const inboxContract = routesService.getProtocolContractAddress(originChain.id, 'Inbox');

const rpcUrl = 'YOUR RPC URL';
const destinationPublicClient = createPublicClient({
  chain: base,
  transport: webSocket(rpcUrl)
});

try {
  // get block to start watching from
  const latestBlock = await destinationPublicClient.getBlockNumber();

  const fulfillmentTxHash = await new Promise<Hex>((resolve, reject) => {
    const unwatch = destinationPublicClient.watchContractEvent({
      fromBlock: latestBlock - BigInt(25),
      abi: InboxAbi,
      address: inboxContract,
      eventName: 'Fulfillment',
      args: {
        _hash: intentHash
      }
      onLogs(logs) {
        if (logs.length > 0) {
          const txHash = logs[0]!.transactionHash;
          unwatch();
          resolve(txHash);
        }
      },
      onError(error) {
        unwatch();
        reject(error);
      }
    });
  });

  console.log('Fulfillment transaction hash:', fulfillmentTxHash);
} catch (error) {
  console.error('Error tracking intent fulfillment:', error);
}
Congrats! You just finished your first stablesend using Eco.

Keeping dependencies updated

Run the following command to ensure you’re using the latest versions:
npm install @eco-foundation/routes-sdk@latest @eco-foundation/routes-ts@latest