Examples & Use Cases

Full Flow Example

End-to-end example: initialize the SDK, find a route, check balance, and execute the transfer.
import { ViemSigner } from "@stable-io/sdk";
import { privateKeyToAccount } from "viem/accounts";
import StableSDK from "@stable-io/sdk";
import dotenv from "dotenv";

dotenv.config();
const privateKey = process.env.EVM_PRIVATE_KEY!;
const account = privateKeyToAccount(privateKey);
const sender = account.address;
const recipient = account.address;

const sdk = new StableSDK({
  network: "Testnet",
  signer: new ViemSigner(account),
  rpcUrls: {
    Ethereum: "https://ethereum-sepolia.rpc.subquery.network/public",
  },
});

const intent = {
  sourceChain: "Ethereum",
  targetChain: "Arbitrum",
  amount: "0.01",
  sender,
  recipient,
  gasDropoffDesired: 0n,
  paymentToken: "usdc"
};

const routes = await sdk.findRoutes(intent);
const route = routes.all[0];

route.progress.on("*", (event) => {
  console.log(`[Progress] ${event.name}:`, event.data);
});

route.transactionListener.on("*", (event) => {
  console.log(`[TxListener] ${event.name}:`, event.data);
});

if (await sdk.checkHasEnoughFunds(route)) {
  const {
    transactions,
    attestations,
    redeems,
    transferHash,
    redeemHash,
  } = await sdk.executeRoute(route);
  console.log("Executed route:");
  console.log("Transfer TX:", transferHash);
  console.log("Redeem TX:", redeemHash);
  console.log("Transactions:", transactions);
  console.log("Attestations:", attestations);
  console.log("Redeems:", redeems);
} else {
  console.log("Not enough funds to execute the route.");
}

Cross Chain Payouts

Send USDC payouts to multiple recipients across different chains from a single treasury wallet, with full tracking and confirmation.
import { ViemSigner } from "@stable-io/sdk";
import { privateKeyToAccount } from "viem/accounts";
import StableSDK from "@stable-io/sdk";
import dotenv from "dotenv";

dotenv.config();
const privateKey = process.env.EVM_PRIVATE_KEY!;
const account = privateKeyToAccount(privateKey);
const sender = account.address;

const sdk = new StableSDK({
  network: "Testnet",
  signer: new ViemSigner(account),
  rpcUrls: {
    Ethereum: "https://ethereum-sepolia.rpc.subquery.network/public",
    Arbitrum: "https://arbitrum-sepolia.blockpi.network/v1/rpc/public",
    Optimism: "https://optimism-sepolia.blockpi.network/v1/rpc/public",
  },
});

const payouts = [
  { recipient: "0xAlice", chain: "Arbitrum", amount: "10" },
  { recipient: "0xBob", chain: "Optimism", amount: "25" },
  { recipient: "0xCarol", chain: "Ethereum", amount: "15" },
];

for (const payout of payouts) {
  const intent = {
    sourceChain: "Ethereum",
    targetChain: payout.chain,
    amount: payout.amount,
    sender,
    recipient: payout.recipient,
    gasDropoffDesired: 0n,
  };

  const routes = await sdk.findRoutes(intent);
  const route = routes.all[0];

  // Listen for transaction lifecycle events
  route.transactionListener.on("transaction-included", (event) => {
    console.log("Payment transaction included in block:", event.data);
    // Store payment record or trigger next step in workflow
  });

  // Listen for final transfer confirmation
  route.progress.on("transfer-redeemed", (event) => {
    console.log("Transfer complete. USDC received by:", payout.recipient);
    // Mark payout as completed in your system
  });

  if (await sdk.checkHasEnoughFunds(route)) {
    await sdk.executeRoute(route);
  } else {
    console.warn(`Insufficient funds for ${payout.recipient} on ${payout.chain}`);
  }
}

Treasury Rebalancing

Dynamically distribute USDC across chains based on live liquidity needs or target vault balances.
import { ViemSigner } from "@stable-io/sdk";
import { privateKeyToAccount } from "viem/accounts";
import StableSDK from "@stable-io/sdk";
import dotenv from "dotenv";

