Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Execute DeFi operations on Starknet including token swaps via avnu aggregator, DCA recurring buys, STRK staking, and lending/borrowing. Supports gasless transactions.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 91% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 51% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 34% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-05 | ✗→✓ | ▲ Improved | -5% | 0% |
Execute DeFi operations on Starknet using avnu aggregator and native protocols.
bashnpm install starknet@^8.9.1 @avnu/avnu-sdk@^4.0.1
typescriptimport { getQuotes, executeSwap, type QuoteRequest } from "@avnu/avnu-sdk"; import { Account, RpcProvider, ETransactionVersion } from "starknet"; const provider = new RpcProvider({ nodeUrl: process.env.STARKNET_RPC_URL }); // starknet.js v8: Account uses options object const account = new Account({ provider, address, signer: privateKey, transactionVersion: ETransactionVersion.V3, }); // Resolve token addresses via avnu SDK (or use MCP server's TokenService) import { fetchVerifiedTokenBySymbol } from '@avnu/avnu-sdk'; const eth = await fetchVerifiedTokenBySymbol('ETH'); const strk = await fetchVerifiedTokenBySymbol('STRK'); // SDK v4: getQuotes takes QuoteRequest object directly const quoteParams: QuoteRequest = { sellTokenAddress: eth.address, buyTokenAddress: strk.address, sellAmount: BigInt(10 ** 17), // 0.1 ETH takerAddress: account.address, }; const quotes = await getQuotes(quoteParams); const bestQuote = quotes[0]; // SDK v4: executeSwap takes single object param const result = await executeSwap({ provider: account, quote: bestQuote, slippage: 0.01, // 1% executeApprove: true, }); console.log("Tx:", result.transactionHash);
typescriptinterface Quote { quoteId: string; sellTokenAddress: string; buyTokenAddress: string; sellAmount: bigint; buyAmount: bigint; sellAmountInUsd: number; buyAmountInUsd: number; priceImpact: number; // In basis points (15 = 0.15%) gasFeesInUsd: number; routes: Array<{ name: string; // e.g., "Ekubo", "JediSwap" percent: number; // e.g., 0.8 = 80% }>; fee: { avnuFees: bigint; integratorFees: bigint; }; }
typescriptimport { quoteToCalls } from "@avnu/avnu-sdk"; const calls = await quoteToCalls({ quote: bestQuote, takerAddress: account.address, slippage: 0.01, includeApprove: true, }); // `calls` can be combined with other calls in account.execute([...calls, ...otherCalls])
typescriptimport { getQuotes, executeSwap } from "@avnu/avnu-sdk"; import { PaymasterRpc } from "starknet"; const quotes = await getQuotes(quoteParams); const bestQuote = quotes[0]; // SDK v4: Use PaymasterRpc from starknet.js // Mainnet: https://starknet.paymaster.avnu.fi // Sepolia: https://sepolia.paymaster.avnu.fi const paymaster = new PaymasterRpc({ nodeUrl: process.env.AVNU_PAYMASTER_URL || "https://starknet.paymaster.avnu.fi", }); const result = await executeSwap({ provider: account, quote: bestQuote, slippage: 0.01, executeApprove: true, paymaster: { active: true, provider: paymaster, params: { feeMode: { mode: "default", gasToken: "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8", // USDC }, }, }, });
typescriptimport { executeCreateDca } from "@avnu/avnu-sdk"; import moment from "moment"; const dcaOrder = { sellTokenAddress: usdcAddress, buyTokenAddress: strkAddress, totalAmount: parseUnits("100", 6), // Total 100 USDC numberOfOrders: 10, // Split into 10 orders frequency: moment.duration(1, "day"), // moment.Duration object, not string startAt: Math.floor(Date.now() / 1000), }; const result = await executeCreateDca({ provider: account, order: dcaOrder, });
typescriptimport { getDcaOrders, executeCancelDca, DcaOrderStatus } from "@avnu/avnu-sdk"; const orders = await getDcaOrders({ traderAddress: account.address, status: DcaOrderStatus.OPEN, // Use enum, not string }); // Cancel an order await executeCancelDca({ provider: account, orderAddress: orders[0].orderAddress, });
typescriptimport { executeStake, getAvnuStakingInfo } from "@avnu/avnu-sdk"; // Get pool info const stakingInfo = await getAvnuStakingInfo(); // stakingInfo.pools[0] = { address, apy, tvl, token, minStake } const result = await executeStake({ provider: account, poolAddress: stakingInfo.pools[0].address, amount: parseUnits("100", 18), // 100 STRK });
typescriptimport { getUserStakingInfo } from "@avnu/avnu-sdk"; const userInfo = await getUserStakingInfo(TOKENS.STRK, account.address); console.log("Staked:", userInfo.amount); console.log("Unclaimed rewards:", userInfo.unclaimedRewards);
typescriptimport { executeClaimRewards } from "@avnu/avnu-sdk"; // Claim and restake (compound) await executeClaimRewards({ provider: account, poolAddress: poolAddress, restake: true, });
typescriptimport { executeInitiateUnstake, executeUnstake } from "@avnu/avnu-sdk"; // Step 1: Initiate (starts cooldown -- 21 days for STRK) await executeInitiateUnstake({ provider: account, poolAddress: poolAddress, amount: parseUnits("50", 18), }); // Step 2: Complete unstake (after cooldown period) await executeUnstake({ provider: account, poolAddress: poolAddress, });
typescriptimport { getPrices, fetchTokens, fetchVerifiedTokenBySymbol } from "@avnu/avnu-sdk"; // Get token by symbol const strk = await fetchVerifiedTokenBySymbol("STRK"); // Get prices for multiple tokens const prices = await getPrices([ethAddress, strkAddress, usdcAddress]); // prices = { "0x049d...": 3200.50, "0x047...": 1.23, ... } // Browse tokens with pagination const tokens = await fetchTokens({ page: 0, size: 20, tags: ["verified"] });
| Protocol | Operations | Notes | |----------|-----------|-------| | avnu | Swap aggregation, DCA, gasless | Best-price routing across all DEXs | | Ekubo | AMM, concentrated liquidity | Highest TVL on Starknet | | JediSwap | AMM, classic pools | V2 with concentrated liquidity | | zkLend | Lending, borrowing | Variable and stable rates | | Nostra | Lending, borrowing | Multi-asset pools |
| Variable | Purpose | Default | |----------|---------|---------| | STARKNET_RPC_URL | Starknet JSON-RPC endpoint | Required | | STARKNET_ACCOUNT_ADDRESS | Agent's account address | Required | | STARKNET_PRIVATE_KEY | Agent's signing key | Required | | AVNU_BASE_URL | avnu API base URL | https://starknet.api.avnu.fi | | AVNU_PAYMASTER_URL | avnu paymaster URL | https://starknet.paymaster.avnu.fi | | AVNU_API_KEY | Optional avnu integrator key | None |
| Network | API URL | Paymaster URL | |---------|---------|---------------| | Mainnet | https://starknet.api.avnu.fi | https://starknet.paymaster.avnu.fi | | Sepolia | https://sepolia.api.avnu.fi | https://sepolia.paymaster.avnu.fi |
typescriptasync function safeSwap(account, quote, slippage = 0.01) { try { return await executeSwap({ provider: account, quote, slippage, executeApprove: true, }); } catch (error) { if (error.message?.includes("INSUFFICIENT_BALANCE")) { throw new Error("Not enough tokens for swap"); } if (error.message?.includes("SLIPPAGE") || error.message?.includes("Insufficient tokens received")) { // Retry with higher slippage return await executeSwap({ provider: account, quote, slippage: slippage * 2, executeApprove: true, }); } if (error.message?.includes("QUOTE_EXPIRED")) { throw new Error("Quote expired. Please retry the operation."); } if (error.message?.includes("INSUFFICIENT_LIQUIDITY")) { throw new Error("Insufficient liquidity. Try a smaller amount."); } throw error; } }
Other measured skills in the registry, with their headline benchmark lift.