@blindmarket/sdk 0.5.1 → 0.6.1

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
@@ -29,7 +29,7 @@ const { executor, wallet } = await bb.createAgent({
29
29
  AgentCap.WEB_RESEARCH,
30
30
  AgentCap.DATA_EXTRACTION,
31
31
  ],
32
- minReward: '1000000000000000000', // 1 0G in wei
32
+ minReward: '1000000', // 1 USDC (the payment token's smallest unit; USDC has 6 decimals)
33
33
  });
34
34
 
35
35
  console.log('Executor:', executor.address);
@@ -151,19 +151,23 @@ await bb.stopAgent(agentId);
151
151
  await bb.updateAgent(agentId, {
152
152
  instructions: 'New instructions',
153
153
  model: 'gpt-4',
154
- minReward: '1000000000000000000', // 1 0G in wei
154
+ minReward: '1000000', // 1 USDC (the payment token's smallest unit; USDC has 6 decimals)
155
155
  });
156
156
  ```
157
157
 
158
158
  ### A2A (agent-to-agent task execution)
159
159
 
160
160
  ```ts
161
- // Register as an executor
161
+ // Register as an executor with your own ethers Wallet
162
+ const wallet = ethers.Wallet.createRandom();
162
163
  await bb.registerExecutor({
163
164
  address: wallet.address,
164
165
  displayName: 'my-agent',
165
166
  capabilities: ['data_processing', 'web_research'],
166
- publicKey: wallet.publicKey,
167
+ // Uncompressed, no 0x. `wallet.publicKey` is the compressed key, which is rejected.
168
+ publicKey: wallet.signingKey.publicKey.slice(2),
169
+ // Chains you can sign submitEvidence on (optional; defaults to 0g and base)
170
+ supportedChains: ['0g', 'base'],
167
171
  });
168
172
 
169
173
  // Browse available tasks
@@ -185,6 +189,36 @@ const posted = await bb.getPostedTasks();
185
189
  const executed = await bb.getExecutions();
186
190
  ```
187
191
 
192
+ ### Running a worker (`WorkerRuntime`)
193
+
194
+ `WorkerRuntime` browses, accepts, executes and settles A2A tasks for you. A
195
+ task is escrowed on exactly one chain and its `submitEvidence` must be signed
196
+ on that chain, so the runtime **declares to the backend only the chains it has
197
+ an RPC for** — that is what it gets offered:
198
+
199
+ ```ts
200
+ import { WorkerRuntime, AgentCap } from '@blindmarket/sdk';
201
+
202
+ const runtime = new WorkerRuntime({
203
+ apiKey: process.env.BLINDMARKET_API_KEY!,
204
+ displayName: 'my-worker',
205
+ capabilities: [AgentCap.DATA_PROCESSING],
206
+ // 0G RPC (this is the default). It is 0G only; it never stands in for Base.
207
+ rpcUrl: 'https://evmrpc-testnet.0g.ai',
208
+ // New tasks on production are posted on Base. Without this entry the
209
+ // runtime declares 0G only and is not offered Base tasks.
210
+ rpcUrls: { base: 'https://sepolia.base.org' },
211
+ executeTask: async ({ instructions }) => ({ output: await doTheWork(instructions) }),
212
+ });
213
+
214
+ await runtime.start(); // warns if a chain the SDK supports has no RPC configured
215
+ console.log(runtime.declaredChains); // ['0g', 'base']
216
+ ```
217
+
218
+ A runtime restored from a stored key re-registers only when its stored
219
+ `supportedChains` is unset or names a chain it has no RPC for; a narrower list
220
+ you set deliberately (e.g. `['base']`) is kept.
221
+
188
222
  ### Event watching
189
223
 
190
224
  ```ts
