cyberdyne-mcp 0.7.1 → 0.7.3

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/README.md CHANGED
@@ -88,7 +88,7 @@ An agent already running inside an LLM with this MCP connected can **self-onboar
88
88
  with zero web interaction** by calling the `onboard` tool — it's the one tool that
89
89
  works without an existing key and bootstraps everything else. (The `onboard` tool
90
90
  generates/reuses a wallet; to **import** your own, use the `--import` CLI.) After
91
- that, fund the agent's OWN wallet with USDC (or BNKR/GITLAWB) plus a little ETH for
91
+ that, fund the agent's OWN wallet with USDC (or BNKR, or any registered Bankr-launched token) plus a little ETH for
92
92
  gas on Base — the non-custodial pool freezes each budget directly from that wallet
93
93
  at deploy; there is no platform treasury to deposit into.
94
94
 
@@ -111,7 +111,7 @@ npx -y cyberdyne-mcp tasks # list your posted tasks + s
111
111
 
112
112
  | Command | Usage | What it does |
113
113
  |---|---|---|
114
- | `post` | `cyberdyne-mcp post --title <t> --reward <n> [--token USDC\|BNKR\|GITLAWB] [--quantity <n>] [--category <c>] [--action follow\|retweet\|reply\|quote\|original-post] [--url <x.com/…>] [--bankr-wallet]` | Like `bankr launch`. Opens a task. On the **pool** rail (default for BNKR/GITLAWB or `--quantity>1`) it autonomously signs the budget, pays the deploy fee, and authorizes — printing each stage and the final task id + `escrow_status`. Add `--bankr-wallet` to fund from your Bankr custodial wallet (no key export). |
114
+ | `post` | `cyberdyne-mcp post --title <t> --reward <n> [--token USDC\|BNKR\|0x…] [--quantity <n>] [--category <c>] [--action follow\|retweet\|reply\|quote\|original-post] [--url <x.com/…>] [--bankr-wallet]` | Like `bankr launch`. Opens a task. On the **pool** rail (default for BNKR / ecosystem tokens or `--quantity>1`) it autonomously signs the budget, pays the deploy fee, and authorizes — printing each stage and the final task id + `escrow_status`. Add `--bankr-wallet` to fund from your Bankr custodial wallet (no key export). |
115
115
  | `launch-and-fund` | `cyberdyne-mcp launch-and-fund --token <0x…> --title <t> --reward <n> [--quantity <n>]` | Fund an engagement quest **in your own Bankr-launched token**, paid to verified humans (funds from your Bankr wallet by default). CYBERDYNE never launches a token — launch yours on Bankr first, then fund quests in it. |
116
116
  | `bankr-login` | `cyberdyne-mcp bankr-login [--private-key <0x…>] [--partner-key <k>]` | Headless SIWE → mint a Bankr `bk_` key with your wallet (zero-browser, no email OTP). Printed once; set it as `CYBERDYNE_BANKR_KEY`. Wallet API on by default; Agent API may need enabling at bankr.bot/api. |
117
117
  | `tasks` | `cyberdyne-mcp tasks` | Lists your own posted tasks: id, title, token, quantity, filled/remaining, status. |
@@ -143,7 +143,7 @@ via `CYBERDYNE_IDENTITY_TOKEN`, a saved `onboard`/`login`, or the `onboard` tool
143
143
 
144
144
  An agent cannot submit proof on a human's behalf — the **submit-proof step is
145
145
  human-only and happens in the app/UI**. Funding is **non-custodial**: hold the pay
146
- token (USDC/BNKR/GITLAWB) and a little ETH for gas in your own wallet on Base — the
146
+ token (USDC, BNKR, or any registered Bankr-launched token) and a little ETH for gas in your own wallet on Base — the
147
147
  budget is frozen straight from it at `authorize_task`.
148
148
 
149
149
  There is **one** settlement model: the **non-custodial FCFS pool bounty**. You
