关于 bnbagent
用于在 BNB Chain 构建链上 AI Agent 的 Python SDK,分别提供 ERC-8004 Agent 身份和 ERC-8183 托管任务能力,并支持多种钱包与存储选项。
主要功能
- 01注册 ERC-8004 Agent 身份
- 02创建和管理 ERC-8183 任务
- 03监控已托管任务并提交结果
- 04支持争议投票和退款
- 05提供 EVM 与 TWAK 钱包后端
适用场景
- 01在 BSC 注册 Agent 身份
- 02接单并交付已托管任务
- 03为服务提供方创建并资助任务
- 04对有争议的任务投票
MCP 相关
SDK 将服务接口(包括 MCP)留给实现者选择。示例包含 Agent 服务端,并将 MCP 列为一种可选接口。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ starchild-ai-agent/ official-skills --skill bnbagent安装后在 Claude Code 中输入/bnbagent确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/auto_settle.pyREADME.mdexamples/a2a-agent/.env.examplepyproject.tomlREADME.mdexamples/a2a-agent/scripts/buyer.pyregister.pyexamples/a2a-agent/src/server.pyexamples/agent-server/.env.examplepyproject.tomlREADME.mdexamples/agent-server/scripts/register.pyrun_agent_mount.pyrun_agent.pysettle.pyexamples/agent-server/src/erc8183_server.pyservice_mount.pyservice.pyexamples/client/_helpers.py.env.examplecancel_open.pycreate_and_verify.pydispute_reject.pygen_job_report.pyhappy.pynever_submit.pyREADME.mdstalemate_expire.pyexamples/security/e2e.pyexamples/twak/.env.example详细介绍
BNBAgent SDK
用于 BNB Chain 上链上 AI Agent 的 Python SDK(pip install bnbagent,Python 3.10+)。两项独立能力:
- ERC-8004(身份) — 将 Agent 作为 ERC-721 身份代币注册到链上,并带有可发现的 profile URI。在 BSC Testnet 上通过 MegaFuel paymaster 免 Gas。
- ERC-8183(Agent 商务) — 客户端(付款)与提供方(交付)之间的免信任任务托管。乐观结算:超过争议窗口后保持沉默 = 批准;客户端提出争议会触发白名单投票人法定人数的拒绝。
二者相互独立:你可以在没有 ERC-8004 注册的情况下运行 ERC-8183 任务(注册仅为便于发现而推荐)。
正在积极开发中 — 可能引入破坏性变更。已使用
bnbagent==0.4.0测试;用python -c "import bnbagent; print(bnbagent.__version__)"验证安装。可选附加项:pip install "bnbagent[ipfs]"用于 IPFS/Pinata 交付物存储。
⚠️ 主网经济模型 — 进行任何主网商务之前必读
- 支付代币是 U(United Stables)
0xcE24439F2D9C6a2289F741120FE202248B666666— 不是 BNB,不是 USDT/USDC。它没有水龙头:在 PancakeSwap 上获取 U(存在 WBNB–U 交易对)。客户端必须在调用fund()之前持有 U。在运行时通过erc8183.token_decimals()获取小数位数 — 不要假设。 - 主网上的 ERC-8183 写入永远不会获得 Gas 赞助。 客户端和提供方都需要 BNB 支付 Gas(仅 ERC-8004 身份注册在主网上获得赞助)。
- 主网争议窗口为 604800s(7 天)。 顺利路径无法在单次会话中达到
COMPLETED— 沉默批准仅在窗口结束后才生效。请规划部分 E2E(推进到SUBMITTED),之后通过 cron/运维脚本(examples/auto_settle.py)结算。在这一周内settle因policy pending而回滚是预期行为,不是错误。
预检余额清单(主网)
| 参与方 | 需要 | 原因 |
|---|---|---|
| 客户端 | BNB | createJob/registerJob/setBudget/approve/fund 的 Gas(+ 可选兑换 Gas) |
| 客户端 | U | fund 托管的任务预算(+ 少量余量用于重试) |
| 提供方 | BNB | submit 的 Gas — 在主网上不获赞助 |
| 结算者(任何人) | BNB | 窗口结束后 settle 的 Gas |
确定你要做什么
| 目标 | 使用 | 参考 |
|---|---|---|
| 在链上注册 Agent 身份 | ERC8004Agent | 下方快速开始 |
| 赚取:接受并交付已资助的任务 | ERC8183JobOps + funded_job_watcher | 下方快速开始,examples/agent-server/,examples/a2a-agent/ |
| 付款:创建/资助/结算任务 | ERC8183Client | 下方快速开始,examples/client/ |
| 对争议投票(白名单投票人) | PolicyClient.vote_reject | examples/voter/ |
| 支付 HTTP 402 挑战(x402) | X402Signer / twak 委托付款方 | examples/x402/,references/twak.md |
| 理解内部机制/扩展 | — | references/architecture.md |
| 钱包后端(EVM keystore 与 twak) | EVMWalletProvider / TWAKProvider | references/wallets.md,references/twak.md |
快速开始:注册 Agent(ERC-8004)
一次性设置。需要私钥(如省略则自动生成)和 WALLET_PASSWORD。
import os
from bnbagent import ERC8004Agent, AgentEndpoint, EVMWalletProvider
wallet = EVMWalletProvider(
password=os.getenv("WALLET_PASSWORD"),
private_key=os.getenv("PRIVATE_KEY"), # only needed on first run; keystore persists to ~/.bnbagent/wallets/
)
sdk = ERC8004Agent(network="bsc-testnet", wallet_provider=wallet)
agent_uri = sdk.generate_agent_uri(
name="my-ai-agent",
description="AI agent for document processing",
endpoints=[
AgentEndpoint.a2a("https://my-agent.example.com"), # A2A first (discovery doc URL)
AgentEndpoint.mcp("https://my-agent.example.com/mcp", version="2025-06-18"), # MCP second, if served
],
)
result = sdk.register_agent(agent_uri=agent_uri)
# result["agentId"], result["transactionHash"]
快速开始:提供方(赚取循环,无头模式)
无需服务器。监听已注资的任务,完成工作,提交:
import asyncio
from bnbagent import EVMWalletProvider
from bnbagent.erc8183 import ERC8183JobOps, funded_job_watcher
from bnbagent.storage import LocalStorageProvider
wallet = EVMWalletProvider(password="...", private_key="0x...")
ops = ERC8183JobOps(
wallet,
network="bsc-testnet",
storage_provider=LocalStorageProvider(),
service_price=1_000_000_000_000_000_000, # min acceptable budget, raw units (18 decimals here)
agent_url="http://localhost:8003/erc8183", # public URL; required for file:// deliverable rewriting
)
async def on_funded(job: dict) -> None:
deliverable = f"Processed: {job['description']}" # your business logic
await ops.submit_result(job["jobId"], deliverable)
asyncio.run(funded_job_watcher(ops, on_funded, interval=30))
submit_result负责处理验证(FUNDED 状态、分配、过期、预算 ≥ service_price)、可交付物上传、清单哈希以及submit交易。- 监听器自身从不提交或结算。结算是单独的步骤 —— 在争议窗口结束后,运行调用
ERC8183Client.settle(job_id)的操作员脚本(examples/auto_settle.py、examples/agent-server/scripts/settle.py)。 job字典字段:jobId、description、budget、client、provider、evaluator、status(始终为FUNDED)、expiredAt、hook。- 服务接口(A2A/MCP/HTTP)由你选择 —— 复制并自行掌控
examples/a2a-agent/(推荐)和examples/agent-server/(FastAPI)中的参考实现。 - 可交付物存储:像
https://example.invalid/manifest.json(如examples/client/happy.py中所示)这样的占位 URL 是仅上链的演示 —— 投票者无法验证可交付物。对于任何客户端可能提出争议的内容,请使用真实存储(公开ERC8183_AGENT_URL背后的LocalStorageProvider,或IPFSStorageProvider)。
快速开始:客户端(创建任务并付款)
import time
from bnbagent.erc8183 import ERC8183Client, JobStatus
from bnbagent.wallets import EVMWalletProvider
wallet = EVMWalletProvider(password="...", private_key="0x...")
erc8183 = ERC8183Client(wallet, network="bsc-testnet")
budget = 1 * (10 ** erc8183.token_decimals()) # DEMO-SCALE (1 full token). On MAINNET use tiny
# budgets, e.g. (10 ** dec) // 100 for 0.01 U.
expired_at = int(time.time()) + 65 * 60
job_id = erc8183.create_job(provider=provider_addr, expired_at=expired_at, description="task")["jobId"]
erc8183.register_job(job_id) # bind default policy (OptimisticPolicy)
erc8183.set_budget(job_id, budget)
erc8183.fund(job_id, budget) # escrows; auto-approves payment token with 100-token floor
# ... provider submits ... dispute window elapses ...
erc8183.settle(job_id) # permissionless — anyone can call
assert erc8183.get_job_status(job_id) == JobStatus.COMPLETED
争议与逃生通道:
erc8183.dispute(job_id) # client only, within dispute window after submit
erc8183.vote_reject(job_id) # whitelisted voters only, after dispute; quorum flips verdict to REJECT
erc8183.claim_refund(job_id) # anyone, after expiredAt if never settled — non-pausable escape hatch
fund(job_id, amount, approve_floor=None):默认授权 max(amount, 100 * 10**decimals),以避免跨任务流重复授权;approve_floor=0 则精确授权 amount;若授权额度已足够则不再发送授权。
任务生命周期
OPEN ──► FUNDED ──► SUBMITTED ──┬─ silence past window ──► COMPLETED (provider paid, minus platform fee)
│ │ ├─ dispute + quorum reject ──► REJECTED (client refunded)
│ │ └─ no verdict + past expiredAt ──► EXPIRED (client claimRefund)
│ └─ past expiredAt ──► EXPIRED (claimRefund)
└─ client reject() before funding ──► REJECTED
Gas 赞助矩阵
| 协议 / 写入 | BSC 测试网 | BSC 主网 |
|---|---|---|
ERC-8004 register_agent | ✅ 已赞助(MegaFuel) | ✅ 已赞助(MegaFuel)—— 可在零 BNB 钱包上使用 |
| ERC-8183 create/fund/submit/settle | 🟡 逐次调用:由 MegaFuel 决定(目前 fund/settle 已赞助);被拒绝的调用自行付费 | ❌ 从不赞助 —— 所有写入自行支付 BNB |
支付代币的 ERC-20 approve(额度不足时在 fund 内部发送) | ❌ 始终自行付费 —— 全新的测试网买家需要少量 tBNB | ❌ 自行付费 |
| twak 钱包操作 | ❌ twak 自行付费(twak 内部,SDK 无法控制) | ✅ twak 自动赞助 —— 参见 references/twak.md |
网络与合约
BSC 测试网(链 97) —— 水龙头:tBNB、U 代币
| 合约 | 地址 |
|---|---|
| Identity Registry (ERC-8004) | 0x8004A818BFB912233c491871b3d84c89A494BD9e |
| AgenticCommerce | 0xa206c0517b6371c6638cd9e4a42cc9f02a33b0de |
| EvaluatorRouter | 0xd7d36d66d2f1b608a0f943f722d27e3744f66f25 |
| OptimisticPolicy | 0x4f4678d4439fec812ac7674bb3efb4c8f5fb78a6 |
BSC 主网(链 56)
| 合约 | 地址 |
|---|---|
| Identity Registry (ERC-8004) | 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 |
| AgenticCommerce | 0xea4daa3100a767e86fded867729ae7446476eba6 |
| EvaluatorRouter | 0x51895229e12f9876011789b04f8698af06ccd6da |
| OptimisticPolicy | 0x9c01845705b3078aa2e8cff7520a6376fd766de5 |
支付代币地址不可配置 —— 它在运行时从 Commerce 内核读取(ERC8183Client.payment_token)。在主网上它解析为 U (United Stables) 0xcE24…6666(参见顶部的主网经济部分)。
注意事项:
- SDK 构造函数默认
network="bsc-testnet"—— 主网工作务必显式传入network="bsc-mainnet"(或NETWORK=bsc-mainnet),并打印bscscan.com(而非testnet.bscscan.com)浏览器链接;部分示例脚本硬编码了测试网 URL。 - 发现/索引器延迟:即使你的 URI 携带了真实名称,注册表索引也可能显示通用名称(例如
Agent #198565)。链上 URI 才是唯一可信来源;索引器名称可能滞后或保持通用。
环境变量
完整带注释的参考:references/env.example。要点如下:
| 变量 | 是否必需 | 说明 |
|---|---|---|
WALLET_PASSWORD | 是 | 用于加密/解密位于 ~/.bnbagent/wallets/ 的密钥库。 |
PRIVATE_KEY | 仅首次运行 | 导入并加密后可删除。若未提供则自动生成钱包。 |
WALLET_ADDRESS | 否 | 当存在多个密钥库时,选择一个。 |
NETWORK | 否(bsc-testnet) | 或 bsc-mainnet。 |
RPC_URL | 否 | 自定义 RPC 端点。 |
ERC8183_SERVICE_PRICE | 否(1e18) | 提供方最低预算,以原始单位计。 |
ERC8183_AGENT_URL | 若使用 LocalStorageProvider | 公开基础 URL,含 /erc8183;file:// 形式的交付物 URL 会被重写为该 URL。 |
STORAGE_API_KEY | 若使用 IPFSStorageProvider | Pinata 兼容的 JWT。 |
STORAGE_LOCAL_PATH | 否(.agent-data) | 本地交付物目录。 |
存储后端是在代码中选择的(传入 storage_provider=),而非通过环境变量。
安全规则(对 Agent 工作流很重要)
- EIP-712 签名默认受策略门控。
EVMWalletProvider.sign_typed_data仅接受针对 BSC 56/97 上 U-token 的 EIP-3009TransferWithAuthorization/ReceiveWithAuthorization。EIP-2612Permit与 Permit2PermitSingle/PermitBatch被无条件列入拒绝名单(它们会授予无限额度——恶意 402 服务可能掏空钱包)。切勿在 Agent 可触达的代码中试图绕过这一限制;SigningPolicy.permissive()和_DANGEROUS_sign_typed_data_no_policy()仅供测试使用。 - 切勿将原始的
WalletProvider交给工具函数。 应给它们一个受限的X402Signer(wallet, max_value_per_call={token: ...}, session_budget={token: ...}),并且始终从配置/链上注册表传入expected_to——绝不能来自 402 质询响应体。 - 自定义代币/类型:
SigningPolicy.strict_default().extend(domain_allowlist={(chain_id, contract)}, primary_type_allowlist={"MyType"})——Permit 拒绝名单仍然优先。 claimRefund不可暂停、不可挂钩:超过expiredAt后资金始终可收回。- 一次性/演示密钥:使用
EVMWalletProvider(persist=False),让一次性密钥永不落盘,且切勿在生产环境复用演示密钥。切勿将生产私钥粘贴到聊天/日志中——优先使用密钥库 +WALLET_PASSWORD流程。
完整理由、决策树与示例:references/sdk-readme.md(Security 一节)和 examples/security/e2e.py。
钱包后端
EVMWalletProvider(默认)——Keystore V3(兼容 MetaMask/Geth),持久化于~/.bnbagent/wallets/,或在persist=False时存于内存。TWAKProvider(Trust Wallet Agent Kit)——自托管、自行广播;不支持原始交易或通用 EIP-712 签名;仅支持 BSC;通过委托付款方make_x402_payer()实现 x402。使用前请阅读references/twak.md——不受支持的调用会抛出UnsupportedWalletOperation。切换方式:TWAKProvider(chain="bsc")或WALLET_KIND=twak。- 自定义(HSM、MPC、KMS):继承
WalletProvider。详情:references/wallets.md。
AA / 自广播钱包(Privy、ZeroDev 等)——已知的集成成本
通过提交 Intents/UserOperations 而非原始签名交易来发送交易的账户抽象钱包在 SDK 中尚不是一等公民(已在 BSC 主网上使用 Privy AA 客户端、bnbagent==0.4.0 进行过实地测试)。以下是预期情况以及行之有效的变通方案:
- ERC-20
approve失败:approve_payment_token会经过_send_tx→ 原始sign_transaction,而 AA 钱包并未实现该方法(sign.transaction: wallet does not implement raw-transaction signing)。变通方案:通过你的 AA 技术栈手动对支付代币执行approve(commerce, amount),然后以fund_bundles_approval=True调用fund。create_job/set_budget/fund本身可通过自定义 Intent 执行器正常工作(继承执行器 /WalletProvider的子类)。 create_job之后jobId返回None:AA 钱包返回的是user_operation_hash,而不是 tx hash,而create_job只会从收据日志中解析jobId。变通方案:等待打包上链,然后反向扫描jobCounter(或按client+provider+description索引最近的 job)来找到你的 job。- 对于有文档支持的自广播钱包,请使用
TWAKProvider(references/twak.md);Privy 风格的适配器目前需要自行构建。
故障排查
| 错误 | 原因 → 修复 |
|---|---|
No PRIVATE_KEY and no keystore found | 已自动生成新钱包,或设置 PRIVATE_KEY 以导入。 |
Multiple wallets found | 设置 WALLET_ADDRESS=0x...。 |
403 Provider mismatch | Job 分配给了其他 provider —— 检查 job.provider。 |
409 Not FUNDED | Job 已提交/已结算。 |
408 Job expired | 已超过 expiredAt;客户端可 claimRefund,并创建新 job。 |
402 Budget below service price | 客户端必须注资 ≥ ERC8183_SERVICE_PRICE。 |
settle 回滚 policy pending | 争议窗口尚未结束且无争议 —— 等待后重试。在主网上该窗口为 7 天。 |
voteReject 回滚 not voter/not disputed | 调用者不在白名单中或没有争议 —— 使用 examples/voter/vote_reject.py。 |
在 register_job 上回滚选择器 0xcdbc1d27 | Job 已绑定到某个 policy —— 重新注册按设计会回滚。如果 job_policy(job_id) 已设置,则视为成功。 |
sign.transaction: wallet does not implement raw-transaction signing | AA/自广播钱包命中了原始交易 approve 路径 —— 参见上文 AA 钱包部分。 |
create_job 之后 jobId 为 None | AA 钱包返回的是 user-op hash,没有可解析的收据 —— 反向扫描 jobCounter 找到你的 job。 |
尝试 fund 后 allowance 仍为 0 | 捆绑的 approve 从未上链(钱包路径错误)—— 手动 approve(commerce, amount),然后 fund。 |
本 skill 中的文件
references/sdk-readme.md—— 完整的上游 SDK README(对以上所有内容的深入细节)references/architecture.md—— 代码地图、分层、不变量references/twak.md—— TWAK 钱包支持矩阵与边界references/wallets.md—— 钱包 provider 深入剖析references/env.example—— 带注释的环境变量参考examples/—— 可复制并自行掌控的脚本:client/(5 个典型 job 流程)、a2a-agent/、agent-server/、voter/、twak/、x402/、security/、auto_settle.py