starknet-defi
0
总安装量
2
周安装量
安装命令
npx skills add https://github.com/keep-starknet-strange/starknet-agentic --skill starknet-defi
Agent 安装分布
amp
2
opencode
2
kimi-cli
2
github-copilot
2
claude-code
2
Skill 文档
Starknet DeFi Skill
Execute DeFi operations on Starknet using avnu aggregator and native protocols.
Prerequisites
npm install starknet@^8.9.1 @avnu/avnu-sdk@^4.0.1
Token Swaps (avnu SDK v4)
Get Quote and Execute Swap
import { 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);
Quote Response Fields (SDK v4)
interface 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;
};
}
Build Swap Calls (for multicall composition)
import { 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])
Gasless Swap (Pay Gas in Token) – SDK v4 + PaymasterRpc
import { 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
},
},
},
});
DCA (Dollar Cost Averaging)
Create DCA Order
import { 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,
});
Check and Cancel DCA
import { 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,
});
STRK Staking
Stake STRK
import { 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
});
Get User Staking Info
import { getUserStakingInfo } from "@avnu/avnu-sdk";
const userInfo = await getUserStakingInfo(TOKENS.STRK, account.address);
console.log("Staked:", userInfo.amount);
console.log("Unclaimed rewards:", userInfo.unclaimedRewards);
Claim Rewards
import { executeClaimRewards } from "@avnu/avnu-sdk";
// Claim and restake (compound)
await executeClaimRewards({
provider: account,
poolAddress: poolAddress,
restake: true,
});
Unstake
import { 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,
});
Market Data
Token Prices
import { 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 Reference
| 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 |
Configuration
| 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 |
avnu URL Reference
| 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 |
Error Handling
async 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;
}
}