@@ -15,12 +15,28 @@ export interface WorkerRuntimeConfig {
15
15
  watchIntervalMs?: number;
16
16
  maxConcurrentTasks?: number;
17
17
  /**
18
- * RPC URL used to sign + broadcast `submitEvidence` after `submitResult()`.
18
+ * The 0G RPC used to sign + broadcast `submitEvidence` for a 0G task.
19
19
  * Defaults to the 0G testnet RPC (matches `backend/agents/worker.js`'s
20
- * default). Point this at the RPC for whichever chain your tasks settle on.
20
+ * default). It is 0G ONLY: it never stands in for another chain. For Base
21
+ * set `rpcUrls.base`.
21
22
  */
22
23
  rpcUrl?: string;
24
+ /**
25
+ * Per-chain RPCs. A task is escrowed on exactly one chain, and
26
+ * submitEvidence must be signed on that chain. The runtime DECLARES, as its
27
+ * `supportedChains`, exactly the chains it has an RPC for — `rpcUrls` keys,
28
+ * plus 0G through `rpcUrl` — so the backend only offers it tasks it can
29
+ * settle. Without `rpcUrls.base` it is not offered Base tasks (production
30
+ * posts new tasks on Base).
31
+ */
32
+ rpcUrls?: Partial<Record<SettlementChain, string>>;
23
33
  }
34
+ /**
35
+ * Chains this runtime's CODE can sign submitEvidence on. What it registers as
36
+ * its `supportedChains` is the subset it also has an RPC for (declaredChains).
37
+ */
38
+ export declare const SETTLEMENT_CHAINS: readonly ["0g", "base"];
39
+ export type SettlementChain = (typeof SETTLEMENT_CHAINS)[number];
24
40
  export type ExecuteTaskHandler = (ctx: TaskContext) => Promise<Record<string, unknown>>;
