Loading...
Loading...
Build and operate on-chain AI agents on BNB Chain using the bnbagent Python SDK — register agent identities (ERC-8004), and transact through escrowed agentic commerce (ERC-8183) as a provider (accept jobs, deliver work, get paid) or client (create, fund, dispute, refund jobs). Also covers x402 micropayment signing. Use for anything involving BNB Chain agent identity, agent-to-agent paid jobs, BSC escrow jobs, or ERC-8004/ERC-8183/x402.
npx skill4agent add starchild-ai-agent/official-skills bnbagentpip install bnbagentActive development — breaking changes possible. Tested with; verify install withbnbagent==0.4.0. Optional extra:python -c "import bnbagent; print(bnbagent.__version__)"for IPFS/Pinata deliverable storage.pip install "bnbagent[ipfs]"
0xcE24439F2D9C6a2289F741120FE202248B666666fund()erc8183.token_decimals()COMPLETEDSUBMITTEDexamples/auto_settle.pysettlepolicy pending| Party | Needs | Why |
|---|---|---|
| Client | BNB | gas for |
| Client | U | the job budget escrowed by |
| Provider | BNB | gas for |
| Settler (anyone) | BNB | gas for |
| Goal | Use | Reference |
|---|---|---|
| Register agent identity on-chain | | quick start below |
| Earn: accept + deliver funded jobs | | quick start below, |
| Pay: create/fund/settle jobs | | quick start below, |
| Vote on disputes (whitelisted voter) | | |
| Pay HTTP 402 challenges (x402) | | |
| Understand internals / extend | — | |
| Wallet backends (EVM keystore vs twak) | | |
WALLET_PASSWORDimport 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_resultsubmitERC8183Client.settle(job_id)examples/auto_settle.pyexamples/agent-server/scripts/settle.pyjobjobIddescriptionbudgetclientproviderevaluatorstatusFUNDEDexpiredAthookexamples/a2a-agent/examples/agent-server/https://example.invalid/manifest.jsonexamples/client/happy.pyLocalStorageProviderERC8183_AGENT_URLIPFSStorageProviderimport 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.COMPLETEDerc8183.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 hatchfund(job_id, amount, approve_floor=None)max(amount, 100 * 10**decimals)approve_floor=0amountOPEN ──► 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| Protocol / write | BSC Testnet | BSC Mainnet |
|---|---|---|
ERC-8004 | ✅ sponsored (MegaFuel) | ✅ sponsored (MegaFuel) — works with a zero-BNB wallet |
| ERC-8183 create/fund/submit/settle | 🟡 per-call: MegaFuel decides ( | ❌ never sponsored — all writes self-pay BNB |
ERC-20 | ❌ always self-pays — fresh testnet buyer needs a little tBNB | ❌ self-pays |
| twak wallet ops | ❌ twak self-pays (twak-internal, SDK has no control) | ✅ twak auto-sponsors — see |
| Contract | Address |
|---|---|
| Identity Registry (ERC-8004) | |
| AgenticCommerce | |
| EvaluatorRouter | |
| OptimisticPolicy | |
| Contract | Address |
|---|---|
| Identity Registry (ERC-8004) | |
| AgenticCommerce | |
| EvaluatorRouter | |
| OptimisticPolicy | |
ERC8183Client.payment_token0xcE24…6666network="bsc-testnet"network="bsc-mainnet"NETWORK=bsc-mainnetbscscan.comtestnet.bscscan.comAgent #198565references/env.example| Variable | Required | Notes |
|---|---|---|
| Yes | Encrypts/decrypts the keystore at |
| First run only | Imported and encrypted, then removable. Auto-generates a wallet if absent. |
| No | Pick a keystore when several exist. |
| No ( | Or |
| No | Custom RPC endpoint. |
| No ( | Provider's minimum budget, raw units. |
| If LocalStorageProvider | Public base URL incl. |
| If IPFSStorageProvider | Pinata-compatible JWT. |
| No ( | Local deliverable dir. |
storage_provider=EVMWalletProvider.sign_typed_dataTransferWithAuthorizationReceiveWithAuthorizationPermitPermitSinglePermitBatchSigningPolicy.permissive()_DANGEROUS_sign_typed_data_no_policy()WalletProviderX402Signer(wallet, max_value_per_call={token: ...}, session_budget={token: ...})expected_toSigningPolicy.strict_default().extend(domain_allowlist={(chain_id, contract)}, primary_type_allowlist={"MyType"})claimRefundexpiredAtEVMWalletProvider(persist=False)WALLET_PASSWORDreferences/sdk-readme.mdexamples/security/e2e.pyEVMWalletProvider~/.bnbagent/wallets/persist=FalseTWAKProvidermake_x402_payer()references/twak.mdUnsupportedWalletOperationTWAKProvider(chain="bsc")WALLET_KIND=twakWalletProviderreferences/wallets.mdbnbagent==0.4.0approveapprove_payment_token_send_txsign_transactionsign.transaction: wallet does not implement raw-transaction signingapprove(commerce, amount)fundfund_bundles_approval=Truecreate_jobset_budgetfundWalletProviderjobIdNonecreate_jobuser_operation_hashcreate_jobjobIdjobCounterclientproviderdescriptionTWAKProviderreferences/twak.md| Error | Cause → Fix |
|---|---|
| New wallet auto-generated, or set |
| Set |
| Job assigned to a different provider — check |
| Job already submitted/settled. |
| Past |
| Client must fund ≥ |
| Dispute window not elapsed and no dispute — wait, then retry. On mainnet the window is 7 days. |
| Caller not whitelisted or no dispute — use |
Revert selector | Job already bound to a policy — re-registering reverts by design. Treat as success if |
| AA/self-broadcast wallet hit the raw-tx |
| AA wallet returned a user-op hash, no receipt to parse — scan |
Allowance stays 0 after | The bundled |
references/sdk-readme.mdreferences/architecture.mdreferences/twak.mdreferences/wallets.mdreferences/env.exampleexamples/client/a2a-agent/agent-server/voter/twak/x402/security/auto_settle.py