@bnbagent/studio-cli 0.0.6-alpha.6 → 0.0.6-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DISCLAIMER.md +6 -27
- package/README.md +6 -13
- package/dist/bag.js +1121 -795
- package/dist/{chunk-VEOOFDSF.js → chunk-JZAW6HMV.js} +74 -5
- package/dist/{deployCli-EEK75T67.js → deployCli-AJ25A4VK.js} +1 -1
- package/package.json +2 -2
- package/recipes/providers/pieverse-llm/skills/funding-pieverse-llm.md +32 -64
- package/skills/bnbagent-studio.md +29 -74
- package/skills/references/bnbagent-studio-adding-to-project.md +58 -169
- package/skills/references/bnbagent-studio-buying-from-bazaar.md +43 -104
- package/skills/references/bnbagent-studio-buying-via-8183.md +38 -112
- package/skills/references/bnbagent-studio-extending-signing.md +47 -50
- package/skills/references/bnbagent-studio-operating.md +58 -111
- package/skills/references/bnbagent-studio-scaffolding-agent.md +136 -403
- package/skills/references/bnbagent-studio-selling-via-8183.md +87 -172
- package/skills/references/bnbagent-studio-selling-via-b402.md +39 -131
- package/skills/references/bnbagent-studio-use-aws-agentcore.md +43 -144
- package/skills/references/bnbagent-studio-use-azure-foundry.md +34 -95
- package/skills/references/bnbagent-studio-use-bnb-trial.md +12 -36
- package/skills/references/bnbagent-studio-using-altana-wallet.md +11 -28
- package/skills/references/bnbagent-studio-using-twak-wallet.md +102 -210
- package/skills/references/bnbagent-studio-wiring-llm-tools.md +71 -150
|
@@ -1,85 +1,50 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bnbagent-studio-buying-via-8183
|
|
3
|
-
description: When the user is acting as ERC-8183 buyer
|
|
3
|
+
description: When the user is acting as ERC-8183 buyer - finding a provider, getting a quote, buying a service, fetching the deliverable, and deciding to approve / dispute / reject the work. Owns the buyer-side decision tree for the entire job lifecycle including settle-window timing (24h dispute window), dispute governance flow, and `Submission deadline has passed` recovery.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
> **Reference file** of the `bnbagent-studio` router skill
|
|
6
|
+
> **Reference file** of the `bnbagent-studio` router skill - installed at `bnbagent-studio/references/` and loaded on demand (not a standalone skill). Route here via the router's decision tree.
|
|
7
7
|
|
|
8
8
|
# bnbagent-studio-buying-via-8183
|
|
9
9
|
|
|
10
|
-
> **v1 scope note:** v1 is **seller-only**
|
|
11
|
-
> the main v1 payment path. The x402 buyer kernel survives only as the Agent's
|
|
12
|
-
> *automatic* LLM top-up (`@bnbagent/studio-runtime/x402`, budget-gated, not an LLM tool),
|
|
13
|
-
> and the erc8183 `buyWorkflow` survives as an **underlying capability** still
|
|
14
|
-
> reachable via the `bag erc8183 buy/status/fetch/settle` CLI. Full buyer-side
|
|
15
|
-
> *product* flows (a buyer agent, buyer-side negotiation wrapping) are deferred
|
|
16
|
-
> to v2. The CLI procedure below still works for manual / testing use.
|
|
10
|
+
> **v1 scope note:** v1 is **seller-only** - buyer flows are **NOT** the main v1 payment path. The x402 buyer kernel survives only as the Agent's _automatic_ LLM top-up (`@bnbagent/studio-runtime/x402`, budget-gated, not an LLM tool), and the erc8183 `buyWorkflow` survives as an **underlying capability** still reachable via the `bag erc8183 buy/status/fetch/settle` CLI. Full buyer-side _product_ flows (a buyer agent, buyer-side negotiation wrapping) are deferred to v2. The CLI procedure below still works for manual / testing use.
|
|
17
11
|
|
|
18
|
-
Procedure for **buyer-side flow**: have your agent (or you directly via CLI)
|
|
19
|
-
purchase a service from another ERC-8183 seller, verify the deliverable, and
|
|
20
|
-
close out the job with the right `settle` action.
|
|
12
|
+
Procedure for **buyer-side flow**: have your agent (or you directly via CLI) purchase a service from another ERC-8183 seller, verify the deliverable, and close out the job with the right `settle` action.
|
|
21
13
|
|
|
22
|
-
Audience: Claude Code in a
|
|
23
|
-
The buying CLI (`bag erc8183 buy/status/fetch/settle`) targets the **Agent
|
|
24
|
-
sub-project's** wallet — these commands resolve to `<workspace>/app/agent/`
|
|
25
|
-
automatically when run from the workspace root.
|
|
14
|
+
Audience: Claude Code in a current TypeScript workspace with a funded wallet (tBNB + U). The buying CLI (`bag erc8183 buy/status/fetch/settle`) targets the **Agent sub-project's** wallet - these commands resolve to `<workspace>/app/agent/` automatically when run from the workspace root.
|
|
26
15
|
|
|
27
|
-
> **Protocol facts** (independent of the local filesystem layout): 24h dispute
|
|
28
|
-
> window, `0x17be5b7b` revert, `expired_at` arithmetic.
|
|
16
|
+
> **Protocol facts** (independent of the local filesystem layout): 24h dispute window, `0x17be5b7b` revert, `expired_at` arithmetic.
|
|
29
17
|
|
|
30
18
|
**Different from**:
|
|
31
|
-
- `bnbagent-studio-operating.md` (same directory) — generic ops (dev / doctor / balance / RPC); jumps here for buyer-specific decisions
|
|
32
|
-
- `bnbagent-studio-adding-to-project` — covers wiring a buyer tool set into an existing project (the static setup, not the runtime decisions)
|
|
33
|
-
- `bnbagent-studio-selling-via-8183.md` (same directory) — the mirror playbook for the seller side (`on_job`, dispute defense)
|
|
34
19
|
|
|
35
|
-
|
|
36
|
-
|
|
20
|
+
- `bnbagent-studio-operating.md` (same directory) - generic ops (dev / doctor / balance / RPC); jumps here for buyer-specific decisions
|
|
21
|
+
- `bnbagent-studio-adding-to-project` - covers wiring a buyer tool set into an existing project (the static setup, not the runtime decisions)
|
|
22
|
+
- `bnbagent-studio-selling-via-8183.md` (same directory) - the mirror playbook for the seller side (`on_job`, dispute defense)
|
|
23
|
+
|
|
24
|
+
This skill owns: **buy → fetch → approve/dispute/reject loop**, whether driven via CLI or the agent's LLM calling `erc8183_buyer_set` tools.
|
|
37
25
|
|
|
38
26
|
## Buyer settle decision tree (load this skill when the user hits any of these)
|
|
39
27
|
|
|
40
28
|
`SUBMITTED` job → buyer has three options, **time-gated by the on-chain dispute window**:
|
|
41
29
|
|
|
42
30
|
| Action | Allowed | What happens |
|
|
43
|
-
|
|
31
|
+
| --- | --- | --- |
|
|
44
32
|
| `bag erc8183 settle <id> --action approve` | **After 24h dispute_window has passed** (chain enforces; reverts with `0x17be5b7b` if early) | Job → `COMPLETED`; seller receives U |
|
|
45
33
|
| `bag erc8183 settle <id> --action dispute` | **Within dispute_window** | Opens governance flow; quorum vote decides; refund possible |
|
|
46
34
|
| `bag erc8183 settle <id> --action reject` | **Only if you're a quorum voter, not the buyer** | Job → `REJECTED` via governance |
|
|
47
35
|
|
|
48
|
-
If the buyer agent (or LLM) is about to call `settle --action approve` and it's
|
|
49
|
-
been <24h since SUBMITTED, **wait** or use `dispute`. Don't try to retry
|
|
50
|
-
through the revert — read the error and pick the right action.
|
|
36
|
+
If the buyer agent (or LLM) is about to call `settle --action approve` and it's been <24h since SUBMITTED, **wait** or use `dispute`. Don't try to retry through the revert - read the error and pick the right action.
|
|
51
37
|
|
|
52
|
-
Common error: `Submission deadline has passed` → buyer set `expiredAt` too
|
|
53
|
-
short (< dispute_window). Fix: re-buy with `--deadline-min ≥ 1` (the buy
|
|
54
|
-
flow now auto-adds dispute_window).
|
|
38
|
+
Common error: `Submission deadline has passed` → buyer set `expiredAt` too short (< dispute_window). Fix: re-buy with `--deadline-min ≥ 1` (the buy flow now auto-adds dispute_window).
|
|
55
39
|
|
|
56
40
|
## Preconditions
|
|
57
41
|
|
|
58
42
|
- `bag doctor` is clean (or only warns on LLM key)
|
|
59
|
-
- For a paid job, the wallet has ≥ 0.05 tBNB (gas) and enough U for the budget
|
|
60
|
-
plus slack. On BSC testnet the
|
|
61
|
-
ERC-8183 kernel writes (`createJob` / `fund` deposit / `settle` …) are
|
|
62
|
-
gas-sponsored via the SDK's MegaFuel paymaster **when they target the
|
|
63
|
-
canonical contracts**, so you spend far less tBNB than
|
|
64
|
-
that — but **not zero**: `fund` sends an ERC-20 `approve` (a token call, not
|
|
65
|
-
sponsored) when the token allowance is too low — typically just the first fund,
|
|
66
|
-
since studio approves a floored cap that later jobs reuse. Keep a little tBNB for
|
|
67
|
-
it. (Mainnet is never sponsored.) Sponsorship is granted per target contract
|
|
68
|
-
by the paymaster policy: a custom/QA stack selected via the
|
|
69
|
-
`ERC8183_*_ADDRESS` overrides is normally **not** covered, so every write
|
|
70
|
-
self-pays gas (the SDK logs `… is not sponsorable on this network;
|
|
71
|
-
self-paying gas` and falls back automatically) — keep tBNB for the whole
|
|
72
|
-
flow, or set `BNBAGENT_USE_PAYMASTER=0` to skip the per-transaction
|
|
73
|
-
sponsorship probe and self-pay directly. For a FREE job, use `--budget-u 0`:
|
|
74
|
-
no U balance, ERC-20 approval, or token escrow is needed, but the ERC-8183
|
|
75
|
-
writes still need the selected gas/paymaster path and a zero-price-compatible
|
|
76
|
-
commerce/router/policy stack — today that means a custom stack, so expect the
|
|
77
|
-
writes to self-pay gas as above.
|
|
43
|
+
- For a paid job, the wallet has ≥ 0.05 tBNB (gas) and enough U for the budget plus slack. On BSC testnet the ERC-8183 kernel writes (`createJob` / `fund` deposit / `settle` …) are gas-sponsored via the SDK's MegaFuel paymaster **when they target the canonical contracts**, so you spend far less tBNB than that - but **not zero**: `fund` sends an ERC-20 `approve` (a token call, not sponsored) when the token allowance is too low - typically just the first fund, since studio approves a floored cap that later jobs reuse. Keep a little tBNB for it. (Mainnet is never sponsored.) Sponsorship is granted per target contract by the paymaster policy: a custom/QA stack selected via the `ERC8183_*_ADDRESS` overrides is normally **not** covered, so every write self-pays gas (the SDK logs `… is not sponsorable on this network; self-paying gas` and falls back automatically) - keep tBNB for the whole flow, or set `BNBAGENT_USE_PAYMASTER=0` to skip the per-transaction sponsorship probe and self-pay directly. For a FREE job, use `--budget-u 0`: no U balance, ERC-20 approval, or token escrow is needed, but the ERC-8183 writes still need the selected gas/paymaster path and a zero-price-compatible commerce/router/policy stack - today that means a custom stack, so expect the writes to self-pay gas as above.
|
|
78
44
|
- You know the **provider's wallet address** (the seller agent's address)
|
|
79
|
-
- The seller is **reachable** (its A2A agent is deployed somewhere); discoverable
|
|
80
|
-
via the provider's `bag erc8004 resolve <agent_id>` endpoint URI
|
|
45
|
+
- The seller is **reachable** (its A2A agent is deployed somewhere); discoverable via the provider's `bag erc8004 resolve <agent_id>` endpoint URI
|
|
81
46
|
|
|
82
|
-
## Stage 1
|
|
47
|
+
## Stage 1 - Pre-flight checks
|
|
83
48
|
|
|
84
49
|
```bash
|
|
85
50
|
# 1. Confirm wallet + balances
|
|
@@ -91,27 +56,15 @@ bag erc8004 resolve <provider_agent_id>
|
|
|
91
56
|
# → returns the agent_uri; decode it (base64 data: URI) to verify the endpoint URL
|
|
92
57
|
```
|
|
93
58
|
|
|
94
|
-
If the provider's endpoint URL points somewhere reachable (e.g. an `https://`
|
|
95
|
-
AgentCore runtime URL), proceed. If it's `http://localhost:...`, that means you
|
|
96
|
-
must be on the same host.
|
|
59
|
+
If the provider's endpoint URL points somewhere reachable (e.g. an `https://` AgentCore runtime URL), proceed. If it's `http://localhost:...`, that means you must be on the same host.
|
|
97
60
|
|
|
98
|
-
## Stage 2
|
|
61
|
+
## Stage 2 - (Optional) Negotiate price
|
|
99
62
|
|
|
100
|
-
|
|
101
|
-
handshake (`@bnbagent/studio-runtime/erc8183` `negotiateWithSeller`, wired into
|
|
102
|
-
the buy workflow). It fires automatically when you buy by `--agent-id` (resolves
|
|
103
|
-
the seller's endpoint from its ERC-8004 record, then POSTs `/negotiate`); pass
|
|
104
|
-
`--no-negotiate` to skip it. When you buy by `--provider <addr>` you supply the
|
|
105
|
-
budget directly and assume the price was agreed off-chain.
|
|
63
|
+
The current `@bnbagent/studio-runtime` wraps the buyer-side negotiation handshake (`@bnbagent/studio-runtime/erc8183` `negotiateWithSeller`, wired into the buy workflow). It fires automatically when you buy by `--agent-id` (resolves the seller's endpoint from its ERC-8004 record, then POSTs `/negotiate`); pass `--no-negotiate` to skip it. When you buy by `--provider <addr>` you supply the budget directly and assume the price was agreed off-chain.
|
|
106
64
|
|
|
107
|
-
If you want to talk to the seller manually, the seller now exposes its
|
|
108
|
-
`negotiate` skill over **A2A** (the seller's ERC-8004 endpoint URI is an A2A base
|
|
109
|
-
URL): send an A2A `message/send` JSON-RPC call carrying a `DataPart`
|
|
110
|
-
`{"skill": "negotiate", "task_description": "...", "terms": {...}}` (+ the seller's
|
|
111
|
-
OAuth2 Bearer when deployed). The reply data part is the signed `NegotiationResult`
|
|
112
|
-
quote. But you don't need this for v0.0.x — go straight to buy.
|
|
65
|
+
If you want to talk to the seller manually, the seller now exposes its `negotiate` skill over **A2A** (the seller's ERC-8004 endpoint URI is an A2A base URL): send an A2A `message/send` JSON-RPC call carrying a `DataPart` `{"skill": "negotiate", "task_description": "...", "terms": {...}}` (+ the seller's OAuth2 Bearer when deployed). The reply data part is the signed `NegotiationResult` quote. For the normal CLI flow, go straight to buy.
|
|
113
66
|
|
|
114
|
-
## Stage 3
|
|
67
|
+
## Stage 3 - Buy
|
|
115
68
|
|
|
116
69
|
```bash
|
|
117
70
|
bag erc8183 buy --provider <provider_addr> "<task description>" \
|
|
@@ -129,34 +82,22 @@ bag erc8183 buy --provider <provider_addr> "<task description>" \
|
|
|
129
82
|
--budget-u 0 --deadline-min 30 --network bsc-testnet
|
|
130
83
|
```
|
|
131
84
|
|
|
132
|
-
> **Task can be passed two ways** (both accepted): as a positional argument
|
|
133
|
-
> `bag erc8183 buy --provider <addr> "<task>"` OR via the flag
|
|
134
|
-
> `bag erc8183 buy --provider <addr> --task "<task>"`. Pass it once — supplying
|
|
135
|
-
> both at the same time is an error.
|
|
85
|
+
> **Task can be passed two ways** (both accepted): as a positional argument `bag erc8183 buy --provider <addr> "<task>"` OR via the flag `bag erc8183 buy --provider <addr> --task "<task>"`. Pass it once - supplying both at the same time is an error.
|
|
136
86
|
|
|
137
87
|
The 4 on-chain steps run sequentially:
|
|
138
88
|
|
|
139
89
|
1. `createJob(provider, expiredAt, description)` → returns `job_id`
|
|
140
90
|
2. `registerJob(jobId)`
|
|
141
91
|
3. `setBudget(jobId, rawBudget)`
|
|
142
|
-
4. `fund(jobId, rawBudget, approveFloor=rawBudget)`
|
|
143
|
-
when the positive budget needs allowance; budget 0 skips approval and escrow
|
|
92
|
+
4. `fund(jobId, rawBudget, approveFloor=rawBudget)` - auto-approves U only when the positive budget needs allowance; budget 0 skips approval and escrow
|
|
144
93
|
|
|
145
94
|
Output prints 4 tx hashes + `job_id`. Note the `job_id` for later.
|
|
146
95
|
|
|
147
|
-
**Gotcha**: `expired_at = now + deadline_minutes*60 + dispute_window` (24h). This
|
|
148
|
-
is intentional — the chain enforces that submission must happen before
|
|
149
|
-
`expired_at - dispute_window`. If you set `deadline_minutes=30`, the seller has
|
|
150
|
-
30 min to submit (then a 24h dispute window starts).
|
|
96
|
+
**Gotcha**: `expired_at = now + deadline_minutes*60 + dispute_window` (24h). This is intentional - the chain enforces that submission must happen before `expired_at - dispute_window`. If you set `deadline_minutes=30`, the seller has 30 min to submit (then a 24h dispute window starts).
|
|
151
97
|
|
|
152
|
-
## Stage 4
|
|
98
|
+
## Stage 4 - Wait for SUBMITTED
|
|
153
99
|
|
|
154
|
-
The single seller runtime has **no standalone poller**
|
|
155
|
-
`notify_funded` A2A message to the seller ("I funded job X, please deliver"). The
|
|
156
|
-
seller **acks `accepted` immediately and delivers in the background**, so you do
|
|
157
|
-
**not** get the deliverable in the A2A reply — you poll the chain for it. The
|
|
158
|
-
seller also sweeps other funded jobs on each `notify_funded` (in the background,
|
|
159
|
-
deduped). Check status:
|
|
100
|
+
The single seller runtime has **no standalone poller** - after funding, send a `notify_funded` A2A message to the seller ("I funded job X, please deliver"). The seller **acks `accepted` immediately and delivers in the background**, so you do **not** get the deliverable in the A2A reply - you poll the chain for it. The seller also sweeps other funded jobs on each `notify_funded` (in the background, deduped). Check status:
|
|
160
101
|
|
|
161
102
|
```bash
|
|
162
103
|
bag erc8183 status <job_id>
|
|
@@ -167,31 +108,21 @@ Statuses:
|
|
|
167
108
|
- `OPEN` → not yet funded (shouldn't see this after Step 3)
|
|
168
109
|
- `FUNDED` → waiting for seller to pick up + submit
|
|
169
110
|
- `SUBMITTED` → ready to fetch + settle
|
|
170
|
-
- `EXPIRED` → seller missed the deadline; use `bag erc8183 settle --action ...` or
|
|
171
|
-
the SDK's `mark_expired` / `claim_refund`
|
|
111
|
+
- `EXPIRED` → seller missed the deadline; use `bag erc8183 settle --action ...` or the SDK's `mark_expired` / `claim_refund`
|
|
172
112
|
|
|
173
|
-
Delivery is no longer instant: because the seller works in the background, the
|
|
174
|
-
wait is however long its work takes (seconds to minutes). Poll until `SUBMITTED`,
|
|
175
|
-
then read `deliverable_url` (Stage 5). If it stays `FUNDED`, either no one sent
|
|
176
|
-
`notify_funded` (and the sweep hasn't run — the seller may be scaled to zero), or
|
|
177
|
-
the seller's background work failed or hasn't finished yet — re-send
|
|
178
|
-
`notify_funded` to nudge it while the runtime is warm. (Also possible: a config
|
|
179
|
-
mismatch, most often `expired_at` too soon — but the workflow auto-fixes this now.)
|
|
113
|
+
Delivery is no longer instant: because the seller works in the background, the wait is however long its work takes (seconds to minutes). Poll until `SUBMITTED`, then read `deliverable_url` (Stage 5). If it stays `FUNDED`, either no one sent `notify_funded` (and the sweep hasn't run - the seller may be scaled to zero), or the seller's background work failed or hasn't finished yet - re-send `notify_funded` to nudge it while the runtime is warm. (Also possible: a config mismatch, most often `expired_at` too soon - but the workflow auto-fixes this now.)
|
|
180
114
|
|
|
181
|
-
## Stage 5
|
|
115
|
+
## Stage 5 - Fetch the deliverable
|
|
182
116
|
|
|
183
|
-
The single seller runtime serves **no** job-query endpoint
|
|
184
|
-
back from the on-chain submission (the `submit` tx carries the `deliverable_url`,
|
|
185
|
-
typically `ipfs://...`) and the IPFS object. Get the URL via CLI:
|
|
117
|
+
The single seller runtime serves **no** job-query endpoint - the deliverable is read back from the on-chain submission (the `submit` tx carries the `deliverable_url`, typically `ipfs://...`) and the IPFS object. Get the URL via CLI:
|
|
186
118
|
|
|
187
119
|
```bash
|
|
188
120
|
bag erc8183 fetch <job_id>
|
|
189
121
|
```
|
|
190
122
|
|
|
191
|
-
Prints the `deliverable_url`. Fetch it yourself (e.g. via an IPFS gateway) to read
|
|
192
|
-
the `DeliverableManifest`: `{"chain_id", "contracts", "job_id", "response": {"content": ..., "content_type": ...}, "metadata": {...}}`.
|
|
123
|
+
Prints the `deliverable_url`. Fetch it yourself (e.g. via an IPFS gateway) to read the `DeliverableManifest`: `{"chain_id", "contracts", "job_id", "response": {"content": ..., "content_type": ...}, "metadata": {...}}`.
|
|
193
124
|
|
|
194
|
-
## Stage 6
|
|
125
|
+
## Stage 6 - Settle
|
|
195
126
|
|
|
196
127
|
```bash
|
|
197
128
|
# Happy path: accept the deliverable. NOTE: must wait 24h dispute_window first.
|
|
@@ -206,18 +137,16 @@ bag erc8183 settle <job_id> --action reject
|
|
|
206
137
|
|
|
207
138
|
Returns a tx hash. Verify on BscScan.
|
|
208
139
|
|
|
209
|
-
**On `--action approve` revert with `0x17be5b7b`**: chain refuses because
|
|
210
|
-
`submitted_at + dispute_window > now`. Either wait the remaining time or
|
|
211
|
-
use `--action dispute` for immediate effect.
|
|
140
|
+
**On `--action approve` revert with `0x17be5b7b`**: chain refuses because `submitted_at + dispute_window > now`. Either wait the remaining time or use `--action dispute` for immediate effect.
|
|
212
141
|
|
|
213
|
-
## Stage 7
|
|
142
|
+
## Stage 7 - Verify on-chain settlement
|
|
214
143
|
|
|
215
144
|
```bash
|
|
216
145
|
bag erc8183 status <job_id>
|
|
217
146
|
# Expect: status COMPLETED (after approve) or unchanged SUBMITTED (after dispute, until voters resolve)
|
|
218
147
|
```
|
|
219
148
|
|
|
220
|
-
Check seller's U balance increased (minus platform fee
|
|
149
|
+
Check seller's U balance increased (minus platform fee - see `CommerceClient.platform_fee_bp()`).
|
|
221
150
|
|
|
222
151
|
## End-to-end smoke (one-shot, for testing your setup)
|
|
223
152
|
|
|
@@ -232,12 +161,9 @@ bag erc8183 status $JOB
|
|
|
232
161
|
bag erc8183 settle $JOB --action dispute
|
|
233
162
|
```
|
|
234
163
|
|
|
235
|
-
For the FREE regression, change the budget to `0` and confirm the compatible
|
|
236
|
-
contract job still reaches `FUNDED` and `SUBMITTED` without an ERC-20 approval
|
|
237
|
-
or balance change.
|
|
164
|
+
For the FREE regression, change the budget to `0` and confirm the compatible contract job still reaches `FUNDED` and `SUBMITTED` without an ERC-20 approval or balance change.
|
|
238
165
|
|
|
239
|
-
This is the exact end-to-end flow used to validate real-chain buying. See the
|
|
240
|
-
references below for the canonical picture.
|
|
166
|
+
This is the exact end-to-end flow used to validate real-chain buying. See the references below for the canonical picture.
|
|
241
167
|
|
|
242
168
|
## Reference
|
|
243
169
|
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bnbagent-studio-extending-signing
|
|
3
|
-
description: When the user wants the agent's wallet to sign EIP-712 typed data for anything beyond BSC U-token transfers
|
|
3
|
+
description: When the user wants the agent's wallet to sign EIP-712 typed data for anything beyond BSC U-token transfers - e.g. integrate a new x402 service, custom commerce contract, or any TypedData primary type beyond TransferWithAuthorization/ReceiveWithAuthorization. Also covers diagnosing `PolicyViolation` / `X402PolicyError` at runtime and how to safely extend `[wallet.signing]` extra_domains / extra_primary_types in studio.toml.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
> **Reference file** of the `bnbagent-studio` router skill
|
|
6
|
+
> **Reference file** of the `bnbagent-studio` router skill - installed at `bnbagent-studio/references/` and loaded on demand (not a standalone skill). Route here via the router's decision tree.
|
|
7
7
|
|
|
8
8
|
# bnbagent-studio-extending-signing
|
|
9
9
|
|
|
10
|
-
Procedure for **extending the EIP-712 signing allowlist** in a bnbagent-studio project.
|
|
11
|
-
Audience: Claude Code helping a user who hit `PolicyViolation` or wants to integrate a non-default x402 service / custom contract.
|
|
10
|
+
Procedure for **extending the EIP-712 signing allowlist** in a bnbagent-studio project. Audience: Claude Code helping a user who hit `PolicyViolation` or wants to integrate a non-default x402 service / custom contract.
|
|
12
11
|
|
|
13
|
-
> **
|
|
14
|
-
> at `<workspace>/app/agent/src/signing.ts` and is called by the
|
|
15
|
-
> `SellerAgentExecutor` skills (`negotiate` / `notify_funded`) — it is **never** an
|
|
16
|
-
> LLM-callable tool. The `[wallet.signing]` config lives in
|
|
17
|
-
> `<workspace>/app/agent/studio.toml`. The workspace is a thin root plus the one
|
|
18
|
-
> `app/agent/` sub-project; every command and edit below targets `app/agent/`.
|
|
12
|
+
> **Current path note**: signing is an **Agent-only** concern. `signing.ts` lives at `<workspace>/app/agent/src/signing.ts` and is called by the `SellerAgentExecutor` skills (`negotiate` / `notify_funded`) - it is **never** an LLM-callable tool. The `[wallet.signing]` config lives in `<workspace>/app/agent/studio.toml`. The workspace is a thin root plus the one `app/agent/` sub-project; every command and edit below targets `app/agent/`.
|
|
19
13
|
|
|
20
14
|
**Different from**:
|
|
21
|
-
|
|
22
|
-
- `
|
|
23
|
-
-
|
|
15
|
+
|
|
16
|
+
- `-operating` - general ops & doctor / balance / job status
|
|
17
|
+
- `funding-pieverse-llm` - Pieverse LLM credit lifecycle (the money)
|
|
18
|
+
- `-adding-to-project` - adds capabilities (`bnbagent-sdk` modules)
|
|
24
19
|
|
|
25
20
|
This skill is specifically the **signing-permission** axis. Funding is "do I have the money"; signing policy is "is the wallet allowed to sign this".
|
|
26
21
|
|
|
@@ -29,7 +24,7 @@ This skill is specifically the **signing-permission** axis. Funding is "do I hav
|
|
|
29
24
|
Every EIP-712 signature goes through SDK `SigningPolicy.strictDefault()`. By default the wallet only signs:
|
|
30
25
|
|
|
31
26
|
| Allowed | Domain | Primary types |
|
|
32
|
-
|
|
27
|
+
| --- | --- | --- |
|
|
33
28
|
| U-token mainnet | chain `56`, `0xcE24439F2D9C6a2289F741120FE202248B666666` | `TransferWithAuthorization`, `ReceiveWithAuthorization` |
|
|
34
29
|
| U-token testnet | chain `97`, `0xc70B8741B8B07A6d61E54fd4B20f22Fa648E5565` | (same) |
|
|
35
30
|
|
|
@@ -38,40 +33,40 @@ Everything else (`Permit`, `PermitSingle`, `PermitBatch`, foreign contracts, for
|
|
|
38
33
|
Two layers wrap this:
|
|
39
34
|
|
|
40
35
|
| Layer | What it gates | Where configured | Can agent change at runtime? |
|
|
41
|
-
|
|
42
|
-
| L2
|
|
43
|
-
| L3
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| L2 - HTTP host | which servers the x402 buyer is allowed to fetch from | `[payments.x402].allowed_hosts` in `app/agent/studio.toml` | ⚠️ Only by editing toml + restart |
|
|
38
|
+
| L3 - SigningPolicy | which `(chain_id, contract)` + primary type the wallet will sign | `[wallet.signing]` in `app/agent/studio.toml` (additive over SDK strictDefault) | ⚠️ Only by editing toml + restart |
|
|
44
39
|
|
|
45
|
-
**Both layers must permit a request** for it to succeed. Adding only one half won't work
|
|
40
|
+
**Both layers must permit a request** for it to succeed. Adding only one half won't work - see Section A.
|
|
46
41
|
|
|
47
42
|
EIP-191 SIWE (the `pieverse_usage` MCP tool / `bag llm activate`) uses a **separate** hardcoded allowlist `{"llm.pieverse.io"}` and is not configurable.
|
|
48
43
|
|
|
49
44
|
## Quick triage decision tree
|
|
50
45
|
|
|
51
46
|
| User said... | Section |
|
|
52
|
-
|
|
53
|
-
| "buy X service via x402" (X is not Pieverse) | A
|
|
54
|
-
| "sign for custom commerce contract / order / quote" | B
|
|
55
|
-
| `PolicyViolation` / `X402PolicyError` at runtime | C
|
|
56
|
-
| "agent needs to approve unlimited USDC" / Permit / Permit2 | D
|
|
57
|
-
| "expand the 600s validity window" | E
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| "buy X service via x402" (X is not Pieverse) | A - new x402 service |
|
|
49
|
+
| "sign for custom commerce contract / order / quote" | B - custom primary type |
|
|
50
|
+
| `PolicyViolation` / `X402PolicyError` at runtime | C - diagnosing the rejection |
|
|
51
|
+
| "agent needs to approve unlimited USDC" / Permit / Permit2 | D - refuse the request |
|
|
52
|
+
| "expand the 600s validity window" | E - validity overrides |
|
|
58
53
|
|
|
59
54
|
## A. Integrate a new x402 service
|
|
60
55
|
|
|
61
56
|
The user wants the agent to pay e.g. `api.example.com` via x402.
|
|
62
57
|
|
|
63
|
-
**Pre-flight checks (must be true in
|
|
58
|
+
**Pre-flight checks (must be true in the current release; refuse and explain if not):**
|
|
64
59
|
|
|
65
|
-
1. The service accepts payment on **BSC chain 56 or 97**.
|
|
60
|
+
1. The service accepts payment on **BSC chain 56 or 97**. Current wallets are BSC-only; services on Base, Ethereum, or Polygon are unsupported.
|
|
66
61
|
2. The service uses **EIP-3009 `TransferWithAuthorization`** (or `ReceiveWithAuthorization`). If it uses Permit2 or a custom primary type → goes to Section B.
|
|
67
|
-
3. The token contract address on BSC is known and the user can confirm it. Don't guess from a token symbol
|
|
62
|
+
3. The token contract address on BSC is known and the user can confirm it. Don't guess from a token symbol - look up the deployed address.
|
|
68
63
|
|
|
69
|
-
If any of (1)
|
|
64
|
+
If any of (1)-(3) fail, **stop and tell the user**. The current release cannot integrate cross-chain services or non-EIP-3009 payment rails. Do not work around it; the SigningPolicy will refuse the request.
|
|
70
65
|
|
|
71
|
-
**Steps when (1)
|
|
66
|
+
**Steps when (1)-(3) all pass:**
|
|
72
67
|
|
|
73
68
|
```bash
|
|
74
|
-
# 1. Edit app/agent/studio.toml
|
|
69
|
+
# 1. Edit app/agent/studio.toml - both gates. `bag config` resolves the app/agent/
|
|
75
70
|
# sub-project automatically from the workspace root
|
|
76
71
|
# (there is NO --side flag); pass --project-root app/agent to be explicit.
|
|
77
72
|
bag config set payments.x402.allowed_hosts '["llm.pieverse.io", "api.example.com"]'
|
|
@@ -98,6 +93,7 @@ bag wallet policy show
|
|
|
98
93
|
```
|
|
99
94
|
|
|
100
95
|
**Hard rules**:
|
|
96
|
+
|
|
101
97
|
- Do not add a contract to `extra_domains` without verifying its address on a block explorer. The SigningPolicy treats `(chain_id, contract)` as a unit; the wrong address = signing into a phishing contract.
|
|
102
98
|
- Do not silently widen `daily_cap_usd` or `max_per_request_usd` to make the new service "fit". The caps are budget guardrails; surface the proposed values to the user before editing.
|
|
103
99
|
|
|
@@ -114,9 +110,10 @@ extra_domains = [
|
|
|
114
110
|
extra_primary_types = ["MyOrder", "BondQuote"]
|
|
115
111
|
```
|
|
116
112
|
|
|
117
|
-
**Both must be added**
|
|
113
|
+
**Both must be added** - adding a primary type without the matching domain is useless (the contract still gets rejected) and adding the domain without the type still rejects the signature (primary_type allowlist).
|
|
118
114
|
|
|
119
115
|
**Hard rules**:
|
|
116
|
+
|
|
120
117
|
- The SDK denylist (`Permit`, `PermitSingle`, `PermitBatch`) **always wins** over `extra_primary_types`. Listing them does not enable them.
|
|
121
118
|
- Custom primary types bypass studio's known semantic checks. Be extra careful that the typed data structure matches what the contract expects; an attacker controlling the contract or the LLM input can construct unfavorable messages.
|
|
122
119
|
|
|
@@ -130,7 +127,7 @@ PolicyViolation: unknown (chain_id, verifyingContract): (56, 0xAbc...)
|
|
|
130
127
|
→ Section A or B: add to [wallet.signing].extra_domains, then restart
|
|
131
128
|
|
|
132
129
|
PolicyViolation: primary type 'Permit' is denylisted
|
|
133
|
-
→ Section D: refuse
|
|
130
|
+
→ Section D: refuse - do not work around
|
|
134
131
|
|
|
135
132
|
PolicyViolation: primary type 'MyOrder' not in allowlist
|
|
136
133
|
→ Section B: add to extra_primary_types
|
|
@@ -138,14 +135,14 @@ PolicyViolation: primary type 'MyOrder' not in allowlist
|
|
|
138
135
|
PolicyViolation: validity window 7200s exceeds max 600
|
|
139
136
|
→ Section E: max_validity_window_seconds in [wallet.signing]
|
|
140
137
|
|
|
141
|
-
X402HostNotAllowedError: x402 buyer refusing 'api.example.com'
|
|
142
|
-
→ L2 HTTP gate (exact hosts only
|
|
138
|
+
X402HostNotAllowedError: x402 buyer refusing 'api.example.com' - not in allowed_hosts
|
|
139
|
+
→ L2 HTTP gate (exact hosts only - wildcard entries are not supported)
|
|
143
140
|
→ bag config set payments.x402.allowed_hosts '[..., "api.example.com"]'
|
|
144
141
|
→ (L3 may also need updating per Section A)
|
|
145
142
|
|
|
146
143
|
X402BudgetExhaustedError: x402 402 demands $1.50 > max_usd=$1.00
|
|
147
144
|
→ Budget cap; user must explicitly raise via studio.toml.
|
|
148
|
-
→ DON'T silently widen
|
|
145
|
+
→ DON'T silently widen - tell the user.
|
|
149
146
|
```
|
|
150
147
|
|
|
151
148
|
Confirm the current state before editing:
|
|
@@ -161,13 +158,14 @@ If the user (or, more dangerously, the LLM) asks the agent to sign `Permit`, `Pe
|
|
|
161
158
|
|
|
162
159
|
**Stop. Refuse. Explain why.**
|
|
163
160
|
|
|
164
|
-
These primary types grant unbounded ERC-20 spend authority to a third party. The denylist exists exactly to prevent LLMs being talked into signing one. Even if the user insists, the SDK will refuse with `PolicyViolation` and the message is correct
|
|
161
|
+
These primary types grant unbounded ERC-20 spend authority to a third party. The denylist exists exactly to prevent LLMs being talked into signing one. Even if the user insists, the SDK will refuse with `PolicyViolation` and the message is correct - do not bypass via `_DANGEROUS_signTypedDataNoPolicy`.
|
|
165
162
|
|
|
166
163
|
Acceptable alternatives:
|
|
167
|
-
- For paying a known service: use EIP-3009 `TransferWithAuthorization` (Section A — bounded amount, single-use, time-windowed)
|
|
168
|
-
- For session-scoped recurring payments: opt-in to `Permit2 PermitTransferFrom` (Section B — single-use, witness-bound; not Permit / PermitSingle / PermitBatch)
|
|
169
164
|
|
|
170
|
-
|
|
165
|
+
- For paying a known service: use EIP-3009 `TransferWithAuthorization` (Section A - bounded amount, single-use, time-windowed)
|
|
166
|
+
- For session-scoped recurring payments: opt-in to `Permit2 PermitTransferFrom` (Section B - single-use, witness-bound; not Permit / PermitSingle / PermitBatch)
|
|
167
|
+
|
|
168
|
+
If the user is integrating something that genuinely requires Permit, this is a v0.2+ decision and should go in an ADR - do not make the change ad-hoc.
|
|
171
169
|
|
|
172
170
|
## E. Widening the validity window
|
|
173
171
|
|
|
@@ -185,10 +183,10 @@ Use sparingly. A signed authorization that lives 30 minutes can be replayed by a
|
|
|
185
183
|
## F. Common refusal patterns (when user asks for something unsafe)
|
|
186
184
|
|
|
187
185
|
| User request | Why refuse |
|
|
188
|
-
|
|
186
|
+
| --- | --- |
|
|
189
187
|
| "Just turn off the signing policy" | The SigningPolicy L3 is the last-mile defense. Disabling it makes the wallet a blind-sign oracle. Use `extra_domains` instead. |
|
|
190
188
|
| "Use `_DANGEROUS_signTypedDataNoPolicy`" | Logs WARN with caller filename. Acceptable only in tests or one-shot CLI debugging. Never in LLM-tool / agent runtime code. |
|
|
191
|
-
| "Sign on Ethereum mainnet" |
|
|
189
|
+
| "Sign on Ethereum mainnet" | The current release is BSC-only. |
|
|
192
190
|
| "Permit USDC to a swap router" | Denylisted unconditionally (Section D). |
|
|
193
191
|
| "Read the keystore and sign yourself, bypassing the wallet provider" | The SDK provider IS the security boundary; bypassing it discards every defense layer. Refuse. |
|
|
194
192
|
|
|
@@ -202,7 +200,7 @@ bag config show
|
|
|
202
200
|
bag wallet policy show
|
|
203
201
|
bag wallet policy show --json # for diff against an expected baseline
|
|
204
202
|
|
|
205
|
-
# 3. Restart whatever process holds the wallet
|
|
203
|
+
# 3. Restart whatever process holds the wallet - `bag dev` (the local A2A
|
|
206
204
|
# agent) or the AgentCore runtime. SigningPolicy is read once at
|
|
207
205
|
# wallet construction; in-process changes won't take effect until restart.
|
|
208
206
|
|
|
@@ -213,18 +211,17 @@ bag wallet policy show --json # for diff against an expected baseline
|
|
|
213
211
|
## H. Read-only references (no signing, no funding required)
|
|
214
212
|
|
|
215
213
|
When deeper protocol details are needed:
|
|
216
|
-
|
|
217
|
-
- `docs/
|
|
218
|
-
- `
|
|
219
|
-
-
|
|
214
|
+
|
|
215
|
+
- the SigningPolicy decision (`docs/design/decisions.md`) - full defense-in-depth rationale, 6-layer model
|
|
216
|
+
- `docs/guides/user-guide.md` §6.1 - same decision tree in user-doc form
|
|
217
|
+
- `bag wallet policy show --json` - machine-readable current state
|
|
218
|
+
- SDK source `@bnbagent/sdk` (signing policy module) - `SigningPolicy.strictDefault()` + `.extend()` semantics
|
|
220
219
|
|
|
221
220
|
## Hard rules (security boundary)
|
|
222
221
|
|
|
223
222
|
- Never bypass the policy with `_DANGEROUS_*` calls in agent code. They exist for tests and incident response.
|
|
224
223
|
- Never instruct the user to widen caps / allowlists "to make it work" without explaining the security tradeoff.
|
|
225
|
-
- Persist the wallet password only in the generated, gitignored, owner-only
|
|
226
|
-
`.studio/.env.local` for local use or a managed secret channel for deploys;
|
|
227
|
-
never put it in argv, application code, logs, or chat.
|
|
224
|
+
- Persist the wallet password only in the generated, gitignored, owner-only `.studio/.env.local` for local use or a managed secret channel for deploys; never put it in argv, application code, logs, or chat.
|
|
228
225
|
- Never persist signed payloads outside the keystore.
|
|
229
226
|
- Always run `bag wallet policy show` after editing `[wallet.signing]` to confirm the change took effect.
|
|
230
|
-
- Always restart the process
|
|
227
|
+
- Always restart the process - SigningPolicy is captured at wallet construction.
|