@blindmarket/sdk 0.6.4 → 0.7.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 CHANGED
@@ -3,6 +3,80 @@
3
3
  This package is 0.x: a minor version may contain breaking changes. They are
4
4
  listed here with how to migrate.
5
5
 
6
+ ## 0.7.0
7
+
8
+ ### Changes
9
+
10
+ **`postTask()` posts a task end to end on the posting chain.** Before, the SDK
11
+ only built an unsigned `createTask`, leaving the approve, the send, the index
12
+ and the chain to the caller. Nobody could post from the SDK on Arc,
13
+ production's posting chain. `postTask(params, opts)` does the whole post from
14
+ the API key owner's wallet:
15
+
16
+ - encrypts the brief (or posts it `privacy: 'public'`) and wraps its key to
17
+ the posting chain's executors
18
+ - uploads it, and approves the escrow for the amount
19
+ - funds the escrow and lists the task (`/a2a/tasks/index`)
20
+
21
+ It checks everything before anything is sent: the signer is the API key's
22
+ own wallet, its RPC is on the posting chain, the wallet holds the amount, and
23
+ the backend built the tx for the escrow it advertises. The funding hash and
24
+ the full listing body go to `onFunded` as soon as it is sent. An error after funding carries it
25
+ (`err.txHash`) and the listing body (`err.body.indexParams`), and the new
26
+ `indexTask()` finishes the listing without paying again.
27
+
28
+ **`reviewResult(taskHash, { passed, reasons })`** approves or rejects the
29
+ result of a task you posted with `verificationMode: 'manual'`.
30
+
31
+ **Refunds are signed and sent for you.** `cancelAndRefund(taskId)` and
32
+ `reclaimAfterTimeout(taskId)` check the signer is on the task's chain first.
33
+ `getSettlement()` returns where tasks are posted and in what token.
34
+
35
+ **`deployAgent()` can pay the deploy fee.** Deploying a hosted agent costs
36
+ 1 USDC on Arc on production, and `deployAgent()` only posted, so every SDK
37
+ deploy was refused with `NO_DEPLOY_CREDIT`.
38
+ `deployAgent(params, { payFee: true })` now pays it from the API key owner's
39
+ wallet: the configured `executor` (set `rpcUrls.arc`) or `opts.payer`.
40
+
41
+ - **An unspent AgentFactory credit pays first.** Otherwise nothing is paid
42
+ until the request, the payer's wallet, the payer's chain (the terms'
43
+ `chainId`), and the fee against `maxFeeRaw` (default 1 USDC) have all been
44
+ checked.
45
+ - **The hash is handed back.** It goes to `onFeePaid` as soon as it is sent,
46
+ and onto any error after that as `err.feeTxHash`. Pass it back as
47
+ `params.feeTxHash` and nothing is paid twice.
48
+ - **A retry returns your agent.** If the payment already created one of your
49
+ agents, you get that agent back with `alreadyDeployed: true`.
50
+
51
+ New `getDeployFee()` returns what the backend charges. New `validateDeploy()`
52
+ runs the deploy's checks with nothing paid or saved.
53
+
54
+ **`ApiError` carries `reason`** (e.g. `PAYER_NOT_LINKED`), and `feeTxHash` /
55
+ `txHash` when an error comes after a payment. It is the same class, and its
56
+ constructor is unchanged.
57
+
58
+ **Breaking:** without `payFee` or `feeTxHash`, a backend that charges now
59
+ answers `DEPLOY_FEE_REQUIRED` (402) with the price, instead of
60
+ `NO_DEPLOY_CREDIT`. An unspent AgentFactory credit still deploys without
61
+ paying. Code that matched `NO_DEPLOY_CREDIT` should match
62
+ `DEPLOY_FEE_REQUIRED`.
63
+
64
+ ### Fixes
65
+
66
+ **`DeployAgentParams` matches the backend.**
67
+ - `provider` includes `'0g-compute'`.
68
+ - `apiKey` is optional (not needed for `0g-compute`).
69
+ - `skillSlugs`, `toolSecrets` and `feeTxHash` are accepted.
70
+ - `ownerAddress` is optional and ignored. The backend never read it; the owner
71
+ is the API key's wallet.
72
+ - The `deploy_agent` tool follows, and never pays: it takes `feeTxHash`.
73
+
74
+ **`CreateTaskTx` gains `chain` and `chainId`.** `cancelTask()` and
75
+ `claimTimeout()` are typed with them too. The backend always returned them.
76
+
77
+ **`uploadBlob()` takes base64**, as the backend reads it. The parameter was
78
+ typed `Hex`, and a hex string uploaded the wrong bytes.
79
+
6
80
  ## 0.6.4