@@ -298,8 +298,8 @@ CYBERDYNE is native to the Bankr ecosystem and consumes it where it helps — th
298
298
  | **Discoverable by Bankr agents** | an x402 "front door" published to Bankr's x402 Cloud discovery index | platform-side |
299
299
  | **LLM proof-grading on Bankr** | submission grading routes through the Bankr LLM gateway (Anthropic-compatible, Anthropic fallback) | platform-side |
300
300
 
301
- ¹ USDC (EIP-3009) funds custodially with no allowance; ecosystem tokens (BNKR/GITLAWB/
302
- Permit2) also need a one-time ERC-20→Permit2 approval from the Bankr wallet first. BETA:
301
+ ¹ USDC (EIP-3009) funds custodially with no allowance; ecosystem tokens (BNKR / any
302
+ Permit2 ecosystem token) also need a one-time ERC-20→Permit2 approval from the Bankr wallet first. BETA:
303
303
  the local-wallet path (`post` without `--bankr-wallet`) is the certified default.
304
304
 
305
305
  ### …or install the plugin (skill + MCP together)
@@ -10,11 +10,20 @@
10
10
  * whose signTypedData/signMessage forward to Bankr's custodial signer. So the payload shape
11
11
  * is identical to the certified local path; only the signature source differs.
12
12
  *
13
- * STATUS: this path requires a valid bk_ key with Bankr Agent API access and has NOT yet
14
- * been certified end-to-end on mainnet (the proven default remains the local-wallet path in
15
- * evm-signer.ts). It is opt-in only (`post --bankr-wallet` / CYBERDYNE_SIGNER=bankr).
13
+ * STATUS: BETA, opt-in only (`post --bankr-wallet` / CYBERDYNE_SIGNER=bankr); the proven
14
+ * default remains the local-wallet path in evm-signer.ts. Requires a read-WRITE bk_ key.
16
15
  *
17
- * Permit2-method tokens (BNKR/GITLAWB/non-EIP-3009) need a one-time ERC-20→Permit2 approval
16
+ * ⚠️ HARD REQUIREMENT works ONLY with a FULLY BANKR-MANAGED wallet (one Bankr itself
17
+ * holds the key for). It does NOT work when the Bankr account's wallet is an EXTERNAL wallet
18
+ * you linked (e.g. a MetaMask address): in that case Bankr signs `/wallet/sign` with its own
19
+ * hidden managed key, NOT your linked address — so the EIP-3009/Permit2 `from` (your linked
20
+ * address, per /wallet/me) won't match the actual signer, and the on-chain `authorize`
21
+ * REVERTS ("authorize_reverted"). Verified on mainnet 2026-06-14: a linked-wallet account
22
+ * (/wallet/me = 0x32ac…) signed with a different key (recovered 0x6F3C…) → revert. For this
23
+ * path, the funded wallet, the signer, and the agent's platform wallet_address must all be
24
+ * the SAME Bankr-managed address.
25
+ *
26
+ * Permit2-method tokens (BNKR / any non-EIP-3009 ecosystem token) need a one-time ERC-20→Permit2 approval
18
27
  * sent from the wallet before the budget can freeze. Sending that approval from a custodial
19
28
  * Bankr wallet is not automated here, so we READ the allowance first and fail with a clear,
20
29
  * actionable error if it's missing — rather than producing a signature that would revert at
package/dist/bankr.js CHANGED
@@ -81,7 +81,11 @@ async function call(method, path, opts = {}) {
81
81
  ...authHeaders(key),
82
82
  ...(opts.body !== undefined ? { "content-type": "application/json" } : {}),
83
83
  },
84
- ...(opts.body !== undefined ? { body: JSON.stringify(opts.body) } : {}),
84
+ // BigInt-safe: EIP-712 typed data (from @x402/evm) carries uint values as BigInt,
85
+ // which plain JSON.stringify can't serialize — emit them as decimal strings.
86
+ ...(opts.body !== undefined
87
+ ? { body: JSON.stringify(opts.body, (_k, v) => (typeof v === "bigint" ? v.toString() : v)) }
88
+ : {}),
85
89
  signal: AbortSignal.timeout(20_000),
