@blindmarket/sdk 0.6.4 → 0.8.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/CHANGELOG.md +104 -0
- package/README.md +91 -26
- package/dist/apiError.d.ts +26 -0
- package/dist/apiError.js +31 -0
- package/dist/chain/abi/BlindEscrow.json +154 -0
- package/dist/escrowCalls.d.ts +51 -0
- package/dist/escrowCalls.js +95 -0
- package/dist/executor/WorkerRuntime.d.ts +20 -0
- package/dist/executor/WorkerRuntime.js +71 -0
- package/dist/index.d.ts +397 -24
- package/dist/index.js +643 -36
- package/dist/onchain.d.ts +60 -0
- package/dist/onchain.js +122 -0
- package/dist/tools/helpers.js +8 -8
- package/dist/types.d.ts +21 -1
- package/package.json +1 -1
|
@@ -62,6 +62,43 @@ class AcceptAbandoned extends Error {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
65
|
+
/**
|
|
66
|
+
* The unit minReward is written in: the pricing token's smallest unit, USDC
|
|
67
|
+
* with 6 decimals (CreateAgentParams.minReward), the backend's pricing unit on
|
|
68
|
+
* every chain it settles on. A reward in any other unit cannot be compared.
|
|
69
|
+
*/
|
|
70
|
+
const MIN_REWARD_UNIT = { symbol: 'USDC', decimals: 6 };
|
|
71
|
+
/**
|
|
72
|
+
* 10^12 base units is 1,000,000 USDC, no plausible floor: the backend reads a
|
|
73
|
+
* floor at or above it as the old 18-decimal units and stores it divided by
|
|
74
|
+
* 10^12, rounded up (normalizeSettlementAmount in
|
|
75
|
+
* backend/src/services/settlementUnits.ts). The runtime applies the floor the
|
|
76
|
+
* backend holds the executor to.
|
|
77
|
+
*/
|
|
78
|
+
const LEGACY_SCALE = 10n ** 12n;
|
|
79
|
+
/** A minReward as the backend holds it, in USDC base units; undefined for no floor (unset, empty, malformed or zero). */
|
|
80
|
+
function rewardFloor(raw) {
|
|
81
|
+
if (typeof raw !== 'string' || !/^\d+$/.test(raw))
|
|
82
|
+
return undefined;
|
|
83
|
+
const value = BigInt(raw);
|
|
84
|
+
const floor = value >= LEGACY_SCALE ? (value + LEGACY_SCALE - 1n) / LEGACY_SCALE : value;
|
|
85
|
+
return floor === 0n ? undefined : floor;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Whether a listing's recorded reward clears `floor`. A missing reward, a
|
|
89
|
+
* malformed one, or one in another unit never does: comparing a floor with
|
|
90
|
+
* something it cannot price would let a 1-base-unit task through.
|
|
91
|
+
*/
|
|
92
|
+
function clearsRewardFloor(meta, floor) {
|
|
93
|
+
const reward = meta?.reward;
|
|
94
|
+
if (!reward || typeof reward !== 'object')
|
|
95
|
+
return false;
|
|
96
|
+
if (reward.unit?.symbol !== MIN_REWARD_UNIT.symbol || reward.unit?.decimals !== MIN_REWARD_UNIT.decimals)
|
|
97
|
+
return false;
|
|
98
|
+
if (typeof reward.amount !== 'string' || !/^\d+$/.test(reward.amount))
|
|
99
|
+
return false;
|
|
100
|
+
return BigInt(reward.amount) >= floor;
|
|
101
|
+
}
|
|
65
102
|
/** base, 2·base, 4·base … capped. `n` is 1 for the first failure. */
|
|
66
103
|
function backoff(base, n, cap) {
|
|
67
104
|
return Math.min(base * 2 ** Math.max(0, n - 1), cap);
|
|
@@ -79,6 +116,8 @@ export class WorkerRuntime {
|
|
|
79
116
|
retries = new Map();
|
|
80
117
|
retryTimers = new Set();
|
|
81
118
|
listeners = new Set();
|
|
119
|
+
/** Set once the runtime has said it skips listings with no recorded reward. */
|
|
120
|
+
warnedNoReward = false;
|
|
82
121
|
constructor(config) {
|
|
83
122
|
this.config = { ...DEFAULTS, ...config };
|
|
84
123
|
this.bb = new BlindMarket({ apiKey: config.apiKey, apiBase: config.apiBase });
|
|
@@ -132,6 +171,10 @@ export class WorkerRuntime {
|
|
|
132
171
|
"owner's registered public key and strand every task it accepted. To only look at tasks, call " +
|
|
133
172
|
'BlindMarket.browseA2ATasks() directly.');
|
|
134
173
|
}
|
|
174
|
+
const minReward = this.config.minReward;
|
|
175
|
+
if (minReward !== undefined && minReward !== '' && !/^\d+$/.test(minReward)) {
|
|
176
|
+
throw new Error(`[WorkerRuntime] minReward must be a whole number of the pricing token's smallest unit (USDC has 6 decimals: '1000000' is 1 USDC), not ${JSON.stringify(minReward)}.`);
|
|
177
|
+
}
|
|
135
178
|
if (this.declaredChains.length === 0) {
|
|
136
179
|
throw new Error('[WorkerRuntime] no RPC configured. Set `rpcUrls.arc` (where production posts new tasks), `rpcUrls.base` and/or `rpcUrl` (0G) to the network the backend at ' +
|
|
137
180
|
'`apiBase` settles on. There is no default: submitEvidence is signed on this RPC after the task is already ' +
|
|
@@ -317,6 +360,12 @@ export class WorkerRuntime {
|
|
|
317
360
|
// no chain) is what keeps such tasks out.
|
|
318
361
|
if (entry.meta?.chain && !this.declaredChains.includes(entry.meta.chain))
|
|
319
362
|
continue;
|
|
363
|
+
// Nor a task below this runtime's minReward: the handler run and the
|
|
364
|
+
// submitEvidence gas are the operator's, and a poster can escrow 1
|
|
365
|
+
// base unit. Newer backends also refuse such an /accept (403
|
|
366
|
+
// BELOW_MIN_REWARD); older ones apply the floor only when ranking offers.
|
|
367
|
+
if (!this.meetsFloor(entry.meta))
|
|
368
|
+
continue;
|
|
320
369
|
this.claim(taskId, state, entry.meta);
|
|
321
370
|
}
|
|
322
371
|
// Tasks that may still be held for this executor are not in the open
|
|
@@ -334,6 +383,28 @@ export class WorkerRuntime {
|
|
|
334
383
|
this.emit({ type: 'error', error: `Browse failed: ${err}` });
|
|
335
384
|
}
|
|
336
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* The floor browse applies: `minReward`, else (restore mode) the floor the
|
|
388
|
+
* executor is registered with. Undefined: no floor.
|
|
389
|
+
*/
|
|
390
|
+
get minRewardFloor() {
|
|
391
|
+
const configured = this.config.minReward;
|
|
392
|
+
return rewardFloor(configured !== undefined && configured !== '' ? configured : this.profile?.minReward);
|
|
393
|
+
}
|
|
394
|
+
/** Whether a listing clears the floor (always, without one). */
|
|
395
|
+
meetsFloor(meta) {
|
|
396
|
+
const floor = this.minRewardFloor;
|
|
397
|
+
if (floor === undefined)
|
|
398
|
+
return true;
|
|
399
|
+
if (clearsRewardFloor(meta, floor))
|
|
400
|
+
return true;
|
|
401
|
+
if (meta?.reward === undefined && !this.warnedNoReward) {
|
|
402
|
+
this.warnedNoReward = true;
|
|
403
|
+
console.warn(`[WorkerRuntime] minReward is set (${floor} USDC base units), so tasks listed without a recorded reward are skipped. ` +
|
|
404
|
+
'A backend older than the reward field lists none: unset minReward to take them.');
|
|
405
|
+
}
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
337
408
|
/** Executions holding a concurrency slot. A task waiting for a wrap or backing off holds none. */
|
|
338
409
|
inFlight() {
|
|
339
410
|
let n = 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ethers } from 'ethers';
|
|
2
|
-
import
|
|
2
|
+
import { ApiError } from './apiError.js';
|
|
3
|
+
import type { Address, Hex, RootHash, HealthStatus, PlatformStats, OpenTask, TaskDetail, CreateTaskTx, ExecutorProfile, RegisterExecutorInput, DeployedAgentInfo, AgentWalletInfo, ReputationInfo, LeaderboardEntry, StorageUploadResult, Message, AgentSearchResult, TaskTemplate, VerifyTaskInput, A2ATaskEntry, AgentCapability, CreateAgentParams, CreateAgentResult, CreateTaskRequest } from './types.js';
|
|
3
4
|
export interface BlindMarketConfig {
|
|
4
5
|
/** Backend API base URL (default: https://api.blindmarket.xyz) */
|
|
5
6
|
apiBase?: string;
|
|
@@ -16,14 +17,85 @@ export interface BlindMarketConfig {
|
|
|
16
17
|
export interface DeployAgentParams {
|
|
17
18
|
name: string;
|
|
18
19
|
instructions: string;
|
|
19
|
-
provider: 'openai' | 'anthropic' | 'groq' | 'gemini';
|
|
20
|
+
provider: 'openai' | 'anthropic' | 'groq' | 'gemini' | '0g-compute';
|
|
20
21
|
model: string;
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
/** The model provider's API key. Not needed for '0g-compute', which bills the agent's own wallet. */
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
/** Uncompressed secp256k1 public key, hex without 0x: the agent's private key is encrypted to it. */
|
|
23
25
|
ownerPublicKey: string;
|
|
24
26
|
capabilities?: string[];
|
|
25
27
|
tools?: object[];
|
|
28
|
+
toolSecrets?: Record<string, string>;
|
|
29
|
+
/** Public skills to install at deploy, by slug. */
|
|
30
|
+
skillSlugs?: string[];
|
|
31
|
+
/**
|
|
32
|
+
* An Arc transaction that already paid the deploy fee (see getDeployFee()).
|
|
33
|
+
* deployAgent() then pays nothing and names this payment instead.
|
|
34
|
+
*/
|
|
35
|
+
feeTxHash?: string;
|
|
36
|
+
/** @deprecated Ignored: the agent's owner is always the API key's wallet. */
|
|
37
|
+
ownerAddress?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface DeployAgentOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Pay the deploy fee if the backend charges one. Off by default, so
|
|
42
|
+
* deployAgent() never spends unless asked: without it (and without
|
|
43
|
+
* `feeTxHash`) a backend that charges answers DEPLOY_FEE_REQUIRED.
|
|
44
|
+
*/
|
|
45
|
+
payFee?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Signs the fee payment on the fee's chain instead of the configured
|
|
48
|
+
* executor (BlindMarketConfig.executor, whose rpcUrls must then name that
|
|
49
|
+
* chain). Must be a wallet of the API key's owner: the backend counts a fee
|
|
50
|
+
* from that wallet only.
|
|
51
|
+
*/
|
|
52
|
+
payer?: ethers.Signer;
|
|
53
|
+
/**
|
|
54
|
+
* The most deployAgent() will pay, in the fee token's smallest unit (USDC
|
|
55
|
+
* has 6 decimals). Default 1_000_000 (1 USDC, today's fee). A backend that
|
|
56
|
+
* asks for more is refused with DEPLOY_FEE_ABOVE_MAX before anything is paid.
|
|
57
|
+
*/
|
|
58
|
+
maxFeeRaw?: bigint | string;
|
|
59
|
+
/**
|
|
60
|
+
* Called with the fee transaction's hash the moment it is broadcast, before
|
|
61
|
+
* any wait. Persist it: if this process dies before the deploy finishes,
|
|
62
|
+
* pass it back as `params.feeTxHash` and nothing is paid twice. Not called
|
|
63
|
+
* for an AgentFactory payment, whose credit the backend keeps for you.
|
|
64
|
+
*/
|
|
65
|
+
onFeePaid?: (feeTxHash: string) => void | Promise<void>;
|
|
66
|
+
/** How long to wait between checks while the backend confirms the payment. Default 5000 ms. */
|
|
67
|
+
pollIntervalMs?: number;
|
|
68
|
+
/** How long to wait for a payment to confirm on-chain. Default 180000 ms. */
|
|
69
|
+
confirmTimeoutMs?: number;
|
|
70
|
+
}
|
|
71
|
+
/** What deploying an agent costs, from GET /api/v1/agents/deploy-fee. */
|
|
72
|
+
export type DeployFeeTerms = {
|
|
73
|
+
required: false;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* One transfer of `amountRaw` of `token` to `recipient` on chain `chainId`,
|
|
77
|
+
* named as feeTxHash. `factory` is the other way to pay. Backends before
|
|
78
|
+
* the field existed leave `chainId` out; deployAgent() will not pay those.
|
|
79
|
+
*/
|
|
80
|
+
| {
|
|
81
|
+
required: true;
|
|
82
|
+
method: 'transfer';
|
|
83
|
+
chain: string;
|
|
84
|
+
chainId?: number;
|
|
85
|
+
token: string;
|
|
86
|
+
recipient: string;
|
|
87
|
+
amountRaw: string;
|
|
88
|
+
decimals: number;
|
|
89
|
+
factory: string | null;
|
|
26
90
|
}
|
|
91
|
+
/** Pay through AgentFactory.deployAgent(); its event becomes a credit the next deploy spends. */
|
|
92
|
+
| {
|
|
93
|
+
required: true;
|
|
94
|
+
method: 'factory';
|
|
95
|
+
chain: string;
|
|
96
|
+
chainId?: number;
|
|
97
|
+
factory: string | null;
|
|
98
|
+
};
|
|
27
99
|
export interface DeployedAgent {
|
|
28
100
|
id: string;
|
|
29
101
|
name: string;
|
|
@@ -31,15 +103,142 @@ export interface DeployedAgent {
|
|
|
31
103
|
publicKey: string;
|
|
32
104
|
inftTokenId?: number;
|
|
33
105
|
status: string;
|
|
106
|
+
/** False when the agent was created but did not start; start it with startAgent(). */
|
|
107
|
+
started?: boolean;
|
|
108
|
+
/** The transaction that paid the deploy fee, when deployAgent() paid it or was given it. */
|
|
109
|
+
feeTxHash?: string;
|
|
110
|
+
/**
|
|
111
|
+
* True when `feeTxHash` had already paid for this agent, one of yours: a
|
|
112
|
+
* retry after a lost response returns the agent the first call created.
|
|
113
|
+
*/
|
|
114
|
+
alreadyDeployed?: boolean;
|
|
115
|
+
}
|
|
116
|
+
export interface PostTaskParams {
|
|
117
|
+
/** The brief. Encrypted here, before it leaves this process, unless `privacy` is 'public'. */
|
|
118
|
+
instructions: string;
|
|
119
|
+
/**
|
|
120
|
+
* The escrow, in the settlement token's smallest unit: USDC has 6
|
|
121
|
+
* decimals, so '2500000' is 2.5 USDC. Paid to the worker (90%) when the
|
|
122
|
+
* result is verified; refundable while no one has taken the task.
|
|
123
|
+
*/
|
|
124
|
+
amountRaw: string | bigint;
|
|
125
|
+
/** Seconds until the deadline. Default 86400 (24h). The escrow allows 1 hour to 90 days. */
|
|
126
|
+
durationSeconds?: number;
|
|
127
|
+
/**
|
|
128
|
+
* 'private' (default): the brief is encrypted and its key wrapped to each
|
|
129
|
+
* registered executor on the posting chain. 'public': the brief and the
|
|
130
|
+
* result are plaintext, readable by any agent.
|
|
131
|
+
*/
|
|
132
|
+
privacy?: 'private' | 'public';
|
|
133
|
+
/** Default 'auto', with `verificationCriteria` defaulting to `{ min_length: 10, pass_threshold: 60 }`. */
|
|
134
|
+
verificationMode?: 'manual' | 'auto' | 'agent';
|
|
135
|
+
verificationCriteria?: Record<string, unknown>;
|
|
136
|
+
/** The designated verifier, with verificationMode 'agent'. */
|
|
137
|
+
verifierAddress?: Address;
|
|
138
|
+
/** Route to agents with these capabilities first. Empty (default) offers it to every agent. */
|
|
139
|
+
requiredCapabilities?: AgentCapability[];
|
|
140
|
+
/** Only this executor can take the task, and only it gets the brief's key. */
|
|
141
|
+
targetExecutor?: Address;
|
|
142
|
+
/** Default 'global'. */
|
|
143
|
+
locationZone?: string;
|
|
144
|
+
}
|
|
145
|
+
export interface PostTaskOptions {
|
|
146
|
+
/**
|
|
147
|
+
* Signs the escrow funding on the posting chain instead of the configured
|
|
148
|
+
* executor (BlindMarketConfig.executor, whose rpcUrls must name that chain).
|
|
149
|
+
* Must be the API key's owner wallet: the task is posted as that wallet.
|
|
150
|
+
*/
|
|
151
|
+
signer?: ethers.Signer;
|
|
152
|
+
/**
|
|
153
|
+
* The most postTask() will lock in escrow, in the token's smallest unit.
|
|
154
|
+
* Refused with AMOUNT_ABOVE_MAX before anything is sent.
|
|
155
|
+
*/
|
|
156
|
+
maxAmountRaw?: bigint | string;
|
|
157
|
+
/**
|
|
158
|
+
* Called the moment the funding transaction is broadcast, with its hash and
|
|
159
|
+
* the complete listing body. Persist `indexParams`: if this process dies
|
|
160
|
+
* before the task is listed, indexTask(indexParams) finishes it and the
|
|
161
|
+
* escrow is not funded twice.
|
|
162
|
+
*/
|
|
163
|
+
onFunded?: (funding: {
|
|
164
|
+
txHash: string;
|
|
165
|
+
taskHash: string;
|
|
166
|
+
indexParams: IndexTaskParams;
|
|
167
|
+
}) => void | Promise<void>;
|
|
168
|
+
/** How long to wait for each transaction to confirm. Default 180000 ms. */
|
|
169
|
+
confirmTimeoutMs?: number;
|
|
170
|
+
}
|
|
171
|
+
export interface PostedTask {
|
|
172
|
+
/** The task's id on the backend (the brief's sha256 commitment). */
|
|
173
|
+
taskHash: string;
|
|
174
|
+
/** The on-chain task id, for cancelAndRefund() and reclaimAfterTimeout(). */
|
|
175
|
+
taskId?: string;
|
|
176
|
+
/** The transaction that funded the escrow. */
|
|
177
|
+
txHash: string;
|
|
178
|
+
chain: string;
|
|
179
|
+
chainId: number;
|
|
180
|
+
rootHash: string;
|
|
181
|
+
privacy: 'private' | 'public';
|
|
182
|
+
/** How many executors can decrypt the brief. 0 for a public task. */
|
|
183
|
+
wrappedTo: number;
|
|
184
|
+
/**
|
|
185
|
+
* The brief's AES key (hex), for a private task. Keep it to wrap the brief
|
|
186
|
+
* to an executor that registers later; never send it anywhere.
|
|
187
|
+
*/
|
|
188
|
+
aesKey?: string;
|
|
189
|
+
}
|
|
190
|
+
/** The body of POST /api/v1/a2a/tasks/index, which lists a funded task on the market. */
|
|
191
|
+
export interface IndexTaskParams {
|
|
192
|
+
txHash: string;
|
|
193
|
+
taskHash: string;
|
|
194
|
+
rootHash?: string;
|
|
195
|
+
wrappedKeys?: Record<string, string>;
|
|
196
|
+
privacy?: 'private' | 'public';
|
|
197
|
+
publicBrief?: string;
|
|
198
|
+
verificationMode?: 'manual' | 'auto' | 'agent';
|
|
199
|
+
verificationCriteria?: Record<string, unknown>;
|
|
200
|
+
verifierAddress?: Address;
|
|
201
|
+
requiredCapabilities?: AgentCapability[];
|
|
202
|
+
targetExecutor?: Address;
|
|
203
|
+
}
|
|
204
|
+
/** A refund the client signed and sent: cancelAndRefund() or reclaimAfterTimeout(). */
|
|
205
|
+
export interface RefundResult {
|
|
206
|
+
txHash: string;
|
|
207
|
+
chain: string;
|
|
208
|
+
chainId: number;
|
|
209
|
+
/** Whether the backend took the task off the market. False leaves it listed until its deadline; the refund stands either way. */
|
|
210
|
+
listingClosed: boolean;
|
|
211
|
+
/**
|
|
212
|
+
* What the transaction did, when the backend says: 'refund' returned the
|
|
213
|
+
* escrow to the poster; 'escalate' (reclaimAfterTimeout on work delivered
|
|
214
|
+
* before the deadline and never judged) sent the task for review and
|
|
215
|
+
* refunded nothing. An admin rules on it, and with no ruling within 14 days
|
|
216
|
+
* the worker is paid.
|
|
217
|
+
*/
|
|
218
|
+
outcome?: 'refund' | 'escalate';
|
|
219
|
+
}
|
|
220
|
+
export interface RefundOptions {
|
|
221
|
+
/** Signs on the task's chain instead of the configured executor. */
|
|
222
|
+
signer?: ethers.Signer;
|
|
223
|
+
/** The task's chain (PostedTask.chain). Task ids repeat across chains, so naming it refunds that one. */
|
|
224
|
+
chain?: string;
|
|
225
|
+
confirmTimeoutMs?: number;
|
|
34
226
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
227
|
+
/** One settlement chain, as GET /health/settlement describes it. */
|
|
228
|
+
export interface SettlementChainInfo {
|
|
229
|
+
chain: string;
|
|
230
|
+
chainId: number;
|
|
231
|
+
tier?: string;
|
|
232
|
+
escrowAddress: string | null;
|
|
233
|
+
token: {
|
|
234
|
+
kind: 'native' | 'erc20';
|
|
235
|
+
address: string | null;
|
|
236
|
+
symbol: string;
|
|
237
|
+
decimals: number;
|
|
238
|
+
};
|
|
239
|
+
relayChain?: string | null;
|
|
240
|
+
gasSymbol?: string;
|
|
241
|
+
postable?: boolean;
|
|
43
242
|
}
|
|
44
243
|
/** Per-chain RPC URLs for signing `submitEvidence` — a task is escrowed on exactly one chain. */
|
|
45
244
|
export interface DeliverSigner {
|
|
@@ -106,7 +305,11 @@ export declare class BlindMarket {
|
|
|
106
305
|
health(): Promise<HealthStatus>;
|
|
107
306
|
/** Live platform counts. */
|
|
108
307
|
stats(): Promise<PlatformStats>;
|
|
109
|
-
/**
|
|
308
|
+
/**
|
|
309
|
+
* List open tasks from the legacy 0G TaskRegistry (numeric ids on the 0G
|
|
310
|
+
* escrow). Tasks escrowed on Base or Arc are not in it: browseA2ATasks()
|
|
311
|
+
* lists the work agents can take.
|
|
312
|
+
*/
|
|
110
313
|
listTasks(limit?: number): Promise<OpenTask[]>;
|
|
111
314
|
/** Get full task details (on-chain + A2A state). */
|
|
112
315
|
getTask(id: string): Promise<TaskDetail>;
|
|
@@ -124,16 +327,28 @@ export declare class BlindMarket {
|
|
|
124
327
|
unsignedTx: object;
|
|
125
328
|
}>;
|
|
126
329
|
/**
|
|
127
|
-
* Build an unsigned `cancelTask` transaction
|
|
330
|
+
* Build an unsigned `cancelTask` transaction (the refund of a task no one
|
|
331
|
+
* has taken). `chain`/`chainId` name where to send it. cancelAndRefund()
|
|
332
|
+
* builds, signs and sends it for you.
|
|
128
333
|
*/
|
|
129
|
-
cancelTask(taskId: string): Promise<{
|
|
334
|
+
cancelTask(taskId: string, chain?: string): Promise<{
|
|
130
335
|
unsignedTx: object;
|
|
336
|
+
chain?: string;
|
|
337
|
+
chainId?: number;
|
|
131
338
|
}>;
|
|
132
339
|
/**
|
|
133
|
-
* Build an unsigned `claimTimeout` transaction
|
|
340
|
+
* Build an unsigned `claimTimeout` transaction (the refund of a task whose
|
|
341
|
+
* deadline passed). reclaimAfterTimeout() builds, signs and sends it for you.
|
|
342
|
+
* `outcome` says what it will do: on work delivered before the deadline and
|
|
343
|
+
* never judged, the escrow sends the task for review ('escalate') instead
|
|
344
|
+
* of refunding it, and `message` explains.
|
|
134
345
|
*/
|
|
135
|
-
claimTimeout(taskId: string): Promise<{
|
|
346
|
+
claimTimeout(taskId: string, chain?: string): Promise<{
|
|
136
347
|
unsignedTx: object;
|
|
348
|
+
chain?: string;
|
|
349
|
+
chainId?: number;
|
|
350
|
+
outcome?: 'refund' | 'escalate';
|
|
351
|
+
message?: string;
|
|
137
352
|
}>;
|
|
138
353
|
/**
|
|
139
354
|
* Build an unsigned `submitEvidence` transaction.
|
|
@@ -145,8 +360,115 @@ export declare class BlindMarket {
|
|
|
145
360
|
unsignedTx: object;
|
|
146
361
|
}>;
|
|
147
362
|
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
363
|
+
* Where new tasks are posted and what each chain settles in
|
|
364
|
+
* (`GET /health/settlement`): no auth, no RPC reads on the backend.
|
|
365
|
+
*/
|
|
366
|
+
getSettlement(): Promise<{
|
|
367
|
+
postingChain: string | null;
|
|
368
|
+
chains: SettlementChainInfo[];
|
|
369
|
+
}>;
|
|
370
|
+
/**
|
|
371
|
+
* `chain`'s entry in /health/settlement, with its escrow: every transaction
|
|
372
|
+
* the backend builds for this client to sign must target that escrow.
|
|
373
|
+
* Throws 409 CHAIN_UNKNOWN when the backend lists no escrow for it.
|
|
374
|
+
*/
|
|
375
|
+
private settlementEntry;
|
|
376
|
+
/**
|
|
377
|
+
* Post a task end to end, from the API key's own wallet: encrypt the brief
|
|
378
|
+
* (unless public) and wrap its key to the posting chain's executors, upload
|
|
379
|
+
* it, build createTask, approve the escrow for the amount when the token is
|
|
380
|
+
* an ERC-20, fund the escrow, and list the task (`POST /a2a/tasks/index`).
|
|
381
|
+
*
|
|
382
|
+
* The wallet signs locally, on the backend's posting chain (Arc on
|
|
383
|
+
* production, where gas is paid in USDC). Before anything is sent it checks
|
|
384
|
+
* the signer is the API key's owner, that its RPC is on the posting chain,
|
|
385
|
+
* that the wallet holds the amount, and that the backend built exactly this
|
|
386
|
+
* createTask (task hash, token, amount, zone, duration) for the escrow it
|
|
387
|
+
* advertises, with no other value: 409 ESCROW_MISMATCH / TX_MISMATCH
|
|
388
|
+
* otherwise. Only the tx's to and data are signed. The funding hash goes to `onFunded` as soon as
|
|
389
|
+
* it is sent; an error after that carries it as `err.txHash`, and
|
|
390
|
+
* indexTask() lists the funded task without paying again.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* const task = await bb.postTask(
|
|
394
|
+
* { instructions: 'Summarise this paper in 5 bullets: …', amountRaw: '2000000' }, // 2 USDC
|
|
395
|
+
* { onFunded: ({ txHash }) => saveSomewhere(txHash) },
|
|
396
|
+
* );
|
|
397
|
+
*/
|
|
398
|
+
postTask(params: PostTaskParams, opts?: PostTaskOptions): Promise<PostedTask>;
|
|
399
|
+
/**
|
|
400
|
+
* List a funded task on the market (`POST /api/v1/a2a/tasks/index`), from
|
|
401
|
+
* its funding transaction. Safe to call again for the same task: the
|
|
402
|
+
* backend merges a repeat from the same poster. postTask() calls it; call
|
|
403
|
+
* it yourself to finish a post whose funding confirmed but whose listing
|
|
404
|
+
* failed (the error's `body.indexParams` holds the fields).
|
|
405
|
+
*/
|
|
406
|
+
indexTask(params: IndexTaskParams): Promise<{
|
|
407
|
+
taskHash: string;
|
|
408
|
+
onChainTaskId?: string;
|
|
409
|
+
indexed: boolean;
|
|
410
|
+
}>;
|
|
411
|
+
/** indexTask(), asking again while the backend's RPC has not seen the receipt or the backend is briefly down. */
|
|
412
|
+
private indexTaskPatiently;
|
|
413
|
+
/**
|
|
414
|
+
* Cancel a task no one has taken and get its escrow back: builds
|
|
415
|
+
* cancelTask, checks the signer is on the task's chain, signs and sends it,
|
|
416
|
+
* then takes the task off the market (`POST /tasks/:id/confirm-tx`).
|
|
417
|
+
* `taskId` is the on-chain id (PostedTask.taskId); pass `chain`
|
|
418
|
+
* (PostedTask.chain) too, since ids repeat across chains.
|
|
419
|
+
*
|
|
420
|
+
* Only a zero-value `cancelTask(taskId)` on the escrow /health/settlement
|
|
421
|
+
* lists for the chain is signed (to and data only), and only on the chain
|
|
422
|
+
* you named: 409 ESCROW_MISMATCH, TX_MISMATCH, CHAIN_MISMATCH or
|
|
423
|
+
* CHAIN_UNKNOWN otherwise, with nothing sent. reclaimAfterTimeout() does
|
|
424
|
+
* the same for `claimTimeout(taskId)`.
|
|
425
|
+
*/
|
|
426
|
+
cancelAndRefund(taskId: string, opts?: RefundOptions): Promise<RefundResult>;
|
|
427
|
+
/**
|
|
428
|
+
* Reclaim the escrow of a task whose deadline passed undelivered
|
|
429
|
+
* (claimTimeout), signed and sent. On work delivered before the deadline
|
|
430
|
+
* and never judged, the escrow sends the task for review instead and
|
|
431
|
+
* refunds nothing: the result's outcome is then 'escalate'.
|
|
432
|
+
*/
|
|
433
|
+
reclaimAfterTimeout(taskId: string, opts?: RefundOptions): Promise<RefundResult>;
|
|
434
|
+
/**
|
|
435
|
+
* Sign the refund the backend built, once it is checked to be exactly
|
|
436
|
+
* `fn(taskId)` on the escrow of the chain it names (the one the caller
|
|
437
|
+
* named, when it named one), with no value. Only its to and data are signed.
|
|
438
|
+
*/
|
|
439
|
+
private sendRefund;
|
|
440
|
+
/**
|
|
441
|
+
* Tell the backend a refund landed (`POST /api/v1/tasks/:id/confirm-tx`),
|
|
442
|
+
* which checks the receipt and takes the task off the market. Without it a
|
|
443
|
+
* refunded task keeps listing as open until its deadline. Best effort: the
|
|
444
|
+
* money has already moved, so a failure here only reports it not closed.
|
|
445
|
+
* A claim that sent the task for review closes nothing (escalated).
|
|
446
|
+
*/
|
|
447
|
+
private confirmRefund;
|
|
448
|
+
/** What deploying an agent costs on this backend, and how to pay it. */
|
|
449
|
+
getDeployFee(): Promise<DeployFeeTerms>;
|
|
450
|
+
/**
|
|
451
|
+
* Run every check POST /deploy makes before it takes a fee, with nothing
|
|
452
|
+
* paid or saved. Throws the same ApiError the deploy would (400 with field
|
|
453
|
+
* errors, 404 SKILL_NOT_FOUND, 400 INVALID_OWNER_PUBLIC_KEY). Returns false
|
|
454
|
+
* when the backend predates the check and nothing could be checked.
|
|
455
|
+
*/
|
|
456
|
+
validateDeploy(params: DeployAgentParams): Promise<boolean>;
|
|
457
|
+
/**
|
|
458
|
+
* Deploy a new hosted agent. The backend generates its wallet, mints an
|
|
459
|
+
* INFT, starts it, and returns the agent descriptor.
|
|
460
|
+
*
|
|
461
|
+
* Deploying costs a fee (1 USDC on Arc on production; getDeployFee() says).
|
|
462
|
+
* An unspent AgentFactory credit pays first. Otherwise deployAgent() pays
|
|
463
|
+
* only with `{ payFee: true }`, from the configured executor wallet (set
|
|
464
|
+
* `rpcUrls.arc`) or `payer`, which must be the API key's owner. Before it
|
|
465
|
+
* pays it checks the payer's chain, the fee against `maxFeeRaw`, and the
|
|
466
|
+
* request itself, so nothing is paid for a deploy that would be refused.
|
|
467
|
+
*
|
|
468
|
+
* The fee's hash goes to `onFeePaid` as soon as it is sent, and onto any
|
|
469
|
+
* error after that (`err.feeTxHash`): retry with `params.feeTxHash` set to
|
|
470
|
+
* it and nothing is paid twice. A retry whose payment already created one
|
|
471
|
+
* of your agents returns that agent, with `alreadyDeployed: true`.
|
|
150
472
|
*
|
|
151
473
|
* @example
|
|
152
474
|
* const agent = await bb.deployAgent({
|
|
@@ -155,12 +477,26 @@ export declare class BlindMarket {
|
|
|
155
477
|
* provider: 'anthropic',
|
|
156
478
|
* model: 'claude-sonnet-4-5',
|
|
157
479
|
* apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
158
|
-
* ownerAddress: wallet.address,
|
|
159
480
|
* // Uncompressed, no 0x (`wallet` is an ethers Wallet; its `publicKey` is compressed).
|
|
160
481
|
* ownerPublicKey: wallet.signingKey.publicKey.slice(2),
|
|
161
|
-
* });
|
|
482
|
+
* }, { payFee: true, onFeePaid: (hash) => saveSomewhere(hash) });
|
|
162
483
|
*/
|
|
163
|
-
deployAgent(params: DeployAgentParams): Promise<DeployedAgent>;
|
|
484
|
+
deployAgent(params: DeployAgentParams, opts?: DeployAgentOptions): Promise<DeployedAgent>;
|
|
485
|
+
/** Deploy with a fee transaction already paid: wait for the backend to see it, and make a retry safe. */
|
|
486
|
+
private deployWithFee;
|
|
487
|
+
/** `agentId` as a DeployedAgent, when the API key's owner owns it; else null. */
|
|
488
|
+
private ownAgent;
|
|
489
|
+
/**
|
|
490
|
+
* An error after the fee was paid, carrying the payment: `feeTxHash` on the
|
|
491
|
+
* error and in its body, and the message says how to reuse it. The
|
|
492
|
+
* backend's code, status and envelope are kept.
|
|
493
|
+
*/
|
|
494
|
+
private withFee;
|
|
495
|
+
private assertFeeCeiling;
|
|
496
|
+
/** POST /agents/deploy, asking again while the backend answers one of `retryCodes`. */
|
|
497
|
+
private postDeploy;
|
|
498
|
+
/** The configured executor as a signer on `chain`. */
|
|
499
|
+
private signerOn;
|
|
164
500
|
/**
|
|
165
501
|
* One-shot executor registration in the A2A marketplace.
|
|
166
502
|
*
|
|
@@ -204,6 +540,15 @@ export declare class BlindMarket {
|
|
|
204
540
|
* route (404 / a non-JSON 404 page) and nothing could be checked.
|
|
205
541
|
*/
|
|
206
542
|
private assertOwnerKey;
|
|
543
|
+
/**
|
|
544
|
+
* Before a spend: throw 409 OWNER_MISMATCH unless `address` is a wallet the
|
|
545
|
+
* backend will credit the spend to. `exact` needs the API key's own address
|
|
546
|
+
* (a task is posted as that wallet); otherwise any wallet linked to it
|
|
547
|
+
* counts, as the deploy fee check does. Unlike assertOwnerKey this fails
|
|
548
|
+
* closed: money never moves on an unchecked wallet.
|
|
549
|
+
*/
|
|
550
|
+
private assertSpender;
|
|
551
|
+
private assertFeePayer;
|
|
207
552
|
/** List deployed agents, optionally filtered by owner address. */
|
|
208
553
|
listAgents(ownerAddress?: string): Promise<DeployedAgentInfo[]>;
|
|
209
554
|
/** Get a single deployed agent by ID. */
|
|
@@ -339,10 +684,34 @@ export declare class BlindMarket {
|
|
|
339
684
|
* unsigned `submitEvidence` on the chain the backend names → `finalize()`.
|
|
340
685
|
* Safe to re-call on a task stranded in 'submitted': INVALID_STATE at submit
|
|
341
686
|
* and NOT_SUBMITTED_ON_CHAIN at finalize both heal through `rebroadcast()`.
|
|
687
|
+
*
|
|
688
|
+
* The executor key signs only a zero-value `submitEvidence(onChainTaskId,
|
|
689
|
+
* evidenceHash)` on the escrow /health/settlement lists for that chain,
|
|
690
|
+
* where (from /submit) evidenceHash is keccak256 of `JSON.stringify(resultData)`,
|
|
691
|
+
* over an RPC checked to serve that chain, and only its to and data.
|
|
692
|
+
* Anything else throws 409 ESCROW_MISMATCH, TX_MISMATCH, CHAIN_MISMATCH or
|
|
693
|
+
* CHAIN_UNKNOWN (or WRONG_CHAIN for the RPC) with nothing sent.
|
|
342
694
|
*/
|
|
343
695
|
deliverResult(taskId: string, resultData: Record<string, unknown>, signerOverride?: DeliverSigner): Promise<Awaited<ReturnType<BlindMarket['finalize']>> & {
|
|
344
696
|
submitTxHash?: string;
|
|
345
697
|
}>;
|
|
698
|
+
/**
|
|
699
|
+
* Approve or reject the delivered result of a task you posted with
|
|
700
|
+
* `verificationMode: 'manual'` (`POST /api/v1/a2a/tasks/:hash/verify`).
|
|
701
|
+
* Approving settles the escrow to the worker (90%); rejecting fails the
|
|
702
|
+
* round, and the worker may resubmit before the deadline. Only the poster
|
|
703
|
+
* can review, and only once the task is `submitted`.
|
|
704
|
+
*/
|
|
705
|
+
reviewResult(taskHash: string, review: {
|
|
706
|
+
passed: boolean;
|
|
707
|
+
reasons?: string[];
|
|
708
|
+
}): Promise<{
|
|
709
|
+
status?: string;
|
|
710
|
+
verificationResult?: {
|
|
711
|
+
passed: boolean;
|
|
712
|
+
reasons?: string[];
|
|
713
|
+
};
|
|
714
|
+
}>;
|
|
346
715
|
/** Get tasks posted by the authenticated user. */
|
|
347
716
|
getPostedTasks(): Promise<{
|
|
348
717
|
tasks: A2ATaskEntry[];
|
|
@@ -378,8 +747,12 @@ export declare class BlindMarket {
|
|
|
378
747
|
getReputation(address: Address): Promise<ReputationInfo>;
|
|
379
748
|
/** Get top workers by decayed score. */
|
|
380
749
|
getLeaderboard(limit?: number): Promise<LeaderboardEntry[]>;
|
|
381
|
-
/**
|
|
382
|
-
|
|
750
|
+
/**
|
|
751
|
+
* Upload a blob to 0G Storage. `data` is the bytes as **base64**: the
|
|
752
|
+
* backend base64-decodes it. (The type once said Hex; a hex string sent
|
|
753
|
+
* here uploads the wrong bytes.)
|
|
754
|
+
*/
|
|
755
|
+
uploadBlob(data: string): Promise<StorageUploadResult>;
|
|
383
756
|
/**
|
|
384
757
|
* Download a blob by root hash. `blob` is base64-encoded raw bytes — the
|
|
385
758
|
* caller decodes (and, for encrypted tasks, decrypts) it client-side.
|