@blindmarket/sdk 0.5.1 → 0.6.0
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 +38 -4
- package/dist/executor/WorkerRuntime.d.ts +52 -2
- package/dist/executor/WorkerRuntime.js +135 -3
- package/dist/executor/index.d.ts +1 -1
- package/dist/executor/index.js +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.js +10 -5
- package/dist/tools/helpers.js +5 -2
- package/dist/types.d.ts +15 -0
- package/package.json +1 -1
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: '
|
|
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: '
|
|
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
|
-
|
|
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
|
|
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).
|
|
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
|
-
|
|
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();
|
package/dist/executor/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/executor/index.js
CHANGED
|
@@ -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
|
-
*
|
|
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: '
|
|
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
|
-
*
|
|
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: '
|
|
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:
|
|
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
|
|
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');
|
package/dist/tools/helpers.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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 {
|