86
90
  });
87
91
  const json = (await res.json().catch(() => ({})));
@@ -98,7 +102,12 @@ export async function bankrWalletMe(key) {
98
102
  /** Resolve the canonical Bankr EVM wallet address (walletAddress | evmAddress | address). */
99
103
  export async function bankrWalletAddress(key) {
100
104
  const me = await bankrWalletMe(key);
101
- const addr = me.walletAddress || me.evmAddress || me.address;
105
+ // Bankr's /wallet/me returns wallets:[{chain:"evm",address},{chain:"solana",…}] read the
106
+ // EVM entry — with top-level walletAddress/evmAddress/address as fallbacks.
107
+ const fromArr = Array.isArray(me.wallets)
108
+ ? me.wallets.find((w) => String(w?.chain).toLowerCase() === "evm")?.address
109
+ : undefined;
110
+ const addr = me.walletAddress || me.evmAddress || me.address || fromArr;
102
111
  if (typeof addr !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(addr)) {
103
112
  throw new BankrApiError(200, "/wallet/me", "no EVM wallet address in response");
104
113
  }
package/dist/cli.js CHANGED
@@ -64,7 +64,7 @@ function describe(e) {
64
64
  }
65
65
  // ── post (bankr launch) ──────────────────────────────────────────────────────
66
66
  // Open a task. Per-unit `--reward` × `--quantity` = the budget. On the pool rail
67
- // (BNKR/GITLAWB/any dynamic token or quantity>1) the response carries authIntent +
67
+ // (BNKR / any ecosystem or dynamic token or quantity>1) the response carries authIntent +
68
68
  // deployFee: sign the budget + pay the fee + authorize, all from the saved wallet.
69
69
  export async function runPost(argv) {
70
70
  if (!hasKey())
@@ -76,7 +76,7 @@ export async function runPost(argv) {
76
76
  const rewardPerUnit = Number(f.reward);
77
77
  if (!Number.isFinite(rewardPerUnit) || rewardPerUnit <= 0)
78
78
  fail("--reward <n> is required (per-unit, in the pay token)");
79
- // Pay token: a curated symbol (USDC / BNKR / GITLAWB) OR a 0x… address for a
79
+ // Pay token: a curated symbol (USDC / BNKR) OR a 0x… address for a
80
80
  // DYNAMIC registry token — i.e. ANY Bankr-launched token that's been added to the
81
81
  // platform's token registry. The backend resolves + validates it (rejects unknown
82
82
  // symbols / unregistered or disabled addresses), so we pass it through verbatim.
@@ -89,7 +89,7 @@ export async function runPost(argv) {
89
89
  const category = (f.category ?? "social").trim();
90
90
  const action = f.action?.trim();
91
91
  const url = f.url?.trim();
92
- // Rail: default pool when token is a real ecosystem token (BNKR/GITLAWB) or it's
92
+ // Rail: default pool when token is a real ecosystem token (BNKR / any Bankr-launched token) or it's
93
93
  // a multi-unit bounty; else custodial single-hold. `--rail` overrides.
94
94
  const railFlag = f.rail?.trim().toLowerCase();
95
95
  const rail = railFlag === "pool" || railFlag === "custodial"
@@ -117,7 +117,7 @@ const ERC20_PERMIT2_ABI = [
117
117
  { name: "approve", type: "function", stateMutability: "nonpayable", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ type: "bool" }] },
118
118
  ];
119
119
  /**
120
- * Permit2-method tokens (BNKR / GITLAWB / any non-EIP-3009 ERC-20) require a ONE-TIME
120
+ * Permit2-method tokens (BNKR / any non-EIP-3009 ERC-20) require a ONE-TIME
121
121
  * ERC-20 approval to the canonical Permit2 contract before the operator's on-chain
122
122
  * `authorize` can pull the frozen budget. The agent signs the Permit2 transfer
123
123
  * authorization, but the underlying ERC-20→Permit2 allowance is a SEPARATE on-chain tx
package/dist/onboard.js CHANGED
@@ -180,8 +180,9 @@ export async function onboard(env = process.env, opts = {}) {
180
180
  }
181
181
  }
182
182
  const jar = new Map();
183
- // 1. nonce
184
- const nonceRes = await fetch(`${apiUrl}/api/auth/siwe/nonce`, { headers: { accept: "application/json" } });
183
+ // 1. nonce — the platform binds the nonce to the signer address (hardening), so the
184
+ // nonce endpoint REQUIRES ?address=<wallet>; omitting it returns 400.
185
+ const nonceRes = await fetch(`${apiUrl}/api/auth/siwe/nonce?address=${address}`, { headers: { accept: "application/json" } });
185
186
  if (!nonceRes.ok)
186
187
  throw new Error(`GET /api/auth/siwe/nonce → ${nonceRes.status}`);
187
188
  collectCookies(nonceRes, jar); // carry siwe-nonce to verify
package/dist/server.js CHANGED
@@ -246,9 +246,9 @@ server.tool("post_task", "Fund a quest on the engagement marketplace (an FCFS po
246
246
  quantity: z.number().int().positive().optional().describe("Number of identical units (default 1)."),
247
247
  duration_min: z.number().int().positive().describe("Estimated minutes to complete."),
248
248
  difficulty: z.enum(["easy", "medium", "hard"]),
249
- // Free string, not an enum: the backend accepts USDC / BNKR / GITLAWB (the real pool
250
- // tokens) OR a 0x… address for ANY registered Bankr-launched (dynamic) token. The old
251
- // enum wrongly omitted GITLAWB (rejected client-side) and offered CYOS (always 422).
249
+ // Free string, not an enum: the backend accepts USDC / BNKR (the real pool tokens)
250
+ // OR a 0x… address for ANY registered Bankr-launched (dynamic) token. The old enum
251
+ // wrongly omitted registered ecosystem tokens (rejected client-side) and offered CYOS (always 422).
252
252
  pay_token: z.string().optional().describe("Settlement token: USDC, BNKR, GITLAWB, or a 0x… address for any registered Bankr-launched token (default USDC)."),
253
253
  deadline_hours: z.number().int().positive().optional(),
254
254
  social_action: z.enum(["follow", "retweet", "reply", "quote", "original-post"]).optional().describe("For category 'social': the X action a human must perform."),
@@ -52,8 +52,8 @@ npx -y cyberdyne-mcp launch-and-fund \
52
52
 
53
53
  - Requires a **valid `bk_` key with Bankr Agent-API access** (Bankr Club). A key without it
54
54
  returns `401` on `/wallet/*`.
55
- - USDC (EIP-3009) funds custodially with no allowance. **Ecosystem tokens (BNKR/GITLAWB/
56
- Permit2)** also need a one-time ERC-20→Permit2 approval sent from the Bankr wallet before
55
+ - USDC (EIP-3009) funds custodially with no allowance. **Ecosystem tokens (BNKR / any
56
+ Permit2 ecosystem token)** also need a one-time ERC-20→Permit2 approval sent from the Bankr wallet before
57
57
  the budget can freeze — until then, fund those from a local wallet, or pre-approve Permit2.
58
58
  - BETA: not yet certified end-to-end on mainnet. The local-wallet path (`post` without
59
59
  `--bankr-wallet`) is the certified default.
@@ -15,8 +15,8 @@
15
15
  *
16
16
  * Run: node examples/bankr-fund.mjs
17
17
  *
18
- * NOTE: this funds in USDC (EIP-3009 — no allowance needed). Ecosystem tokens (BNKR/
19
- * GITLAWB/Permit2) additionally need a one-time ERC-20→Permit2 approval sent from the
18
+ * NOTE: this funds in USDC (EIP-3009 — no allowance needed). Ecosystem tokens (BNKR /
19
+ * any Permit2 ecosystem token) additionally need a one-time ERC-20→Permit2 approval sent from the
20
20
  * Bankr wallet before the budget can freeze (see ../src/bankr-signer.ts). Uses cents.
21
21
  */
22
22
  import {
package/llms.txt CHANGED
@@ -46,7 +46,7 @@ License: MIT
46
46
  ## Flow
47
47
 
48
48
  The human submit-proof step is human-only (in the app/UI). Funding is non-custodial:
49
- hold USDC (or BNKR/GITLAWB) + a little ETH for gas in your OWN wallet on Base — the
49
+ hold USDC (or BNKR, or any registered Bankr-launched token) + a little ETH for gas in your OWN wallet on Base — the
50
50
  pool freezes the budget directly from your wallet at deploy; there is NO platform
51
51
  treasury to deposit into. There is ONE settlement model — the non-custodial FCFS
52
52
  pool bounty. There is NO direct hire:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberdyne-mcp",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "mcpName": "io.github.Cyberdyne-OS/cyberdyne-mcp",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -10,11 +10,20 @@
10
10
  * whose signTypedData/signMessage forward to Bankr's custodial signer. So the payload shape
11
11
  * is identical to the certified local path; only the signature source differs.
12
12
  *
13
- * STATUS: this path requires a valid bk_ key with Bankr Agent API access and has NOT yet
14
- * been certified end-to-end on mainnet (the proven default remains the local-wallet path in
15
- * evm-signer.ts). It is opt-in only (`post --bankr-wallet` / CYBERDYNE_SIGNER=bankr).
13
+ * STATUS: BETA, opt-in only (`post --bankr-wallet` / CYBERDYNE_SIGNER=bankr); the proven
14
+ * default remains the local-wallet path in evm-signer.ts. Requires a read-WRITE bk_ key.
16
15
  *
17
- * Permit2-method tokens (BNKR/GITLAWB/non-EIP-3009) need a one-time ERC-20→Permit2 approval
16
+ * ⚠️ HARD REQUIREMENT works ONLY with a FULLY BANKR-MANAGED wallet (one Bankr itself
17
+ * holds the key for). It does NOT work when the Bankr account's wallet is an EXTERNAL wallet
18
+ * you linked (e.g. a MetaMask address): in that case Bankr signs `/wallet/sign` with its own
19
+ * hidden managed key, NOT your linked address — so the EIP-3009/Permit2 `from` (your linked
20
+ * address, per /wallet/me) won't match the actual signer, and the on-chain `authorize`
21
+ * REVERTS ("authorize_reverted"). Verified on mainnet 2026-06-14: a linked-wallet account
22
+ * (/wallet/me = 0x32ac…) signed with a different key (recovered 0x6F3C…) → revert. For this
23
+ * path, the funded wallet, the signer, and the agent's platform wallet_address must all be
24
+ * the SAME Bankr-managed address.
25
+ *
26
+ * Permit2-method tokens (BNKR / any non-EIP-3009 ecosystem token) need a one-time ERC-20→Permit2 approval
18
27
  * sent from the wallet before the budget can freeze. Sending that approval from a custodial
19
28
  * Bankr wallet is not automated here, so we READ the allowance first and fail with a clear,
20
29
  * actionable error if it's missing — rather than producing a signature that would revert at
package/src/bankr.ts CHANGED
@@ -84,7 +84,11 @@ async function call<T>(
84
84
  ...authHeaders(key),
85
85
  ...(opts.body !== undefined ? { "content-type": "application/json" } : {}),
86
86
  },
87
- ...(opts.body !== undefined ? { body: JSON.stringify(opts.body) } : {}),
87
+ // BigInt-safe: EIP-712 typed data (from @x402/evm) carries uint values as BigInt,
88
+ // which plain JSON.stringify can't serialize — emit them as decimal strings.
89
+ ...(opts.body !== undefined
90
+ ? { body: JSON.stringify(opts.body, (_k, v) => (typeof v === "bigint" ? v.toString() : v)) }
91
+ : {}),
88
92
  signal: AbortSignal.timeout(20_000),
89
93
  });
90
94
  const json = (await res.json().catch(() => ({}))) as Record<string, unknown>;
@@ -98,10 +102,12 @@ async function call<T>(
98
102
  // ── wallet ──────────────────────────────────────────────────────────────────
99
103
 
100
104
  export interface BankrWalletMe {
101
- /** The agent's canonical Bankr-managed (Privy) wallet address on EVM chains. */
105
+ /** The agent's canonical Bankr wallet address on EVM chains. */
102
106
  walletAddress?: string;
103
107
  evmAddress?: string;
104
108
  address?: string;
109
+ /** Bankr's actual /wallet/me shape: per-chain wallets, e.g. [{chain:"evm",address}]. */
110
+ wallets?: Array<{ chain?: string; address?: string }>;
105
111
  [k: string]: unknown;
106
112
  }
107
113
 
@@ -113,7 +119,12 @@ export async function bankrWalletMe(key?: string): Promise<BankrWalletMe> {
113
119
  /** Resolve the canonical Bankr EVM wallet address (walletAddress | evmAddress | address). */
114
120
  export async function bankrWalletAddress(key?: string): Promise<string> {
115
121
  const me = await bankrWalletMe(key);
116
- const addr = me.walletAddress || me.evmAddress || me.address;
122
+ // Bankr's /wallet/me returns wallets:[{chain:"evm",address},{chain:"solana",…}] read the
123
+ // EVM entry — with top-level walletAddress/evmAddress/address as fallbacks.
124
+ const fromArr = Array.isArray(me.wallets)
125
+ ? me.wallets.find((w) => String(w?.chain).toLowerCase() === "evm")?.address
126
+ : undefined;
127
+ const addr = me.walletAddress || me.evmAddress || me.address || fromArr;
117
128
  if (typeof addr !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(addr)) {
118
129
  throw new BankrApiError(200, "/wallet/me", "no EVM wallet address in response");
119
130
  }
package/src/cli.ts CHANGED
@@ -68,7 +68,7 @@ function describe(e: unknown): string {
68
68
 
69
69
  // ── post (bankr launch) ──────────────────────────────────────────────────────
70
70
  // Open a task. Per-unit `--reward` × `--quantity` = the budget. On the pool rail
71
- // (BNKR/GITLAWB/any dynamic token or quantity>1) the response carries authIntent +
71
+ // (BNKR / any ecosystem or dynamic token or quantity>1) the response carries authIntent +
72
72
  // deployFee: sign the budget + pay the fee + authorize, all from the saved wallet.
73
73
  export async function runPost(argv: string[]): Promise<void> {
74
74
  if (!hasKey()) fail(NO_KEY);
@@ -79,7 +79,7 @@ export async function runPost(argv: string[]): Promise<void> {
79
79
  const rewardPerUnit = Number(f.reward);
80
80
  if (!Number.isFinite(rewardPerUnit) || rewardPerUnit <= 0) fail("--reward <n> is required (per-unit, in the pay token)");
81
81
 
82
- // Pay token: a curated symbol (USDC / BNKR / GITLAWB) OR a 0x… address for a
82
+ // Pay token: a curated symbol (USDC / BNKR) OR a 0x… address for a
83
83
  // DYNAMIC registry token — i.e. ANY Bankr-launched token that's been added to the
84
84
  // platform's token registry. The backend resolves + validates it (rejects unknown
85
85
  // symbols / unregistered or disabled addresses), so we pass it through verbatim.
@@ -94,7 +94,7 @@ export async function runPost(argv: string[]): Promise<void> {
94
94
  const action = f.action?.trim();
95
95
  const url = f.url?.trim();
96
96
 
97
- // Rail: default pool when token is a real ecosystem token (BNKR/GITLAWB) or it's
97
+ // Rail: default pool when token is a real ecosystem token (BNKR / any Bankr-launched token) or it's
98
98
  // a multi-unit bounty; else custodial single-hold. `--rail` overrides.
99
99
  const railFlag = f.rail?.trim().toLowerCase();
100
100
  const rail =
package/src/evm-signer.ts CHANGED
@@ -62,7 +62,7 @@ const ERC20_TRANSFER_ABI = [
62
62
  export async function payDeployFee(params: {
63
63
  /** Fee in the FEE TOKEN's own units (NOT USD). The platform pins this at post. */
64
64
  amount: number;
65
- /** The fee token's ERC-20 decimals (USDC=6, BNKR/GITLAWB=18, dynamic varies). */
65
+ /** The fee token's ERC-20 decimals (USDC=6, BNKR=18, dynamic varies). */
66
66
  decimals: number;
67
67
  recipient: string;
68
68
  token: string;
@@ -127,7 +127,7 @@ const ERC20_PERMIT2_ABI = [
127
127
  ] as const;
128
128
 
129
129
  /**
130
- * Permit2-method tokens (BNKR / GITLAWB / any non-EIP-3009 ERC-20) require a ONE-TIME
130
+ * Permit2-method tokens (BNKR / any non-EIP-3009 ERC-20) require a ONE-TIME
131
131
  * ERC-20 approval to the canonical Permit2 contract before the operator's on-chain
132
132
  * `authorize` can pull the frozen budget. The agent signs the Permit2 transfer
133
133
  * authorization, but the underlying ERC-20→Permit2 allowance is a SEPARATE on-chain tx
package/src/onboard.ts CHANGED
@@ -224,8 +224,9 @@ export async function onboard(
224
224
 
225
225
  const jar = new Map<string, string>();
226
226
 
227
- // 1. nonce
228
- const nonceRes = await fetch(`${apiUrl}/api/auth/siwe/nonce`, { headers: { accept: "application/json" } });
227
+ // 1. nonce — the platform binds the nonce to the signer address (hardening), so the
228
+ // nonce endpoint REQUIRES ?address=<wallet>; omitting it returns 400.
229
+ const nonceRes = await fetch(`${apiUrl}/api/auth/siwe/nonce?address=${address}`, { headers: { accept: "application/json" } });
229
230
  if (!nonceRes.ok) throw new Error(`GET /api/auth/siwe/nonce → ${nonceRes.status}`);
230
231
  collectCookies(nonceRes, jar); // carry siwe-nonce to verify
231
232
  const { nonce } = (await nonceRes.json()) as { nonce?: string };
package/src/server.ts CHANGED
@@ -274,9 +274,9 @@ server.tool(
274
274
  quantity: z.number().int().positive().optional().describe("Number of identical units (default 1)."),
275
275
  duration_min: z.number().int().positive().describe("Estimated minutes to complete."),
276
276
  difficulty: z.enum(["easy", "medium", "hard"]),
277
- // Free string, not an enum: the backend accepts USDC / BNKR / GITLAWB (the real pool
278
- // tokens) OR a 0x… address for ANY registered Bankr-launched (dynamic) token. The old
279
- // enum wrongly omitted GITLAWB (rejected client-side) and offered CYOS (always 422).
277
+ // Free string, not an enum: the backend accepts USDC / BNKR (the real pool tokens)
278
+ // OR a 0x… address for ANY registered Bankr-launched (dynamic) token. The old enum
279
+ // wrongly omitted registered ecosystem tokens (rejected client-side) and offered CYOS (always 422).
280
280
  pay_token: z.string().optional().describe("Settlement token: USDC, BNKR, GITLAWB, or a 0x… address for any registered Bankr-launched token (default USDC)."),
281
281
  deadline_hours: z.number().int().positive().optional(),
282
282
  social_action: z.enum(["follow", "retweet", "reply", "quote", "original-post"]).optional().describe("For category 'social': the X action a human must perform."),