关于 starknet-wallet
面向 AI Agent 的 Starknet 钱包管理技能,涵盖账户抽象、余额查询、转账和合约交互。它通过 Starknet MCP Server 提供的钱包工具支持日常链上操作及会话密钥工作流。
主要功能
- 01查询单个或多个代币余额
- 02发送代币并支持 Paymaster 免 Gas
- 03读取及调用合约
- 04通过 AVNU 获取兑换报价并执行兑换
- 05使用会话密钥执行预授权操作
适用场景
- 01检查 Agent 钱包的代币余额
- 02从 Agent 钱包发起代币转账
- 03让 Agent 在授权策略下自主操作
- 04查询兑换报价并执行代币兑换
MCP 相关
Starknet MCP Server 向 MCP 客户端提供余额、转账、合约调用、兑换及 Agent 身份注册工具。该技能也展示通过 MCP 客户端调用这些工具的方式。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ keep-starknet-strange/ starknet-agentic --skill starknet-wallet安装后在 Claude Code 中输入/starknet-wallet确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.env.examplepackage.jsonagents/openai.yamlscripts/check-balance.tscheck-balances.tsREADME.mdrpc-spec-version.ts详细介绍
Starknet Wallet Skill
为 AI Agent 管理 Starknet 钱包,并原生支持 Account Abstraction。
适用场景
- 为 Agent 工作流创建、注资和操作 Starknet 账户。
- 从 Agent 钱包查询余额、发送转账、管理会话密钥或调用合约。
不适用场景
- 更适合由
starknet-defi处理的 DeFi 专项路由、质押或借贷流程。 - Cairo 合约编写、仅限部署的操作或安全审计。
快速开始
- 安装 SDK 依赖项并设置 Starknet RPC/账户环境变量。
- 使用技能目录切换到相邻模块,例如 DeFi、部署或审计。
前置条件
npm install starknet@^10.8.0 @avnu/avnu-sdk@^4.2.0
环境变量:
STARKNET_RPC_URL=https://starknet-mainnet.g.alchemy.com/v2/YOUR_KEY
STARKNET_ACCOUNT_ADDRESS=0x...
STARKNET_PRIVATE_KEY=0x...
AVNU_BASE_URL=https://starknet.api.avnu.fi (optional)
AVNU_PAYMASTER_URL=https://starknet.paymaster.avnu.fi (optional)
AVNU_PAYMASTER_API_KEY=your_key (optional, for free gas)
可用 MCP 工具
Starknet MCP Server 提供以下钱包操作工具:
| 工具 | 用途 | 主要功能 |
|---|---|---|
starknet_get_balance | 查询单个代币余额 | 查询简单、快速 |
starknet_get_balances | 查询多个代币余额 | 批量查询(最多 200 种代币),单次 RPC 调用 |
starknet_transfer | 发送代币 | 支持免 Gas 模式(paymaster) |
starknet_call_contract | 读取合约状态 | 调用 view 函数 |
starknet_invoke_contract | 执行合约函数 | 写入操作,支持免 Gas |
starknet_swap | 执行代币兑换 | 集成 AVNU,提供最优价格路由 |
starknet_get_quote | 获取兑换报价 | 兑换前估算价格 |
starknet_register_agent | 注册 Agent 身份 | ERC-8004 链上身份 |
余额工具详情
starknet_get_balance(单个代币)
查询一种代币的余额。适用于简单场景。
输入:
token(必填):代币符号(ETH、STRK、USDC、USDT)或合约地址address(可选):钱包地址(默认为 Agent 的地址)
响应:
{
"address": "0x...",
"token": "ETH",
"tokenAddress": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
"balance": "1.5",
"raw": "1500000000000000000",
"decimals": 18
}
starknet_get_balances(多个代币)
通过单次 RPC 调用查询多种代币的余额。适合查看投资组合,效率更高。
输入:
tokens(必填):代币符号或地址数组(最多 200 个)address(可选):钱包地址
响应:
{
"address": "0x...",
"balances": [
{ "token": "ETH", "tokenAddress": "0x...", "balance": "1.5", "raw": "...", "decimals": 18 },
{ "token": "USDC", "tokenAddress": "0x...", "balance": "100", "raw": "...", "decimals": 6 }
],
"tokensQueried": 2,
"method": "balance_checker"
}
使用场景:
- starknet_get_balance:快速查询单个代币
- starknet_get_balances:查看投资组合概况、执行多代币操作
核心操作
查询余额(单个代币)
简单地查询单个代币时,使用 starknet_get_balance MCP 工具:
// Via MCP tool (recommended)
const result = await mcpClient.callTool({
name: "starknet_get_balance",
arguments: {
address: "0x...", // Account address
token: "ETH", // Symbol (ETH, STRK, USDC, USDT) or contract address
}
});
// Returns: { address, token, tokenAddress, balance, raw, decimals }
直接使用 starknet.js:
import { RpcProvider, Contract } from "starknet";
const provider = new RpcProvider({ nodeUrl: process.env.STARKNET_RPC_URL });
// ETH balance (starknet.js v10 uses an options object for Contract)
const ethAddress = "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";
const ethContract = new Contract({
abi: erc20Abi,
address: ethAddress,
providerOrAccount: provider,
});
// starknet.js parses a Cairo 1 u256 return value to bigint (no low/high struct)
const balance: bigint = await ethContract.balanceOf(accountAddress);
// Format: (balance / 10n ** 18n).toString() for whole units
查询多个余额(批量)
使用 starknet_get_balances 高效查询多种代币(单次 RPC 调用):
// Via MCP tool (recommended for multiple tokens)
const result = await mcpClient.callTool({
name: "starknet_get_balances",
arguments: {
address: "0x...",
tokens: ["ETH", "STRK", "USDC", "USDT"], // Up to 200 tokens
}
});
// Returns:
// {
// address: "0x...",
// balances: [
// { token: "ETH", tokenAddress: "0x...", balance: "1.5", raw: "1500000000000000000", decimals: 18 },
// { token: "STRK", tokenAddress: "0x...", balance: "100", raw: "100000000000000000000", decimals: 18 },
// ...
// ],
// tokensQueried: 4,
// method: "balance_checker" // Uses BalanceChecker contract for efficiency
// }
直接使用 starknet.js 和 BalanceChecker 合约:
import { RpcProvider, Contract } from "starknet";
const provider = new RpcProvider({ nodeUrl: process.env.STARKNET_RPC_URL });
// BalanceChecker contract (returns non-zero balances only)
const BALANCE_CHECKER_ADDRESS = "0x031ce64a666fbf9a2b1b2ca51c2af60d9a76d3b85e5fbfb9d5a8dbd3fedc9716";
const BALANCE_CHECKER_ABI = [
{
type: "struct",
name: "core::integer::u256",
members: [
{ name: "low", type: "core::integer::u128" },
{ name: "high", type: "core::integer::u128" },
],
},
{
type: "struct",
name: "governance::balance_checker::NonZeroBalance",
members: [
{ name: "token", type: "core::starknet::contract_address::ContractAddress" },
{ name: "balance", type: "core::integer::u256" },
],
},
{
type: "function",
name: "get_balances",
inputs: [
{ name: "address", type: "core::starknet::contract_address::ContractAddress" },
{ name: "tokens", type: "core::array::Span::<core::starknet::contract_address::ContractAddress>" },
],
outputs: [{ type: "core::array::Span::<governance::balance_checker::NonZeroBalance>" }],
state_mutability: "view",
},
];
const balanceChecker = new Contract({
abi: BALANCE_CHECKER_ABI,
address: BALANCE_CHECKER_ADDRESS,
providerOrAccount: provider,
});
// Query multiple tokens at once
const tokens = [
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", // ETH
"0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", // STRK
"0x033068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb", // USDC
"0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8", // USDC.e (legacy bridged USDC)
];
const result = await balanceChecker.get_balances(accountAddress, tokens);
// Parse response (only non-zero balances returned)
for (const item of result) {
const tokenAddr = "0x" + BigInt(item.token).toString(16).padStart(64, "0");
const balance = BigInt(item.balance); // starknet.js converts u256 to bigint
console.log(`${tokenAddr}: ${balance}`);
}
如何选择:
starknet_get_balance:单个代币,简单场景starknet_get_balances:多个代币、查看投资组合,效率更高
转账代币
使用 starknet_transfer MCP 工具,并可选择免 Gas 模式:
// Via MCP tool (recommended)
const result = await mcpClient.callTool({
name: "starknet_transfer",
arguments: {
recipient: "0x...",
token: "STRK", // Symbol or contract address
amount: "10.5", // Human-readable amount
gasfree: false, // Optional: use paymaster
}
});
// Returns: { transactionHash, recipient, token, amount, gasfree }
免 Gas 转账(使用代币而非 ETH/STRK 支付 Gas):
// Pay gas in USDC instead of ETH/STRK
const result = await mcpClient.callTool({
name: "starknet_transfer",
arguments: {
recipient: "0x...",
token: "STRK",
amount: "100",
gasfree: true,
gasToken: "USDC", // Gas paid in USDC
}
});
直接使用 starknet.js:
import { Account, RpcProvider, CallData, cairo, ETransactionVersion } from "starknet";
const provider = new RpcProvider({ nodeUrl: process.env.STARKNET_RPC_URL });
const address = process.env.STARKNET_ACCOUNT_ADDRESS;
const privateKey = process.env.STARKNET_PRIVATE_KEY;
if (!address || !privateKey) throw new Error("Set STARKNET_ACCOUNT_ADDRESS and STARKNET_PRIVATE_KEY");
// starknet.js v10: Account uses an options object; provider calls go through account.provider
const account = new Account({
provider,
address,
signer: privateKey,
transactionVersion: ETransactionVersion.V3,
});
const tokenAddress = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"; // STRK
// Use cairo.uint256() instead of uint256.bnToUint256()
const { transaction_hash } = await account.execute({
contractAddress: tokenAddress,
entrypoint: "transfer",
calldata: CallData.compile({
recipient: recipientAddress,
amount: cairo.uint256(amountInWei),
}),
});
await account.provider.waitForTransaction(transaction_hash);
估算费用
const estimatedFee = await account.estimateInvokeFee({
contractAddress: tokenAddress,
entrypoint: "transfer",
calldata: CallData.compile({
recipient: recipientAddress,
amount: cairo.uint256(amountInWei),
}),
});
// estimatedFee.overall_fee -- total fee in STRK (V3 transactions)
合约交互
读取合约状态(view 函数):
// Via MCP tool
const result = await mcpClient.callTool({
name: "starknet_call_contract",
arguments: {
contractAddress: "0x...",
entrypoint: "balanceOf",
calldata: [accountAddress],
}
});
写入合约(更改状态的函数):
// Via MCP tool with gasless option
const result = await mcpClient.callTool({
name: "starknet_invoke_contract",
arguments: {
contractAddress: "0x...",
entrypoint: "approve",
calldata: [spenderAddress, ...uint256Amount],
gasfree: true, // Optional: use paymaster
gasToken: "USDC", // Optional: pay gas in token
}
});
多调用(批量交易)
// Execute multiple operations in a single transaction
const { transaction_hash } = await account.execute([
{
contractAddress: tokenA,
entrypoint: "approve",
calldata: CallData.compile({
spender: routerAddress,
amount: cairo.uint256(amount),
}),
},
{
contractAddress: routerAddress,
entrypoint: "swap",
calldata: CallData.compile({ /* swap params */ }),
},
]);
免 Gas 转账(使用代币支付 Gas)- SDK v4 + PaymasterRpc
import { getQuotes, executeSwap } from "@avnu/avnu-sdk";
import { PaymasterRpc } from "starknet";
// 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",
});
// Any swap can be made gasless by adding paymaster option
const result = await executeSwap({
provider: account,
quote: bestQuote,
slippage: 0.01,
executeApprove: true,
paymaster: {
active: true,
provider: paymaster,
params: {
version: "0x1",
feeMode: {
mode: "default",
gasToken: usdcAddress, // Pay gas in USDC instead of ETH/STRK
},
},
},
});
代币解析
MCP 服务使用 TokenService 解析代币符号和地址。静态代币(ETH、STRK、USDC、USDT)始终可用。对于其他代币,该服务会从 avnu SDK 获取元数据。
import { fetchTokenByAddress, fetchTokens, fetchVerifiedTokenBySymbol } from '@avnu/avnu-sdk';
// Get token by symbol (undefined if not a verified/unruggable token)
const lords = await fetchVerifiedTokenBySymbol('LORDS');
if (!lords) throw new Error('LORDS not found in avnu token list');
console.log(lords.address, lords.decimals); // 0x0124aeb..., 18
// Get token by address (any token)
const token = await fetchTokenByAddress('0x...');
console.log(token.symbol, token.name, token.decimals);
// Get verified tokens (returns Page<Token>; tokens are in `content`)
const page = await fetchTokens({ tags: ['Verified'], size: 100 });
page.content.forEach(t => console.log(t.symbol, t.address));
无需网络调用即可使用的静态代币:ETH、STRK、USDC、USDT
会话密钥(Agent 自主执行)
会话密钥允许 Agent 执行预先批准的交易,无需对每项操作逐一征求人工批准:
- 所有者创建会话密钥并设置策略:
- 允许的合约地址和方法
- 每笔交易/每个时段的最高支出
- 过期时间戳
- Agent 使用会话密钥自主执行操作
- 所有者可随时撤销
参考实现:Cartridge Controller
配置
| 变量 | 用途 | 默认值 |
|---|---|---|
STARKNET_RPC_URL | Starknet JSON-RPC 端点 | 必需 |
STARKNET_ACCOUNT_ADDRESS | Agent 的账户地址 | 必需 |
STARKNET_PRIVATE_KEY | Agent 的签名密钥 | 必需 |
AVNU_BASE_URL | avnu API 基础 URL | https://starknet.api.avnu.fi |
AVNU_PAYMASTER_URL | avnu 代付服务 URL | https://starknet.paymaster.avnu.fi |
错误处理
| 错误 | 原因 | 解决方法 |
|---|---|---|
INSUFFICIENT_BALANCE | 代币余额不足 | 转账前检查余额 |
INVALID_NONCE | Nonce 不匹配 | 使用新的 Nonce 重试 |
TRANSACTION_REVERTED | 合约执行失败 | 检查 calldata 和授权额度 |
FEE_TRANSFER_FAILURE | 无法支付 Gas 费用 | 使用代付服务或添加 ETH/STRK |