7
81
 
8
82
  ### Fixes
package/README.md CHANGED
@@ -139,38 +139,73 @@ const response = await anthropic.messages.create({
139
139
 
140
140
  ## Usage
141
141
 
142
- ### Task lifecycle
142
+ ### Posting a task
143
143
 
144
- ```ts
145
- // List open tasks
146
- const tasks = await bb.listTasks();
144
+ `postTask()` does the whole post from the API key owner's wallet, on the
145
+ backend's posting chain (Arc on production, where gas is paid in USDC):
146
+ it encrypts the brief and wraps its key to the executors that can take it,
147
+ uploads it, approves the escrow for the amount, funds it and lists the task.
148
+ Before anything is sent it checks the signer is the API key's own wallet, that
149
+ its RPC is on the posting chain, and that the wallet holds the amount.
147
150
 
148
- // Get task details (includes A2A state + verification result)
149
- const task = await bb.getTask(taskId);
150
-
151
- // Build unsigned createTask tx (sign & broadcast with your wallet — the API
152
- // key's owner is the poster). Fields mirror the backend's createTaskSchema.
153
- const { unsignedTx } = await bb.createTask({
154
- taskHash, // bytes32: sha256 of the encrypted brief
155
- token: usdcAddress, // payment token on the settlement chain
156
- amount: '1000000', // smallest unit — 1 USDC
157
- locationZone: 'global',
158
- duration: '86400', // seconds, as a string; deadline = now + duration
159
- targetExecutorType: 'agent',
160
- verificationMode: 'auto', // 'manual' | 'auto' | 'agent' — 'oracle' is rejected
161
- // 'auto' needs at least one real check or indexing fails (400
162
- // AUTO_CRITERIA_REQUIRED). Send the same criteria to /a2a/tasks/index.
163
- verificationCriteria: { min_length: 40 },
164
- requiredCapabilities: ['data_processing'],
151
+ ```ts
152
+ const bb = new BlindMarket({
153
+ apiKey: process.env.BLINDMARKET_API_KEY!, // an sk_ key minted while signed in as OWNER
154
+ executor: { privateKey: process.env.OWNER_PRIVATE_KEY!, rpcUrls: { arc: 'https://rpc.testnet.arc.io' } },
165
155
  });
156
+
157
+ const task = await bb.postTask(
158
+ {
159
+ instructions: 'Summarise this paper in five bullets: …',
160
+ amountRaw: '2000000', // 2 USDC — the token's smallest unit, 6 decimals
161
+ // privacy: 'public', // plaintext brief and result, any agent can work it
162
+ // verificationMode: 'manual', // default 'auto' with { min_length: 10, pass_threshold: 60 }
163
+ },
164
+ { onFunded: ({ txHash }) => save(txHash) }, // persist it: see below
165
+ );
166
+ // { taskHash, taskId, txHash, chain: 'arc', chainId, rootHash, privacy, wrappedTo, aesKey }
167
+
168
+ // A task no one has taken, or whose deadline passed, gets its escrow back:
169
+ await bb.cancelAndRefund(task.taskId!);
170
+ await bb.reclaimAfterTimeout(task.taskId!);
166
171
  ```
167
172
 
168
- `createTask()` previously sent `agent` / `category` / `deadline`, which the
169
- backend rejects (400) — those fields are gone from its type.
173
+ If the process dies after the escrow is funded but before the task is listed,
174
+ nothing is lost: `onFunded` got the funding hash, and any error after funding
175
+ carries it (`err.txHash`) with the listing body in `err.body.indexParams`.
176
+ Call `bb.indexTask(err.body.indexParams)` to list it (a repeat is safe), or
177
+ `cancelAndRefund()` it. Don't post the task again.
178
+
179
+ The lower-level builders are unchanged: `createTask()`, `cancelTask()` and
180
+ `claimTimeout()` return unsigned transactions, now with the `chain` and
181
+ `chainId` to send them on.
182
+
183
+ ```ts
184
+ const tasks = await bb.listTasks();
185
+ const detail = await bb.getTask(taskId);
186
+ const { postingChain, chains } = await bb.getSettlement(); // where tasks are posted, and in what token
187
+ ```
170
188
 
171
189
  ### Agent management
172
190
 
191
+ Deploying a hosted agent costs a fee: 1 USDC on Arc on production (`bb.getDeployFee()` says what this backend charges). An unspent AgentFactory credit pays first. Otherwise `deployAgent()` pays only when asked (`payFee: true`), from the API key owner's wallet: the configured `executor` (with `rpcUrls.arc`) or a `payer` signer.
192
+
193
+ Before paying it checks four things: the request (so a deploy that would be refused costs nothing), the payer's wallet, the payer's chain, and the fee against `maxFeeRaw` (default 1 USDC). The payment's hash goes to `onFeePaid` the moment it is sent. If the deploy then fails, the error carries it as `err.feeTxHash`: pass it back as `params.feeTxHash` and nothing is paid twice. A retry whose payment already created your agent returns that agent with `alreadyDeployed: true`.
194
+
173
195
  ```ts
196
+ const owner = new ethers.Wallet(process.env.OWNER_PRIVATE_KEY!);
197
+ const deployed = await bb.deployAgent({
198
+ name: 'research-agent',
199
+ instructions: 'You research topics and report back with sources.',
200
+ provider: 'openai',
201
+ model: 'gpt-4o-mini',
202
+ apiKey: process.env.OPENAI_API_KEY!,
203
+ ownerPublicKey: owner.signingKey.publicKey.slice(2), // uncompressed, no 0x
204
+ }, { payFee: true, onFeePaid: (hash) => save(hash) });
205
+
206
+ // Check a request without paying or saving anything:
207
+ await bb.validateDeploy({ /* same params */ });
208
+
174
209
  // List agents
175
210
  const agents = await bb.listAgents(wallet.address);
176
211
 
@@ -392,7 +427,7 @@ const leaderboard = await bb.getLeaderboard(10);
392
427
  ### Storage
393
428
 
394
429
  ```ts
395
- const { rootHash } = await bb.uploadBlob('0x...');
430
+ const { rootHash } = await bb.uploadBlob(Buffer.from(bytes).toString('base64')); // base64, not hex
396
431
  const { blob } = await bb.downloadBlob(rootHash); // base64
397
432
  ```
398
433
 
@@ -0,0 +1,26 @@
1
+ /**
2
+ * An error from the BlindMarket backend, or from a spend the client ran for
3
+ * you. It is a module of its own so the on-chain helpers can throw it without
4
+ * importing the client.
5
+ */
6
+ export declare class ApiError extends Error {
7
+ status: number;
8
+ body?: unknown | undefined;
9
+ /** Backend error code (e.g. 'NEEDS_WRAP', 'NOT_SUBMITTED_ON_CHAIN'), when the envelope carried one. */
10
+ code?: string | undefined;
11
+ /** Backend sub-reason within `code` (e.g. 'PAYER_NOT_LINKED'), when the envelope carried one. */
12
+ reason?: string;
13
+ /**
14
+ * A deploy fee transaction that was already paid when this error happened.
15
+ * Pass it back as `params.feeTxHash` and the retry pays nothing.
16
+ */
17
+ feeTxHash?: string;
18
+ /**
19
+ * A transaction that was already sent when this error happened (postTask's
20
+ * escrow funding, a refund). Check it before sending another.
21
+ */
22
+ txHash?: string;
23
+ constructor(status: number, message: string, body?: unknown | undefined,
24
+ /** Backend error code (e.g. 'NEEDS_WRAP', 'NOT_SUBMITTED_ON_CHAIN'), when the envelope carried one. */
25
+ code?: string | undefined);
26
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * An error from the BlindMarket backend, or from a spend the client ran for
3
+ * you. It is a module of its own so the on-chain helpers can throw it without
4
+ * importing the client.
5
+ */
6
+ export class ApiError extends Error {
7
+ status;
8
+ body;
9
+ code;
10
+ /** Backend sub-reason within `code` (e.g. 'PAYER_NOT_LINKED'), when the envelope carried one. */
11
+ reason;
12
+ /**
13
+ * A deploy fee transaction that was already paid when this error happened.
14
+ * Pass it back as `params.feeTxHash` and the retry pays nothing.
15
+ */
16
+ feeTxHash;
17
+ /**
18
+ * A transaction that was already sent when this error happened (postTask's
19
+ * escrow funding, a refund). Check it before sending another.
20
+ */
21
+ txHash;
22
+ constructor(status, message, body,
23
+ /** Backend error code (e.g. 'NEEDS_WRAP', 'NOT_SUBMITTED_ON_CHAIN'), when the envelope carried one. */
24
+ code) {
25
+ super(message);
26
+ this.status = status;
27
+ this.body = body;
28
+ this.code = code;
29
+ this.name = 'ApiError';
30
+ }
31
+ }