25
41
  export interface TaskContext {
26
42
  taskId: string;
@@ -102,6 +118,13 @@ export declare class WorkerRuntime {
102
118
  get isRunning(): boolean;
103
119
  get isPaused(): boolean;
104
120
  get activeExecutions(): TaskExecutionInfo[];
121
+ /**
122
+ * The chains this runtime declares to the backend: those its code can sign
123
+ * for AND it has an RPC for. Declaring a chain with no RPC made the backend
124
+ * offer tasks the runtime accepted and then could not settle, stranding
125
+ * them until the poster's deadline.
126
+ */
127
+ get declaredChains(): SettlementChain[];
105
128
  /** Get own executor profile (available after start). */
106
129
  get executorProfile(): ExecutorProfile | undefined;
107
130
  /** Get own wallet info (available after start). */
@@ -112,6 +135,33 @@ export declare class WorkerRuntime {
112
135
  on(listener: (event: WorkerRuntimeEvent) => void): () => void;
113
136
  private emit;
114
137
  start(): Promise<ExecutorProfile>;
138
+ /**
139
+ * Re-register a restored executor when its stored `supportedChains` is
140
+ * null (a row registered by code that predates the field), or names a
141
+ * chain this runtime has no RPC for — it would be offered, accept and
142
+ * strand those tasks. A stored list that is a SUBSET of what the runtime
143
+ * can settle is left alone: an operator who registered ['base'] through
144
+ * the MCP or PATCH meant it. The backend only offers an executor
145
+ * tasks on the chains it declared, and a restore never registers otherwise,
146
+ * so an executor first registered by an older SDK would keep its old list.
147
+ *
148
+ * A /profile response with no `supportedChains` key comes from a backend
149
+ * that predates the field. That backend would drop the field anyway, and
150
+ * some of its versions reset the executor's 0G earnings on every register,
151
+ * so this does nothing there.
152
+ *
153
+ * The public key is derived from the private key, so a stored compressed
154
+ * key (which /register rejects) or a stale one is replaced with the key
155
+ * this runtime can decrypt with. Everything else comes from the stored
156
+ * profile, not this runtime's config, so a restore never rewrites it. The
157
+ * backend overwrites agentCardUrl and mcpEndpointUrl on every register
158
+ * (clearing them when absent), so they are copied from the raw /profile
159
+ * row, which carries them even though ExecutorProfile doesn't type them.
160
+ *
161
+ * Best-effort: on failure it logs, emits 'error', and returns the stored
162
+ * profile, so start() still succeeds.
163
+ */
164
+ private declareSupportedChains;
115
165
  stop(): void;
116
166
  pause(): void;
117
167
  resume(): void;
@@ -1,6 +1,34 @@
1
1
  import { ethers } from 'ethers';
2
2
  import { BlindMarket } from '../index.js';
3
- import { eciesDecrypt, aesDecrypt } from '../crypto/index.js';
3
+ import { eciesDecrypt, aesDecrypt, derivePublicKey } from '../crypto/index.js';
4
+ /**
5
+ * Chains this runtime's CODE can sign submitEvidence on. What it registers as
6
+ * its `supportedChains` is the subset it also has an RPC for (declaredChains).
7
+ */
8
+ export const SETTLEMENT_CHAINS = ['0g', 'base'];
9
+ /**
10
+ * The chain the backend named for a task. Missing means 0G (backends older
11
+ * than the field). Any other value throws: signing it on the 0G RPC would
12
+ * target the wrong escrow.
13
+ */
14
+ function settlementChain(taskId, reported) {
15
+ if (reported == null)
16
+ return '0g';
17
+ const known = SETTLEMENT_CHAINS.find((c) => c === reported);
18
+ if (!known) {
19
+ throw new Error(`task ${taskId} settles on "${reported}", which this runtime cannot sign for (it signs on ${SETTLEMENT_CHAINS.join(' and ')}) — update @blindmarket/sdk`);
20
+ }
21
+ return known;
22
+ }
23
+ /**
24
+ * The RPC this runtime signs on for `chain`. `rpcUrls` is per chain; the
25
+ * single `rpcUrl` is the 0G RPC (its default is 0G testnet) and never stands
26
+ * in for another chain: a Base submitEvidence sent through it is rejected by
27
+ * ethers' chainId pin — after the handler has already run.
28
+ */
29
+ function rpcFor(config, chain) {
30
+ return config.rpcUrls?.[chain] ?? (chain === '0g' ? config.rpcUrl : undefined);
31
+ }
4
32
  // ── Defaults ────────────────────────────────────────────────────────────────
5
33
  const DEFAULTS = {
6
34
  browseIntervalMs: 15_000,
@@ -34,6 +62,15 @@ export class WorkerRuntime {
34
62
  get activeExecutions() {
35
63
  return [...this.executions.values()];
36
64
  }
65
+ /**
66
+ * The chains this runtime declares to the backend: those its code can sign
67
+ * for AND it has an RPC for. Declaring a chain with no RPC made the backend
68
+ * offer tasks the runtime accepted and then could not settle, stranding
69
+ * them until the poster's deadline.
70
+ */
71
+ get declaredChains() {
72
+ return SETTLEMENT_CHAINS.filter((chain) => !!rpcFor(this.config, chain));
73
+ }
37
74
  /** Get own executor profile (available after start). */
38
75
  get executorProfile() {
39
76
  return this.profile;
@@ -55,6 +92,14 @@ export class WorkerRuntime {
55
92
  async start() {
56
93
  if (this.running)
57
94
  return this.profile;
95
+ // "No tasks arrive" looks the same as "no work available", so say which
96
+ // chains this runtime will be offered tasks on, and what is missing.
97
+ const undeclared = SETTLEMENT_CHAINS.filter((c) => !this.declaredChains.includes(c));
98
+ if (undeclared.length > 0) {
99
+ console.warn(`[WorkerRuntime] declaring chains: ${this.declaredChains.join(', ') || 'none'}. ` +
100
+ `No RPC for ${undeclared.join(', ')} — set ${undeclared.map((c) => `rpcUrls.${c}`).join(', ')} to be offered those tasks ` +
101
+ `(production posts new tasks on Base).`);
102
+ }
58
103
  // 1. Register or restore executor
59
104
  if (this.config.existingPrivateKey && this.config.existingAddress && this.config.existingPublicKey) {
60
105
  this.wallet = {
@@ -64,7 +109,7 @@ export class WorkerRuntime {
64
109
  };
65
110
  // Fetch existing profile — backend identifies by API key
66
111
  const result = await this.bb.getExecutorProfile();
67
- this.profile = result.agent;
112
+ this.profile = await this.declareSupportedChains(result.agent);
68
113
  }
69
114
  else {
70
115
  const result = await this.bb.createAgent({
@@ -72,6 +117,7 @@ export class WorkerRuntime {
72
117
  capabilities: this.config.capabilities,
73
118
  minReward: this.config.minReward,
74
119
  preferredCapabilities: this.config.preferredCapabilities,
120
+ supportedChains: this.declaredChains,
75
121
  });
76
122
  this.wallet = {
77
123
  address: result.wallet.address,
@@ -87,6 +133,73 @@ export class WorkerRuntime {
87
133
  this.emit({ type: 'started' });
88
134
  return this.profile;
89
135
  }
136
+ /**
137
+ * Re-register a restored executor when its stored `supportedChains` is
138
+ * null (a row registered by code that predates the field), or names a
139
+ * chain this runtime has no RPC for — it would be offered, accept and
140
+ * strand those tasks. A stored list that is a SUBSET of what the runtime
141
+ * can settle is left alone: an operator who registered ['base'] through
142
+ * the MCP or PATCH meant it. The backend only offers an executor
143
+ * tasks on the chains it declared, and a restore never registers otherwise,
144
+ * so an executor first registered by an older SDK would keep its old list.
145
+ *
146
+ * A /profile response with no `supportedChains` key comes from a backend
147
+ * that predates the field. That backend would drop the field anyway, and
148
+ * some of its versions reset the executor's 0G earnings on every register,
149
+ * so this does nothing there.
150
+ *
151
+ * The public key is derived from the private key, so a stored compressed
152
+ * key (which /register rejects) or a stale one is replaced with the key
153
+ * this runtime can decrypt with. Everything else comes from the stored
154
+ * profile, not this runtime's config, so a restore never rewrites it. The
155
+ * backend overwrites agentCardUrl and mcpEndpointUrl on every register
156
+ * (clearing them when absent), so they are copied from the raw /profile
157
+ * row, which carries them even though ExecutorProfile doesn't type them.
158
+ *
159
+ * Best-effort: on failure it logs, emits 'error', and returns the stored
160
+ * profile, so start() still succeeds.
161
+ */
162
+ async declareSupportedChains(stored) {
163
+ const raw = stored;
164
+ if (!('supportedChains' in raw))
165
+ return stored;
166
+ const can = this.declaredChains;
167
+ const storedChains = Array.isArray(stored.supportedChains) ? stored.supportedChains : null;
168
+ // A deliberate subset of what this runtime can settle stays as it is.
169
+ // (An empty list cannot come from the API: both schemas require one entry.)
170
+ if (storedChains && storedChains.every((c) => can.includes(c)))
171
+ return stored;
172
+ // Otherwise declare what it can settle, keeping the operator's choice
173
+ // where the two overlap.
174
+ const kept = storedChains ? can.filter((c) => storedChains.includes(c)) : [];
175
+ const declare = kept.length > 0 ? kept : can;
176
+ const optionalString = (v) => typeof v === 'string' && v !== '' ? v : undefined;
177
+ try {
178
+ // No `address`: /register has no such field and registers the wallet
179
+ // the API key authenticates.
180
+ const body = {
181
+ displayName: stored.displayName,
182
+ capabilities: stored.capabilities,
183
+ // Uncompressed, without 0x, as /register requires.
184
+ publicKey: derivePublicKey(this.wallet.privateKey),
185
+ agentCardUrl: optionalString(raw.agentCardUrl),
186
+ mcpEndpointUrl: optionalString(raw.mcpEndpointUrl),
187
+ minReward: optionalString(stored.minReward),
188
+ // The backend reads an unset list back as []; sending [] would store
189
+ // an empty list where there was none.
190
+ preferredCapabilities: stored.preferredCapabilities?.length ? stored.preferredCapabilities : undefined,
191
+ supportedChains: declare,
192
+ };
193
+ const { agent } = await this.bb.registerExecutor(body);
194
+ return agent ?? stored;
195
+ }
196
+ catch (err) {
197
+ const error = `Could not register supported chains (${declare.join(', ')}); continuing with the stored profile: ${err}`;
198
+ console.warn(`[WorkerRuntime] ${error}`);
199
+ this.emit({ type: 'error', error });
200
+ return stored;
201
+ }
202
+ }
90
203
  stop() {
91
204
  this.running = false;
92
205
  if (this.browseTimer) {
@@ -192,6 +305,13 @@ export class WorkerRuntime {
192
305
  const acceptResult = await this.bb.acceptTask(taskId);
193
306
  exec.task = a2a;
194
307
  this.emit({ type: 'task_accepted', taskId });
308
+ // Fail before running the handler if this runtime can't settle the task:
309
+ // an unknown chain, or one it has no RPC for (the send would fail only
310
+ // after the handler had spent its run, leaving the task Assigned).
311
+ const acceptedChain = settlementChain(taskId, acceptResult.chain);
312
+ if (!rpcFor(this.config, acceptedChain)) {
313
+ throw new Error(`task ${taskId} is escrowed on ${acceptedChain} but no RPC is configured for it — set rpcUrls.${acceptedChain} in the WorkerRuntime config`);
314
+ }
195
315
  // Decrypt the brief. 'public' tasks carry no wrappedKey by design — the
196
316
  // blob at rootHash is already plaintext, so skip ECIES/AES entirely.
197
317
  // Download by acceptResult.rootHash (the 0G Storage pointer) — NOT the
@@ -232,7 +352,19 @@ export class WorkerRuntime {
232
352
  // 'Assigned' and completeVerification always reverts — the task only
233
353
  // LOOKS complete.
234
354
  if (submitResult.unsignedSubmitEvidence) {
235
- const provider = new ethers.JsonRpcProvider(this.config.rpcUrl);
355
+ // Pick the RPC for the chain the backend says holds this task. This
356
+ // used to be a single 0G provider, so a Base submitEvidence was
357
+ // broadcast onto 0G. The tx now also carries chainId, so a wrong RPC
358
+ // fails loudly at ethers instead of landing on the wrong network.
359
+ const chain = settlementChain(taskId, submitResult.chain);
360
+ // rpcUrls per chain; rpcUrl is the 0G RPC only (rpcFor). The chainId
361
+ // pin still rejects a genuine mismatch at ethers before anything is
362
+ // broadcast.
363
+ const rpc = rpcFor(this.config, chain);
364
+ if (!rpc) {
365
+ throw new Error(`task ${taskId} is escrowed on ${chain} but no RPC is configured for it — set rpcUrls.${chain} in the WorkerRuntime config`);
366
+ }
367
+ const provider = new ethers.JsonRpcProvider(rpc);
236
368
  const signer = new ethers.Wallet(this.wallet.privateKey, provider);
237
369
  const tx = await signer.sendTransaction(submitResult.unsignedSubmitEvidence);
238
370
  await tx.wait();
@@ -1 +1 @@
1
- export { WorkerRuntime, type WorkerRuntimeConfig, type ExecuteTaskHandler, type TaskContext, type TaskExecutionInfo, type WorkerRuntimeEvent, } from './WorkerRuntime.js';
1
+ export { WorkerRuntime, SETTLEMENT_CHAINS, type SettlementChain, type WorkerRuntimeConfig, type ExecuteTaskHandler, type TaskContext, type TaskExecutionInfo, type WorkerRuntimeEvent, } from './WorkerRuntime.js';
@@ -1 +1 @@
1
- export { WorkerRuntime, } from './WorkerRuntime.js';
1
+ export { WorkerRuntime, SETTLEMENT_CHAINS, } from './WorkerRuntime.js';
package/dist/index.d.ts CHANGED
@@ -140,7 +140,8 @@ export declare class BlindMarket {
140
140
  * model: 'claude-sonnet-4-5',
141
141
  * apiKey: process.env.ANTHROPIC_API_KEY!,
142
142
  * ownerAddress: wallet.address,
143
- * ownerPublicKey: wallet.publicKey,
143
+ * // Uncompressed, no 0x (`wallet` is an ethers Wallet; its `publicKey` is compressed).
144
+ * ownerPublicKey: wallet.signingKey.publicKey.slice(2),
144
145
  * });
145
146
  */
146
147
  deployAgent(params: DeployAgentParams): Promise<DeployedAgent>;
@@ -156,7 +157,7 @@ export declare class BlindMarket {
156
157
  * const { executor, wallet } = await bb.createAgent({
157
158
  * displayName: 'DataBot',
158
159
  * capabilities: [AgentCap.DATA_PROCESSING, AgentCap.WEB_RESEARCH],
159
- * minReward: '1000000000000000000', // 1 0G in wei
160
+ * minReward: '1000000', // 1 USDC (the payment token's smallest unit; USDC has 6 decimals)
160
161
  * });
161
162
  * console.log(`Agent ${wallet.address} registered as ${executor.address}`);
162
163
  */
@@ -225,6 +226,9 @@ export declare class BlindMarket {
225
226
  privacy?: 'public' | 'private';
226
227
  alreadySettled?: boolean;
227
228
  assignTxHash?: Hex;
229
+ /** Which chain holds the task's escrow ('0g', 'base', …). Absent from
230
+ * backends older than the field, in which case the task is on 0G. */
231
+ chain?: string;
228
232
  }>;
229
233
  /**
230
234
  * Submit result for an accepted task. Returns an unsigned `submitEvidence`
@@ -237,6 +241,11 @@ export declare class BlindMarket {
237
241
  onChainTaskId?: string;
238
242
  status: string;
239
243
  evidenceHash?: Hex;
244
+ /** Which escrow the unsigned tx targets. Tasks are funded on exactly one
245
+ * chain; sign on that chain's RPC. Absent from backends older than the
246
+ * field, in which case the task is on 0G. A string, not a union: a newer
247
+ * backend can name a chain this SDK version doesn't know. */
248
+ chain?: string;
240
249
  unsignedSubmitEvidence?: Record<string, unknown> | null;
241
250
  }>;
242
251
  /**
@@ -370,7 +379,7 @@ export declare class BlindMarket {
370
379
  * const { url, token } = await BlindMarket.register({
371
380
  * agentName: 'my-agent',
372
381
  * agentWallet: wallet.address,
373
- * agentPublicKey: wallet.publicKey,
382
+ * agentPublicKey: wallet.signingKey.publicKey.slice(2), // uncompressed, no 0x
374
383
  * agentSigner: wallet,
375
384
  * });
376
385
  * console.log('Open', url, 'to confirm');
package/dist/index.js CHANGED
@@ -143,7 +143,8 @@ export class BlindMarket {
143
143
  * model: 'claude-sonnet-4-5',
144
144
  * apiKey: process.env.ANTHROPIC_API_KEY!,
145
145
  * ownerAddress: wallet.address,
146
- * ownerPublicKey: wallet.publicKey,
146
+ * // Uncompressed, no 0x (`wallet` is an ethers Wallet; its `publicKey` is compressed).
147
+ * ownerPublicKey: wallet.signingKey.publicKey.slice(2),
147
148
  * });
148
149
  */
149
150
  async deployAgent(params) {
@@ -161,28 +162,32 @@ export class BlindMarket {
161
162
  * const { executor, wallet } = await bb.createAgent({
162
163
  * displayName: 'DataBot',
163
164
  * capabilities: [AgentCap.DATA_PROCESSING, AgentCap.WEB_RESEARCH],
164
- * minReward: '1000000000000000000', // 1 0G in wei
165
+ * minReward: '1000000', // 1 USDC (the payment token's smallest unit; USDC has 6 decimals)
165
166
  * });
166
167
  * console.log(`Agent ${wallet.address} registered as ${executor.address}`);
167
168
  */
168
169
  async createAgent(params) {
169
170
  const wallet = ethers.Wallet.createRandom();
171
+ // The uncompressed key (0x04…): /register requires it, and posters wrap
172
+ // brief keys to it. `wallet.publicKey` is the compressed form in ethers v6.
173
+ const publicKey = wallet.signingKey.publicKey;
170
174
  const executor = {
171
175
  address: wallet.address,
172
176
  displayName: params.displayName,
173
177
  capabilities: params.capabilities,
174
- publicKey: wallet.publicKey.slice(2), // strip 0x prefix — backend expects raw hex
178
+ publicKey: publicKey.slice(2), // strip 0x prefix — backend expects raw hex
175
179
  agentCardUrl: params.agentCardUrl,
176
180
  mcpEndpointUrl: params.mcpEndpointUrl,
177
181
  minReward: params.minReward,
178
182
  preferredCapabilities: params.preferredCapabilities,
183
+ supportedChains: params.supportedChains,
179
184
  };
180
185
  const result = await this.registerExecutor(executor);
181
186
  return {
182
187
  executor: result.agent,
183
188
  wallet: {
184
189
  address: wallet.address,
185
- publicKey: wallet.publicKey,
190
+ publicKey,
186
191
  privateKey: wallet.privateKey,
187
192
  },
188
193
  };
@@ -446,7 +451,7 @@ export class BlindMarket {
446
451
  * const { url, token } = await BlindMarket.register({
447
452
  * agentName: 'my-agent',
448
453
  * agentWallet: wallet.address,
449
- * agentPublicKey: wallet.publicKey,
454
+ * agentPublicKey: wallet.signingKey.publicKey.slice(2), // uncompressed, no 0x
450
455
  * agentSigner: wallet,
451
456
  * });
452
457
  * console.log('Open', url, 'to confirm');
@@ -39,7 +39,7 @@ export function createBlindMarketTools(bb) {
39
39
  tool(bb, 'create_agent', 'One-shot agent creation: generates wallet + registers as A2A executor', {
40
40
  displayName: str('Display name for the agent'),
41
41
  capabilities: arr('List of capabilities', str('Capability', CAP_ENUM)),
42
- minReward: str('Minimum reward per task in wei (optional)'),
42
+ minReward: str("Minimum reward per task, as an integer in the payment token's smallest unit (USDC: 6 decimals) (optional)"),
43
43
  preferredCapabilities: arr('Preferred subset of capabilities (optional)', str('Capability', CAP_ENUM)),
44
44
  agentCardUrl: str('Agent card URL for marketplace display (optional)'),
45
45
  mcpEndpointUrl: str('MCP endpoint URL (optional)'),
@@ -51,8 +51,11 @@ export function createBlindMarketTools(bb) {
51
51
  displayName: str('Human-readable display name'),
52
52
  capabilities: arr('List of capabilities', str('Capability', CAP_ENUM)),
53
53
  publicKey: str('Your uncompressed secp256k1 public key'),
54
- minReward: str('Minimum reward in wei (optional)'),
54
+ minReward: str("Minimum reward, as an integer in the payment token's smallest unit (USDC: 6 decimals) (optional)"),
55
55
  preferredCapabilities: arr('Preferred subset of capabilities (optional)', str('Capability', CAP_ENUM)),
56
+ // No enum: the backend validates the list, and a newer backend may accept
57
+ // a chain this SDK version doesn't know.
58
+ supportedChains: arr("Settlement chains you can sign submitEvidence on, e.g. ['0g', 'base']. You are only offered tasks escrowed on these (optional; omitted means 0g and base)", str('Chain slug')),
56
59
  }, async (a) => {
57
60
  return bb.registerExecutor(a);
58
61
  }, ['address', 'displayName', 'capabilities', 'publicKey']),
package/dist/types.d.ts CHANGED
@@ -122,9 +122,16 @@ export interface ExecutorProfile {
122
122
  publicKey: string;
123
123
  reputation: number;
124
124
  tasksCompleted: number;
125
+ /** Native 0G earned, in wei (18 decimals). */
125
126
  totalEarnedRaw: string;
127
+ /** USDC earned, in base units (6 decimals). Absent from backends older than Sep 2026. */
128
+ totalEarnedUsdcRaw?: string;
126
129
  minReward?: string;
127
130
  preferredCapabilities?: AgentCapability[];
131
+ /** Settlement chains the executor declared at registration. `null` means it
132
+ * never declared any, which the backend treats as 0G and Base. Absent from
133
+ * backends that predate the field. */
134
+ supportedChains?: string[] | null;
128
135
  registeredAt: string;
129
136
  decayedScore?: number;
130
137
  disputeRatio?: number;
@@ -139,6 +146,11 @@ export interface RegisterExecutorInput {
139
146
  mcpEndpointUrl?: string;
140
147
  minReward?: string;
141
148
  preferredCapabilities?: AgentCapability[];
149
+ /** Settlement chains ('0g', 'base', …) this executor can sign
150
+ * `submitEvidence` on. The backend only offers and assigns it tasks
151
+ * escrowed on these chains. Omitted: the backend's default, 0G and Base.
152
+ * Backends that predate the field ignore it. */
153
+ supportedChains?: string[];
142
154
  }
143
155
  /** Params for BlindMarket.createAgent() — generates wallet + registers executor in one call. */
144
156
  export interface CreateAgentParams {
@@ -154,6 +166,9 @@ export interface CreateAgentParams {
154
166
  agentCardUrl?: string;
155
167
  /** MCP endpoint URL for tool-based agents. */
156
168
  mcpEndpointUrl?: string;
169
+ /** Settlement chains the agent can sign `submitEvidence` on. See
170
+ * {@link RegisterExecutorInput.supportedChains}. */
171
+ supportedChains?: string[];
157
172
  }
158
173
  /** Result of BlindMarket.createAgent(). */
159
174
  export interface CreateAgentResult {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blindmarket/sdk",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "BlindMarket SDK — deploy agents, assign workers, verify evidence",
5
5
  "author": "BlindMarket Team",
6
6
  "license": "MIT",