dotenv.config();
const privateKey = process.env.EVM_PRIVATE_KEY!;
const account = privateKeyToAccount(privateKey);
const sender = account.address;

const sdk = new StableSDK({
  network: "Testnet",
  signer: new ViemSigner(account)
});

// Simulated current vault balances
const TARGET_BALANCE = 50000;
const currentBalances = {
  Base: 30000,
  Arbitrum: 42000,
  Optimism: 48000,
};

const chains = [
  { chain: "Base", recipient: "0xBaseVault" },
  { chain: "Arbitrum", recipient: "0xArbitrumVault" },
  { chain: "Optimism", recipient: "0xOptimismVault" },
];

// Calculate rebalancing needs
const neededTransfers = chains
  .map(({ chain, recipient }) => {
    const shortfall = TARGET_BALANCE - (currentBalances[chain] || 0);
    return shortfall > 0 ? { chain, recipient, amount: shortfall } : null;
  })
  .filter(Boolean);

// Execute transfers using Stable SDK
for (const transfer of neededTransfers) {
  const intent = {
    sourceChain: "Ethereum",
    targetChain: transfer.chain,
    amount: transfer.amount.toFixed(2),
    sender,
    recipient: transfer.recipient,
    gasDropoffDesired: 0n,
  };

  const routes = await sdk.findRoutes(intent);
  const route = routes.all[0];

  route.transactionListener.on("transaction-included", (event) => {
    console.log(`[${transfer.chain}] Tx included:`, event.data);
  });

  route.progress.on("transfer-redeemed", () => {
    console.log(`[${transfer.chain}] Rebalance complete.`);
  });

  if (await sdk.checkHasEnoughFunds(route)) {
    await sdk.executeRoute(route);
  } else {
    console.warn(`Not enough funds for ${transfer.chain}`);
  }
}

USDC Consolidation

Merge USDC balances from multiple source chains into a single destination chain to streamline treasury management.
import { ViemSigner } from "@stable-io/sdk";
import { privateKeyToAccount } from "viem/accounts";
import StableSDK from "@stable-io/sdk";
import dotenv from "dotenv";

dotenv.config();
const privateKey = process.env.EVM_PRIVATE_KEY!;
const account = privateKeyToAccount(privateKey);
const sender = account.address;
const recipient = account.address;

const sdk = new StableSDK({
  network: "Testnet",
  signer: new ViemSigner(account)
});

// Simulated USDC balances from different chains
const chainsToConsolidate = [
  {
    chain: "Polygon",
    balance: 11,
    chainId: "0x13881",
  },
  {
    chain: "Optimism",
    balance: 8,
    chainId: "0x45",
  },
  {
    chain: "Arbitrum",
    balance: 14,
    chainId: "0x66eee",
  },
];

const targetChain = "Ethereum";

// Execute consolidation from each chain into the target
for (const { chain, balance, chainId } of chainsToConsolidate) {
  if (balance <= 0) {
    console.log(`[${chain}] No USDC to consolidate.`);
    continue;
  }

  const intent = {
    sourceChain: chain,
    targetChain,
    amount: balance.toFixed(2),
    sender,
    recipient,
    gasDropoffDesired: 0n,
  };

  // If using this in a browser (e.g. MetaMask), prompt the user to switch to the source chain:
  // await window.ethereum.request({
  //   method: "wallet_switchEthereumChain",
  //   params: [{ chainId }],
  // });

  const routes = await sdk.findRoutes(intent);
  const route = routes.all[0];

  route.transactionListener.on("transaction-included", (event) => {
    console.log(`[${chain}] Tx included:`, event.data);
  });

  route.progress.on("transfer-redeemed", () => {
    console.log(`[${chain}] Consolidation to ${targetChain} complete.`);
  });

  if (await sdk.checkHasEnoughFunds(route)) {
    await sdk.executeRoute(route);
  } else {
    console.warn(`[${chain}] Not enough funds to consolidate.`);
  }
}

Last updated

Was this helpful?