bnbagent について
BNB Chain 上でオンチェーン AI エージェントを構築する Python SDK です。ERC-8004 のエージェント ID と ERC-8183 のエスクロー型ジョブを個別に扱い、複数のウォレットとストレージ方式を利用できます。
主な機能
- 01ERC-8004 エージェント ID の登録
- 02ERC-8183 ジョブの作成と管理
- 03入金済みジョブの監視と成果物の提出
- 04異議申し立てへの投票と返金
- 05EVM と TWAK のウォレットバックエンド
活用シーン
- 01BSC でのエージェント ID 登録
- 02入金済みジョブの受注と納品
- 03提供者向けジョブの作成と入金
- 04異議のあるジョブへの投票
MCP 対応
MCP を含むサービスインターフェースの選択は実装者に委ねられています。サンプルには、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 エージェント向けの Python SDK(pip install bnbagent、Python 3.10+)です。互いに独立した 2 つの機能があります:
- ERC-8004(アイデンティティ) — エージェントを、検出可能なプロフィール URI を持つ ERC-721 アイデンティティトークンとしてオンチェーンに登録します。BSC Testnet では MegaFuel paymaster によりガス代が無料です。
- ERC-8183(エージェント型コマース) — クライアント(支払う側)とプロバイダー(納品する側)の間のトラストレスなジョブエスクローです。楽観的決済: 異議申立て期間を過ぎても沈黙していれば承認とみなされ、クライアントが異議を申し立てるとホワイトリスト登録された投票者の定足数による却下が引き起こされます。
これらは独立しています: ERC-8004 への登録なしで ERC-8183 のジョブを実行できます(登録は検出のためにのみ推奨されます)。
活発に開発中 — 破壊的な変更が発生する可能性があります。
bnbagent==0.4.0でテスト済みです。インストールはpython -c "import bnbagent; print(bnbagent.__version__)"で確認してください。オプションの追加機能: IPFS/Pinata への成果物保存にはpip install "bnbagent[ipfs]"。
⚠️ メインネットの経済性 — メインネットでのコマースの前に必ずお読みください
- 支払いトークンは U(United Stables)
0xcE24439F2D9C6a2289F741120FE202248B666666です — BNB でも USDT/USDC でもありません。これ用のフォーセットはありません: PancakeSwap で U を入手してください(WBNB–U ペアが存在します)。クライアントはfund()を呼び出す前に U を保有している必要があります。decimals は実行時にerc8183.token_decimals()で取得してください — 決め打ちしないでください。 - メインネットでの ERC-8183 の書き込みは決してガス代がスポンサードされません。 クライアントとプロバイダーの両方がガス代用の BNB を必要とします(メインネットでスポンサードされるのは ERC-8004 のアイデンティティ登録のみです)。
- メインネットの異議申立て期間は 604800 秒(7 日間)です。 正常系の経路では 1 回のセッションで
COMPLETEDに到達できません — 沈黙による承認は期間が過ぎてから初めて有効になります。部分的な E2E(SUBMITTEDまで)を計画し、後で cron/オペレータースクリプト(examples/auto_settle.py)で決済してください。この 1 週間の間にsettleがpolicy pendingでリバートするのは想定どおりであり、エラーではありません。
事前残高チェックリスト(メインネット)
| 関係者 | 必要なもの | 理由 |
|---|---|---|
| クライアント | BNB | createJob/registerJob/setBudget/approve/fund のガス代(+任意のスワップのガス代) |
| クライアント | U | fund でエスクローされるジョブ予算(+再試行用の少額の残高) |
| プロバイダー | BNB | submit のガス代 — メインネットではスポンサードされません |
| 決済者(誰でも) | BNB | 期間経過後の settle のガス代 |
何をするのかを決める
| 目的 | 使用するもの | リファレンス |
|---|---|---|
| エージェントのアイデンティティをオンチェーンに登録する | 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 |
クイックスタート: エージェントを登録する(ERC-8004)
1 回だけのセットアップです。秘密鍵(省略時は自動生成されます)と 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"]
クイックスタート: プロバイダー(earn ループ、ヘッドレス)
サーバーは不要です。資金提供されたジョブを監視し、作業を行い、提出します:
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 ステータス、割り当て、有効期限、budget ≥ 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のようなプレースホルダー URL(examples/client/happy.pyと同様)は、チェーン上のみのデモであり、投票者は成果物を検証できません。クライアントが異議を申し立てる可能性があるものについては、実際のストレージ(公開された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 を承認します。allowance がすでにそれをカバーしている場合は、承認は送信されません。
ジョブのライフサイクル
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
ガススポンサーシップのマトリクス
| プロトコル / 書き込み | BSC Testnet | BSC Mainnet |
|---|---|---|
ERC-8004 register_agent | ✅ スポンサーあり (MegaFuel) | ✅ スポンサーあり (MegaFuel) — BNB 残高ゼロのウォレットでも動作します |
| ERC-8183 create/fund/submit/settle | 🟡 呼び出しごと: MegaFuel が判断(現在 fund/settle はスポンサーあり)。拒否された呼び出しは自己負担 | ❌ スポンサーなし — すべての書き込みは BNB の自己負担 |
支払いトークンの ERC-20 approve(allowance 不足時に fund 内で送信される) | ❌ 常に自己負担 — 新しいテストネット購入者には少しの tBNB が必要 | ❌ 自己負担 |
| twak ウォレット操作 | ❌ twak が自己負担(twak 内部、SDK に制御なし) | ✅ twak が自動スポンサー — references/twak.md を参照 |
ネットワークとコントラクト
BSC Testnet (chain 97) — フォーセット: tBNB、U tokens
| コントラクト | アドレス |
|---|---|
| Identity Registry (ERC-8004) | 0x8004A818BFB912233c491871b3d84c89A494BD9e |
| AgenticCommerce | 0xa206c0517b6371c6638cd9e4a42cc9f02a33b0de |
| EvaluatorRouter | 0xd7d36d66d2f1b608a0f943f722d27e3744f66f25 |
| OptimisticPolicy | 0x4f4678d4439fec812ac7674bb3efb4c8f5fb78a6 |
BSC Mainnet (chain 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 | いいえ | 複数のキーストアが存在する場合に 1 つを選択します。 |
NETWORK | いいえ (bsc-testnet) | または bsc-mainnet。 |
RPC_URL | いいえ | カスタム RPC エンドポイント。 |
ERC8183_SERVICE_PRICE | いいえ (1e18) | プロバイダーの最低予算、生の単位。 |
ERC8183_AGENT_URL | LocalStorageProvider の場合 | /erc8183 を含む公開ベース URL。file:// の成果物 URL はこれに書き換えられます。 |
STORAGE_API_KEY | IPFSStorageProvider の場合 | Pinata 互換の JWT。 |
STORAGE_LOCAL_PATH | いいえ (.agent-data) | ローカルの成果物ディレクトリ。 |
ストレージバックエンドは環境変数ではなくコード内で選択します (storage_provider= を渡します)。
セキュリティルール (エージェントのフローで重要)
- EIP-712 署名はデフォルトでポリシーによって制御されます。
EVMWalletProvider.sign_typed_dataは、BSC 56/97 上の U-token に対する EIP-3009 のTransferWithAuthorization/ReceiveWithAuthorizationのみを受け付けます。EIP-2612 のPermitおよび Permit2 のPermitSingle/PermitBatchは無条件で拒否リストに登録されています (無制限のアローワンスを付与するため、悪意のある 402 サーバーがウォレットを流出させる可能性があります)。エージェントから到達可能なコードでこれを回避しようとしないでください。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 (セキュリティのセクション) および examples/security/e2e.py。
ウォレットバックエンド
EVMWalletProvider(デフォルト) — Keystore V3 (MetaMask/Geth 互換)、~/.bnbagent/wallets/に永続化、またはpersist=Falseでメモリ内に保持。TWAKProvider(Trust Wallet Agent Kit) — セルフカストディ、セルフブロードキャスト。生のトランザクションや汎用の EIP-712 署名はなし。BSC のみ。x402 は委任ペイヤーmake_x402_payer()経由。使用前にreferences/twak.mdをお読みください — サポートされていない呼び出しはUnsupportedWalletOperationを発生させます。切り替え:TWAKProvider(chain="bsc")またはWALLET_KIND=twak。- カスタム (HSM、MPC、KMS):
WalletProviderをサブクラス化します。詳細:references/wallets.md。
AA / セルフブロードキャストウォレット(Privy、ZeroDev など) — 既知の統合コスト
生の署名済みトランザクションの代わりに Intent/UserOperation を送信するアカウント抽象化ウォレットは、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)。回避策: 支払いトークンに対してapprove(commerce, amount)を AA スタック経由で手動実行し、その後fund_bundles_approval=Trueでfundを呼び出します。create_job/set_budget/fund自体はカスタム Intent エグゼキューター(エグゼキューター /WalletProviderをサブクラス化)を通じて動作します。 create_job後にjobIdがNoneで返る: AA ウォレットは tx ハッシュではなくuser_operation_hashを返し、create_jobはレシートログからjobIdしか解析しません。回避策: 取り込みを待ってからjobCounterを逆方向にスキャンする(またはclient+provider+descriptionで最近のジョブをインデックス化する)ことで自分のジョブを見つけます。- ドキュメント化されたセルフブロードキャストウォレットには
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 を確認してください。 |
409 Not FUNDED | ジョブはすでに送信済み/決済済みです。 |
408 Job expired | expiredAt を過ぎています。クライアントは claimRefund を実行し、新しいジョブを作成できます。 |
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_id) がすでに設定されていれば成功として扱ってください。 |
sign.transaction: wallet does not implement raw-transaction signing | AA/セルフブロードキャストウォレットが生の tx の approve パスに到達しました — 上記の AA ウォレットのセクションを参照してください。 |
create_job 後に jobId が None | AA ウォレットが user-op ハッシュを返し、解析するレシートがありません — jobCounter を逆方向にスキャンして自分のジョブを探してください。 |
fund 試行後もアローワンスが 0 のまま | バンドルされた approve がまったく着地していません(誤ったウォレットパス) — 手動で approve(commerce, amount) を実行し、その後 fund を実行してください。 |
このスキルに含まれるファイル
references/sdk-readme.md— 上流 SDK の完全な README(上記すべてについての詳細な説明)references/architecture.md— コードマップ、レイヤリング、不変条件references/twak.md— TWAK ウォレットのサポートマトリックスと境界references/wallets.md— ウォレットプロバイダーの詳細解説references/env.example— 注釈付きの環境変数リファレンスexamples/— コピーして自分のものにできるスクリプト:client/(5 つの標準的なジョブフロー)、a2a-agent/、agent-server/、voter/、twak/、x402/、security/、auto_settle.py