mcpField
S
starknet-wallet

starknet-wallet

github.com/keep-starknet-strange其他8138 次安装3 周前更新
代码仓库

关于 starknet-wallet

面向 AI Agent 的 Starknet 钱包管理技能,涵盖账户抽象、余额查询、转账和合约交互。它通过 Starknet MCP Server 提供的钱包工具支持日常链上操作及会话密钥工作流。

主要功能

  1. 01查询单个或多个代币余额
  2. 02发送代币并支持 Paymaster 免 Gas
  3. 03读取及调用合约
  4. 04通过 AVNU 获取兑换报价并执行兑换
  5. 05使用会话密钥执行预授权操作

适用场景

  1. 01检查 Agent 钱包的代币余额
  2. 02从 Agent 钱包发起代币转账
  3. 03让 Agent 在授权策略下自主操作
  4. 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.example
package.json
agents/
openai.yaml
scripts/
check-balance.ts
check-balances.ts
README.md
rpc-spec-version.ts

详细介绍

Starknet Wallet Skill

为 AI Agent 管理 Starknet 钱包,并原生支持 Account Abstraction。

适用场景

  • 为 Agent 工作流创建、注资和操作 Starknet 账户。
  • 从 Agent 钱包查询余额、发送转账、管理会话密钥或调用合约。

不适用场景

  • 更适合由 starknet-defi 处理的 DeFi 专项路由、质押或借贷流程。
  • Cairo 合约编写、仅限部署的操作或安全审计。

快速开始

  1. 安装 SDK 依赖项并设置 Starknet RPC/账户环境变量。
  2. 使用技能目录切换到相邻模块,例如 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 执行预先批准的交易,无需对每项操作逐一征求人工批准:

  1. 所有者创建会话密钥并设置策略:
    • 允许的合约地址和方法
    • 每笔交易/每个时段的最高支出
    • 过期时间戳
  2. Agent 使用会话密钥自主执行操作
  3. 所有者可随时撤销

参考实现:Cartridge Controller

配置

变量用途默认值
STARKNET_RPC_URLStarknet JSON-RPC 端点必需
STARKNET_ACCOUNT_ADDRESSAgent 的账户地址必需
STARKNET_PRIVATE_KEYAgent 的签名密钥必需
AVNU_BASE_URLavnu API 基础 URLhttps://starknet.api.avnu.fi
AVNU_PAYMASTER_URLavnu 代付服务 URLhttps://starknet.paymaster.avnu.fi

错误处理

错误原因解决方法
INSUFFICIENT_BALANCE代币余额不足转账前检查余额
INVALID_NONCENonce 不匹配使用新的 Nonce 重试
TRANSACTION_REVERTED合约执行失败检查 calldata 和授权额度
FEE_TRANSFER_FAILURE无法支付 Gas 费用使用代付服务或添加 ETH/STRK

参考资料

评论

支持 Markdown