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?