@blindmarket/mcp-server 0.3.6 → 0.4.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 +13 -8
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/rent.d.ts.map +1 -1
- package/dist/rent.js +124 -57
- package/dist/rent.js.map +1 -1
- package/dist/settlement.d.ts +63 -13
- package/dist/settlement.d.ts.map +1 -1
- package/dist/settlement.js +105 -14
- package/dist/settlement.js.map +1 -1
- package/dist/state.d.ts +3 -2
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js.map +1 -1
- package/dist/tools.d.ts +11 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +63 -27
- package/dist/tools.js.map +1 -1
- package/dist/wallet.js +3 -3
- package/dist/wallet.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -26,11 +26,11 @@ Environment:
|
|
|
26
26
|
| `BLINDMARKET_PRIVATE_KEY` | 0G: yes · Base: for private briefs | On **0G** this wallet funds escrow in native 0G, pays gas, and signs `submitEvidence` for `complete_task`. On **Base** nothing is *signed* locally — the relay does that — but this key is still the executor's **decryption identity**: `fetch_brief` unwraps a private brief with it, so the pubkey you pass to `register_as_executor` must be the one `wallet_status` reports as `executorPublicKey`. Omit for read-only use or Base tasks that are all public. |
|
|
27
27
|
| `BLINDMARKET_API_BASE` | no | Default `https://api.blindmarket.xyz` |
|
|
28
28
|
| `BLINDMARKET_RPC_URL` | no | 0G RPC for the local wallet. Default `https://evmrpc.0g.ai` |
|
|
29
|
-
| `BLINDMARKET_SETTLEMENT` | no |
|
|
30
|
-
| `BLINDMARKET_BASE_ESCROW_ADDRESS` | with forced `base
|
|
29
|
+
| `BLINDMARKET_SETTLEMENT` | no | A chain key to require (`0g`, `base`, …). Default: ask the backend (`GET /health/bridge`). A backend that names its posting chain (`postingChain`) is followed: new tasks are escrowed there, and that chain's settlement token picks how you pay — an ERC-20 the relay serves (USDC on Base) through the relay, native 0G from the local wallet, anything else refused with `UNSUPPORTED_SETTLEMENT`. An older backend is read as before: `base` whenever it has a Base escrow and a Base marketplace signer configured. `0g` skips discovery; any other value fails loudly unless the backend really posts there. |
|
|
30
|
+
| `BLINDMARKET_BASE_ESCROW_ADDRESS` | with forced `base`, older backends | The escrow the backend builds against, when an older backend's `/health/bridge` cannot confirm it. Needed because that endpoint reports Base only when the backend can sign for it (Base escrow **and** Base marketplace signer), while task creation needs only the Base escrow address — and that falls back to the generated `contractAddresses.ts`, so a backend with an empty Base `.env` still builds Base transactions. Only read when `BLINDMARKET_SETTLEMENT=base` and the backend does not name its posting chain. |
|
|
31
31
|
| `BLINDMARKET_BASE_CHAIN_ID` | no | Chain for that override. Default `84532` (Base Sepolia). |
|
|
32
|
-
| `BLINDMARKET_BASE_RPC_URL` | no | Read-only Base RPC for allowance/balance checks and receipt polling. Default by chain: `https://sepolia.base.org` (84532) / `https://mainnet.base.org` (8453). |
|
|
33
|
-
| `BLINDMARKET_USDC_ADDRESS` | no |
|
|
32
|
+
| `BLINDMARKET_BASE_RPC_URL` | no | Read-only Base RPC for allowance/balance checks and receipt polling. Default by chain: `https://sepolia.base.org` (84532) / `https://mainnet.base.org` (8453). Another relay chain `<key>` reads `BLINDMARKET_<KEY>_RPC_URL` and has no default. |
|
|
33
|
+
| `BLINDMARKET_USDC_ADDRESS` | no | Older backends: override the USDC address if the backend reports a Base chain not listed in `settlement.ts`. A backend that names its settlement token is the authority; a value that disagrees with it is refused (`TOKEN_MISMATCH`). |
|
|
34
34
|
|
|
35
35
|
How a spend is paid, by settlement mode (`wallet_status` shows which you are in):
|
|
36
36
|
|
|
@@ -44,7 +44,11 @@ How a spend is paid, by settlement mode (`wallet_status` shows which you are in)
|
|
|
44
44
|
|
|
45
45
|
**What an `sk_` key can do on Base — read this before putting one in a config file.** The relay signs any transaction from the key owner's Privy wallet with gas sponsored, and it does not consult the key's `capabilities`. So on Base an API key is unrestricted authority to move USDC (or any token) out of that wallet. Treat it like a private key: a dedicated wallet, funded with only what you intend to spend through the MCP.
|
|
46
46
|
|
|
47
|
-
Discovery is a hint, not a proof
|
|
47
|
+
Discovery is a hint, not a proof. On an older backend `/health/bridge` reports Base only when the backend has a Base marketplace signer, while task creation routes on `BASE_ESCROW_ADDRESS` alone; a backend that names its posting chain removes that gap, but not every way a task can live on another chain. Every send therefore checks the unsigned tx targets the escrow the current mode expects and refuses with `ESCROW_MISMATCH` otherwise — that check is what prevents native 0G value being sent to a Base address.
|
|
48
|
+
|
|
49
|
+
`register_as_executor` and `create_agent` declare exactly one chain in `supportedChains`: the one this process settles on (`wallet_status` shows it), because `complete_task` delivers only there. A backend that filters by it then offers you only tasks you can complete, and refuses bids and `/accept` (409 `CHAIN_UNSUPPORTED`) elsewhere — including tasks indexed before chains were recorded, which need both `0g` and `base` declared. If the chain cannot be learned, registration is refused rather than sent without one.
|
|
50
|
+
|
|
51
|
+
`BLINDMARKET_SETTLEMENT` may name any chain the backend has an escrow on, not only the one it posts on: that is how you deliver, cancel or reclaim a task left on a chain the backend has since stopped posting on. `post_task` and `rent_service` refuse there (`NOT_POSTING_CHAIN`), because new escrow is funded only on the posting chain. On 0G the local wallet must be on the backend's 0G chain id (`CHAIN_MISMATCH` otherwise) — checked only when the backend names its chains and discovery runs: `BLINDMARKET_SETTLEMENT=0g` skips discovery, and an older backend does not say.
|
|
48
52
|
|
|
49
53
|
### Base: what the wallet and key must be
|
|
50
54
|
|
|
@@ -69,7 +73,7 @@ claude mcp add blindmarket \
|
|
|
69
73
|
-- node /path/to/BlindBounty/mcp/dist/index.js
|
|
70
74
|
```
|
|
71
75
|
|
|
72
|
-
(`BLINDMARKET_BASE_ESCROW_ADDRESS`
|
|
76
|
+
(`BLINDMARKET_SETTLEMENT` and `BLINDMARKET_BASE_ESCROW_ADDRESS` are only needed while an older backend's `/health/bridge` cannot confirm Base — see the env table. Use the mainnet escrow once deployed.)
|
|
73
77
|
|
|
74
78
|
**Claude Code — 0G (legacy, local wallet)**
|
|
75
79
|
|
|
@@ -133,8 +137,9 @@ live one. It signs `submitEvidence` **locally** (no relay): it needs
|
|
|
133
137
|
`BLINDMARKET_PRIVATE_KEY` to be the wallet that owns `BLINDMARKET_API_KEY`, and
|
|
134
138
|
an RPC for the settlement chain (`BLINDMARKET_RPC_URL` for 0G,
|
|
135
139
|
`BLINDMARKET_BASE_RPC_URL` for Base — there is no default for Base, and tasks
|
|
136
|
-
on a chain without an RPC are skipped by the runtime itself:
|
|
137
|
-
the declared `supportedChains`
|
|
140
|
+
on a chain without an RPC are skipped by the runtime itself: older backends
|
|
141
|
+
store the declared `supportedChains` without filtering offers or `/accept` by
|
|
142
|
+
it).
|
|
138
143
|
Without `BLINDMARKET_PRIVATE_KEY` the runtime refuses to start (SDK 0.6.0) —
|
|
139
144
|
it no longer registers a throwaway wallet. `BLINDMARKET_EXPERIMENTAL_RUNTIME=true`
|
|
140
145
|
enables it. The maintained way to EARN is still a platform agent deployed in
|
package/dist/index.js
CHANGED
|
@@ -35,11 +35,15 @@ const server = new McpServer({
|
|
|
35
35
|
// rent/post through the CURRENT encrypted flow (see rent.ts). Registered even
|
|
36
36
|
// without a wallet so tools/list is stable; spends fail cleanly with NO_WALLET.
|
|
37
37
|
const walletCtx = loadWallet();
|
|
38
|
-
// Register all marketplace tools
|
|
39
|
-
|
|
38
|
+
// Register all marketplace tools. They register first (tools/list order is
|
|
39
|
+
// unchanged), but register_as_executor/create_agent need rent.ts's settlement
|
|
40
|
+
// resolver, which is created just below — hence the late-bound getter.
|
|
41
|
+
let resolveSettlement = null;
|
|
42
|
+
registerMarketTools(server, bb, walletCtx, () => resolveSettlement());
|
|
40
43
|
// rent.ts owns settlement discovery (which chain escrow settles on, and so
|
|
41
44
|
// whether the local wallet or the Privy relay pays); wallet_status reports it.
|
|
42
45
|
const { settlement } = registerRentTools(server, cfg, walletCtx);
|
|
46
|
+
resolveSettlement = settlement;
|
|
43
47
|
registerWalletTools(server, walletCtx, settlement);
|
|
44
48
|
// Executor runtime tools (runtime_start/stop/…) are GATED OFF by default.
|
|
45
49
|
// The three bugs this gate used to cite (wrappedKey parsed as a record, blob
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/rent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rent.d.ts","sourceRoot":"","sources":["../src/rent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,
|
|
1
|
+
{"version":3,"file":"rent.d.ts","sourceRoot":"","sources":["../src/rent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,EAAkD,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAmDlG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG;IAAE,UAAU,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,CAwuC3I"}
|
package/dist/rent.js
CHANGED
|
@@ -95,8 +95,9 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
95
95
|
}
|
|
96
96
|
const settlement = createSettlementResolver({ apiBase: cfg.apiBase ?? 'https://api.blindmarket.xyz', api });
|
|
97
97
|
/** How this process pays. On 0G that is the local wallet, which must exist.
|
|
98
|
-
* On Base nothing signs locally — the relay signs from the
|
|
99
|
-
* wallet — so a missing BLINDMARKET_PRIVATE_KEY is not an
|
|
98
|
+
* On a relay chain (Base) nothing signs locally — the relay signs from the
|
|
99
|
+
* API key's owner wallet — so a missing BLINDMARKET_PRIVATE_KEY is not an
|
|
100
|
+
* error there. */
|
|
100
101
|
async function requireFunding() {
|
|
101
102
|
let s;
|
|
102
103
|
try {
|
|
@@ -105,26 +106,44 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
105
106
|
catch (err) {
|
|
106
107
|
return { error: fail(err.code ?? 'SETTLEMENT_UNKNOWN', err.message) };
|
|
107
108
|
}
|
|
108
|
-
if (s.
|
|
109
|
+
if (s.payment === 'relay-erc20')
|
|
109
110
|
return { s, payFrom: s.payFrom };
|
|
110
111
|
if (!walletCtx) {
|
|
111
112
|
return { error: fail('NO_WALLET', 'Spending on 0G needs a local funding wallet — set BLINDMARKET_PRIVATE_KEY (see wallet_status)') };
|
|
112
113
|
}
|
|
114
|
+
// The escrow address is compared before every send, but not the chain:
|
|
115
|
+
// a testnet backend's escrow address, paid on the mainnet RPC, is some
|
|
116
|
+
// other account there. A backend that names its 0G chain id settles it.
|
|
117
|
+
if (s.chainId !== undefined && walletCtx.chainId !== s.chainId) {
|
|
118
|
+
return {
|
|
119
|
+
error: fail('CHAIN_MISMATCH', `The backend settles 0G on chain ${s.chainId}, but BLINDMARKET_PRIVATE_KEY signs on chain ${walletCtx.chainId} (BLINDMARKET_RPC_URL / BLINDMARKET_CHAIN_ID). Native value sent there would not reach this backend's escrow. Point both at chain ${s.chainId}.`),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
113
122
|
return { s, payFrom: walletCtx.wallet.address };
|
|
114
123
|
}
|
|
124
|
+
/** A new escrow is funded where POST /api/v1/tasks builds: the backend's
|
|
125
|
+
* posting chain. A process forced onto another chain (to finish or refund
|
|
126
|
+
* tasks already there) cannot post. Unknown on an older backend. */
|
|
127
|
+
function notPostingChain(s) {
|
|
128
|
+
if (s.postingChain === undefined || s.mode === s.postingChain)
|
|
129
|
+
return null;
|
|
130
|
+
const e = new Error(`This process settles on ${s.mode} (BLINDMARKET_SETTLEMENT), but the backend posts new tasks on ${s.postingChain}, so a new escrow can only be funded there. Unset BLINDMARKET_SETTLEMENT (or set it to ${s.postingChain}) to post; ${s.mode} stays usable for tasks already on it.`);
|
|
131
|
+
e.code = 'NOT_POSTING_CHAIN';
|
|
132
|
+
return e;
|
|
133
|
+
}
|
|
115
134
|
const ERC20 = new Interface([
|
|
116
135
|
'function allowance(address owner, address spender) view returns (uint256)',
|
|
117
136
|
'function approve(address spender, uint256 amount) returns (bool)',
|
|
118
137
|
'function balanceOf(address owner) view returns (uint256)',
|
|
119
138
|
]);
|
|
120
|
-
function
|
|
121
|
-
return new Contract(s.
|
|
139
|
+
function settlementToken(s) {
|
|
140
|
+
return new Contract(s.token.address, ERC20, s.provider);
|
|
122
141
|
}
|
|
123
142
|
/** Spendable balance of whoever pays, in the settlement token's units. */
|
|
124
143
|
async function payFromBalance(s, payFrom) {
|
|
125
144
|
try {
|
|
126
|
-
const raw = s.
|
|
127
|
-
? await
|
|
145
|
+
const raw = s.payment === 'relay-erc20'
|
|
146
|
+
? await settlementToken(s).balanceOf(payFrom)
|
|
128
147
|
: await walletCtx.provider.getBalance(payFrom);
|
|
129
148
|
return formatUnits(raw, s.decimals);
|
|
130
149
|
}
|
|
@@ -136,19 +155,22 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
136
155
|
* the task, and the tx carries no chainId. Discovery is only a hint about
|
|
137
156
|
* that chain (see settlement.ts). So before broadcasting, check the tx
|
|
138
157
|
* targets the escrow THIS mode expects — otherwise native value goes to a
|
|
139
|
-
*
|
|
140
|
-
* mismatch also drops the cached mode so the next call
|
|
158
|
+
* relay chain's address on the 0G RPC, or a 0G refund is relayed onto
|
|
159
|
+
* another chain. A mismatch also drops the cached mode so the next call
|
|
160
|
+
* re-asks. */
|
|
141
161
|
async function verifyTarget(s, to, what) {
|
|
142
162
|
const target = String(to).toLowerCase();
|
|
143
|
-
const expected = s.
|
|
163
|
+
const expected = s.escrowAddress;
|
|
144
164
|
if (expected) {
|
|
145
165
|
if (target === expected.toLowerCase())
|
|
146
166
|
return;
|
|
147
167
|
settlement.invalidate();
|
|
148
168
|
const e = new Error(`backend built ${what} for ${to} but this process is in ${s.mode} mode expecting escrow ${expected}. ` +
|
|
149
|
-
(s.
|
|
150
|
-
?
|
|
151
|
-
:
|
|
169
|
+
(s.escrowChains
|
|
170
|
+
? `This task is escrowed on another chain — set BLINDMARKET_SETTLEMENT to the chain that holds it (this backend has escrows on ${s.escrowChains.join(', ')}; 0G needs a local key), or use the web app.`
|
|
171
|
+
: s.payment === 'relay-erc20'
|
|
172
|
+
? 'This task is escrowed on 0G — handle it with BLINDMARKET_SETTLEMENT=0g and a local key, or from the web app.'
|
|
173
|
+
: 'The backend is building Base transactions — re-run and discovery will re-check, or set BLINDMARKET_SETTLEMENT=base.'));
|
|
152
174
|
e.code = 'ESCROW_MISMATCH';
|
|
153
175
|
throw e;
|
|
154
176
|
}
|
|
@@ -214,17 +236,17 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
214
236
|
e.code = 'TX_PENDING';
|
|
215
237
|
throw e;
|
|
216
238
|
}
|
|
217
|
-
/**
|
|
218
|
-
* allowance first. Confirmed by re-reading allowance()
|
|
219
|
-
* receipt, which is what makes the user-op case decidable. */
|
|
239
|
+
/** Relay chains only: createTask pulls the ERC-20 via transferFrom, so the
|
|
240
|
+
* escrow needs an allowance first. Confirmed by re-reading allowance()
|
|
241
|
+
* rather than by receipt, which is what makes the user-op case decidable. */
|
|
220
242
|
async function ensureAllowance(s, record) {
|
|
221
243
|
const need = BigInt(record.amountWei);
|
|
222
|
-
const token =
|
|
244
|
+
const token = settlementToken(s);
|
|
223
245
|
if ((await token.allowance(s.payFrom, s.escrowAddress)) >= need)
|
|
224
246
|
return;
|
|
225
247
|
const approve = async () => {
|
|
226
248
|
const data = ERC20.encodeFunctionData('approve', [s.escrowAddress, need]);
|
|
227
|
-
const { hash } = await relaySend(s, { to: s.
|
|
249
|
+
const { hash } = await relaySend(s, { to: s.token.address, data });
|
|
228
250
|
// Persist BEFORE waiting: a crash here must resume into the poll below.
|
|
229
251
|
updateSpend(record.idempotencyKey, { stage: 'approved', approveTxHash: hash });
|
|
230
252
|
record.stage = 'approved';
|
|
@@ -252,19 +274,19 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
252
274
|
await approve();
|
|
253
275
|
if (await settled())
|
|
254
276
|
return;
|
|
255
|
-
const e = new Error(
|
|
277
|
+
const e = new Error(`${s.symbol} allowance still below ${formatUnits(need, s.decimals)} after two approves (last ${record.approveTxHash}) — check the relay wallet's ${s.symbol} balance and retry with the same idempotencyKey`);
|
|
256
278
|
e.code = 'APPROVE_PENDING';
|
|
257
279
|
throw e;
|
|
258
280
|
}
|
|
259
281
|
/** Fund escrow + index — the shared tail of rent_service and post_task.
|
|
260
282
|
* Resumable at every stage via the spend ledger.
|
|
261
283
|
*
|
|
262
|
-
* Two funding paths, chosen by
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
* createTask the backend built,
|
|
266
|
-
* no local signing at all. The
|
|
267
|
-
* check it is the one we approved. */
|
|
284
|
+
* Two funding paths, chosen by `payment`:
|
|
285
|
+
* local-native — native 0G from the local wallet, signed and sent here.
|
|
286
|
+
* relay-erc20 — the ERC-20 (USDC on Base) via transferFrom: approve
|
|
287
|
+
* first (ensureAllowance), then the createTask the backend built,
|
|
288
|
+
* both through the Privy relay with no local signing at all. The
|
|
289
|
+
* backend picks the escrow; we only check it is the one we approved. */
|
|
268
290
|
async function fundAndIndex(record) {
|
|
269
291
|
const s = await settlement();
|
|
270
292
|
let { txHash } = record;
|
|
@@ -277,11 +299,14 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
277
299
|
throw e;
|
|
278
300
|
}
|
|
279
301
|
if (record.stage === 'created' || record.stage === 'approved') {
|
|
280
|
-
|
|
302
|
+
const refused = notPostingChain(s);
|
|
303
|
+
if (refused)
|
|
304
|
+
throw refused;
|
|
305
|
+
if (s.payment === 'relay-erc20')
|
|
281
306
|
await ensureAllowance(s, record);
|
|
282
|
-
const { unsignedTx } = await api('POST', '/api/v1/tasks', {
|
|
307
|
+
const { unsignedTx, chain: builtChain, chainId: builtChainId } = await api('POST', '/api/v1/tasks', {
|
|
283
308
|
taskHash: record.taskHash,
|
|
284
|
-
token: s.
|
|
309
|
+
token: s.payment === 'relay-erc20' ? s.token.address : ZERO_TOKEN,
|
|
285
310
|
amount: record.amountWei,
|
|
286
311
|
locationZone: 'global',
|
|
287
312
|
duration: String(record.durationSecs ?? 3600),
|
|
@@ -292,10 +317,19 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
292
317
|
rootHash: record.rootHash,
|
|
293
318
|
wrappedKeys: record.privacy === 'public' ? undefined : record.wrappedKeys,
|
|
294
319
|
});
|
|
295
|
-
// Either branch: the tx must target the escrow this mode expects. On
|
|
296
|
-
//
|
|
320
|
+
// Either branch: the tx must target the escrow this mode expects. On a
|
|
321
|
+
// relay chain that is also the escrow the allowance above was granted to.
|
|
322
|
+
// A chain-aware backend also names the chain it built for (older ones
|
|
323
|
+
// return only the tx, and are checked by address alone, as before).
|
|
324
|
+
if (s.postingChain !== undefined && ((builtChain !== undefined && builtChain !== s.mode) ||
|
|
325
|
+
(builtChainId !== undefined && s.chainId !== undefined && Number(builtChainId) !== s.chainId))) {
|
|
326
|
+
settlement.invalidate();
|
|
327
|
+
const e = new Error(`backend built createTask for ${builtChain ?? '?'} (chain ${builtChainId ?? '?'}) but this process settles on ${s.mode} (chain ${s.chainId ?? '?'}) — re-run and discovery will re-check`);
|
|
328
|
+
e.code = 'ESCROW_MISMATCH';
|
|
329
|
+
throw e;
|
|
330
|
+
}
|
|
297
331
|
await verifyTarget(s, unsignedTx.to, 'createTask');
|
|
298
|
-
if (s.
|
|
332
|
+
if (s.payment === 'relay-erc20') {
|
|
299
333
|
const { hash, isUserOp, gas } = await relaySend(s, { to: unsignedTx.to, data: unsignedTx.data });
|
|
300
334
|
// Persist BEFORE waiting, same reasoning as the 0G branch below.
|
|
301
335
|
updateSpend(record.idempotencyKey, { stage: 'funded', txHash: hash, isUserOp, gas });
|
|
@@ -310,6 +344,8 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
310
344
|
data: unsignedTx.data,
|
|
311
345
|
value: BigInt(record.amountWei),
|
|
312
346
|
gasLimit: GAS_LIMIT,
|
|
347
|
+
// ethers refuses to send when its provider is on another chain.
|
|
348
|
+
...(s.chainId !== undefined ? { chainId: s.chainId } : {}),
|
|
313
349
|
});
|
|
314
350
|
// Persist the tx hash BEFORE waiting: if we crash mid-confirmation the
|
|
315
351
|
// resume path re-runs /tasks/index with this hash instead of re-funding.
|
|
@@ -393,6 +429,9 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
393
429
|
return fail(err.code ?? 'RESUME_FAILED', err.message);
|
|
394
430
|
}
|
|
395
431
|
}
|
|
432
|
+
const offPosting = notPostingChain(s);
|
|
433
|
+
if (offPosting)
|
|
434
|
+
return fail(offPosting.code, offPosting.message);
|
|
396
435
|
const service = await api('GET', `/api/v1/marketplace/services/${serviceId}`);
|
|
397
436
|
const isPublic = privacy === 'public';
|
|
398
437
|
if (!isPublic && !service.agent_public_key) {
|
|
@@ -405,10 +444,10 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
405
444
|
// A service priced in 18-decimal units on a 6-decimal chain would quote
|
|
406
445
|
// as a trillion USDC and relay an approve for it before createTask
|
|
407
446
|
// failed. The backend converts such prices since migration 31, but an
|
|
408
|
-
// older backend may still serve one. 1,000,000
|
|
447
|
+
// older backend may still serve one. 1,000,000 tokens per call is far
|
|
409
448
|
// above any real listing — refuse.
|
|
410
|
-
if (s.
|
|
411
|
-
return fail('PRICE_UNITS_SUSPECT', `service ${serviceId} lists price_raw=${priceRaw} which is ${formatUnits(priceRaw,
|
|
449
|
+
if (s.decimals < 18 && priceRaw > 1000000n * 10n ** BigInt(s.decimals)) {
|
|
450
|
+
return fail('PRICE_UNITS_SUSPECT', `service ${serviceId} lists price_raw=${priceRaw} which is ${formatUnits(priceRaw, s.decimals)} ${s.symbol} — this looks like an 18-decimal 0G price on a ${s.symbol} chain. Not sending. Re-list the service in ${s.symbol} base units.`);
|
|
412
451
|
}
|
|
413
452
|
const price = formatUnits(priceRaw, s.decimals);
|
|
414
453
|
if (!confirm) {
|
|
@@ -467,7 +506,7 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
467
506
|
requiredCapabilities: [],
|
|
468
507
|
amountWei: String(service.price_raw),
|
|
469
508
|
settlement: s.mode,
|
|
470
|
-
token: s.
|
|
509
|
+
token: s.payment === 'relay-erc20' ? s.token.address : ZERO_TOKEN,
|
|
471
510
|
durationSecs: 3600,
|
|
472
511
|
createdAt: new Date().toISOString(),
|
|
473
512
|
updatedAt: new Date().toISOString(),
|
|
@@ -518,6 +557,9 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
518
557
|
return fail(err.code ?? 'RESUME_FAILED', err.message);
|
|
519
558
|
}
|
|
520
559
|
}
|
|
560
|
+
const offPosting = notPostingChain(s);
|
|
561
|
+
if (offPosting)
|
|
562
|
+
return fail(offPosting.code, offPosting.message);
|
|
521
563
|
const isPublic = privacy === 'public';
|
|
522
564
|
const amountStr = amount ?? amount0G;
|
|
523
565
|
if (!amountStr) {
|
|
@@ -595,7 +637,7 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
595
637
|
requiredCapabilities: capabilities ?? [],
|
|
596
638
|
amountWei: amountWei.toString(),
|
|
597
639
|
settlement: s.mode,
|
|
598
|
-
token: s.
|
|
640
|
+
token: s.payment === 'relay-erc20' ? s.token.address : ZERO_TOKEN,
|
|
599
641
|
durationSecs: durationSeconds ?? 86400,
|
|
600
642
|
createdAt: new Date().toISOString(),
|
|
601
643
|
updatedAt: new Date().toISOString(),
|
|
@@ -643,7 +685,8 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
643
685
|
//
|
|
644
686
|
// Chain note: the backend resolves which chain holds the task
|
|
645
687
|
// (resolveTaskChainById) and builds the unsigned tx for it; this side only
|
|
646
|
-
// decides who signs — the local wallet on 0G, the Privy relay on
|
|
688
|
+
// decides who signs — the local wallet on 0G, the Privy relay on a relay
|
|
689
|
+
// chain (Base) — and
|
|
647
690
|
// verifies the tx targets the escrow that mode expects before sending
|
|
648
691
|
// (verifyTarget). Task state is read from the chain the mode names, not
|
|
649
692
|
// from GET /tasks/:id, which is bound to the 0G escrow.
|
|
@@ -653,32 +696,55 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
653
696
|
}
|
|
654
697
|
/** Resolve a task id-or-hash to its live on-chain state.
|
|
655
698
|
*
|
|
656
|
-
* GET /api/v1/tasks/:id accepts either form — but
|
|
657
|
-
* only (escrowService.getTask
|
|
658
|
-
* struct is for whatever 0G task happens to share the
|
|
659
|
-
* use it just to turn a hash into an id, then read
|
|
660
|
-
*
|
|
699
|
+
* GET /api/v1/tasks/:id accepts either form — but older backends read the
|
|
700
|
+
* 0G escrow only (escrowService.getTask was bound to the 0G contract), so
|
|
701
|
+
* on a relay chain its struct is for whatever 0G task happens to share the
|
|
702
|
+
* id. On a relay chain we use it just to turn a hash into an id, then read
|
|
703
|
+
* the struct from that chain's escrow ourselves over the read-only
|
|
704
|
+
* provider. */
|
|
661
705
|
async function loadTask(s, task) {
|
|
662
|
-
|
|
663
|
-
|
|
706
|
+
/** The backend said the task is on another chain: its id means a different task here. */
|
|
707
|
+
const onOtherChain = (taskId, chain) => {
|
|
708
|
+
const label = s.chain === 'base' ? 'Base' : s.chain;
|
|
709
|
+
const e = new Error(`task ${taskId} is a ${chain} task, and this process settles on ${label} — handle it with BLINDMARKET_SETTLEMENT=${chain}`);
|
|
710
|
+
// The code names the chain; on Base it is the TASK_NOT_ON_BASE it always was.
|
|
711
|
+
e.code = `TASK_NOT_ON_${s.chain.toUpperCase().replace(/-/g, '_')}`;
|
|
712
|
+
return e;
|
|
713
|
+
};
|
|
714
|
+
if (s.payment !== 'relay-erc20') {
|
|
715
|
+
const detail = await api('GET', `/api/v1/tasks/${encodeURIComponent(task)}`);
|
|
716
|
+
if (detail.chain && detail.chain !== s.chain)
|
|
717
|
+
throw onOtherChain(detail.taskId, detail.chain);
|
|
718
|
+
return detail;
|
|
664
719
|
}
|
|
665
|
-
//
|
|
720
|
+
// Relay chain: the escrow is the authority, and we can read it directly. Only ask
|
|
666
721
|
// the backend when we need a hash resolved to an id — that endpoint makes
|
|
667
722
|
// several Redis round trips and is the slowest thing in this path, so a
|
|
668
723
|
// numeric id must not pay for it.
|
|
669
724
|
let taskId;
|
|
725
|
+
let backendChain;
|
|
670
726
|
if (/^\d+$/.test(task)) {
|
|
671
727
|
taskId = task;
|
|
672
728
|
}
|
|
673
729
|
else {
|
|
674
730
|
const viaBackend = await api('GET', `/api/v1/tasks/${encodeURIComponent(task)}`);
|
|
675
731
|
taskId = viaBackend.taskId;
|
|
732
|
+
backendChain = viaBackend.chain;
|
|
733
|
+
// The same id can exist on this chain's escrow too (another task of the
|
|
734
|
+
// same poster): reading it would act on the wrong task.
|
|
735
|
+
if (backendChain && backendChain !== s.chain)
|
|
736
|
+
throw onOtherChain(taskId, backendChain);
|
|
676
737
|
}
|
|
677
738
|
const escrow = new Contract(s.escrowAddress, ESCROW_READ_ABI, s.provider);
|
|
678
739
|
const t = await escrow.getTask(BigInt(taskId));
|
|
679
740
|
if (String(t.agent).toLowerCase() === ZERO_TOKEN) {
|
|
680
|
-
const
|
|
681
|
-
|
|
741
|
+
const label = s.chain === 'base' ? 'Base' : s.chain;
|
|
742
|
+
const hint = s.escrowChains
|
|
743
|
+
? `it is on another chain; set BLINDMARKET_SETTLEMENT to the one that holds it (this backend has escrows on ${s.escrowChains.join(', ')})`
|
|
744
|
+
: 'it is probably a 0G task; handle it with BLINDMARKET_SETTLEMENT=0g';
|
|
745
|
+
const e = new Error(`task ${taskId} does not exist on the ${label} escrow ${s.escrowAddress} — ${hint}`);
|
|
746
|
+
// The code names the chain; on Base it is the TASK_NOT_ON_BASE it always was.
|
|
747
|
+
e.code = `TASK_NOT_ON_${s.chain.toUpperCase().replace(/-/g, '_')}`;
|
|
682
748
|
throw e;
|
|
683
749
|
}
|
|
684
750
|
return {
|
|
@@ -692,8 +758,8 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
692
758
|
decimals: s.decimals,
|
|
693
759
|
};
|
|
694
760
|
}
|
|
695
|
-
/** Amount formatted against the task's OWN decimals — a
|
|
696
|
-
* USDC
|
|
761
|
+
/** Amount formatted against the task's OWN decimals — a relay-chain task
|
|
762
|
+
* settles in its ERC-20 (USDC: 6), not the wallet's native 18. */
|
|
697
763
|
function refundAmount(detail) {
|
|
698
764
|
return formatUnits(BigInt(detail.amount), detail.decimals ?? 18);
|
|
699
765
|
}
|
|
@@ -714,7 +780,7 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
714
780
|
}
|
|
715
781
|
/** Broadcast the refund and wait for it — the shared tail of both tools.
|
|
716
782
|
* Resumable: a record past 'created' waits on the tx it already saved.
|
|
717
|
-
* Same two paths as fundAndIndex: local wallet on 0G, Privy relay on
|
|
783
|
+
* Same two paths as fundAndIndex: local wallet on 0G, Privy relay on a relay chain.
|
|
718
784
|
* The backend resolves which chain holds the task and builds the tx for
|
|
719
785
|
* it; this only decides who signs. */
|
|
720
786
|
async function sendRefund(record) {
|
|
@@ -731,10 +797,10 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
731
797
|
const { unsignedTx } = await api('POST', `/api/v1/tasks/${taskId}/${route}`);
|
|
732
798
|
// The backend resolves the chain that holds the task and builds for it;
|
|
733
799
|
// the tx carries no chainId. If that chain is not the one this mode
|
|
734
|
-
// broadcasts on, stop here — relaying a 0G refund onto
|
|
735
|
-
// address with no escrow and burns the gas.
|
|
800
|
+
// broadcasts on, stop here — relaying a 0G refund onto another chain
|
|
801
|
+
// lands on an address with no escrow and burns the gas.
|
|
736
802
|
await verifyTarget(s, unsignedTx.to, `${route}Task`);
|
|
737
|
-
if (s.
|
|
803
|
+
if (s.payment === 'relay-erc20') {
|
|
738
804
|
const { hash, isUserOp, gas } = await relaySend(s, { to: unsignedTx.to, data: unsignedTx.data });
|
|
739
805
|
// Persist BEFORE waiting, same reasoning as the 0G branch below.
|
|
740
806
|
updateSpend(record.idempotencyKey, { stage: 'sent', txHash: hash, isUserOp, gas });
|
|
@@ -748,6 +814,7 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
748
814
|
to: unsignedTx.to,
|
|
749
815
|
data: unsignedTx.data,
|
|
750
816
|
gasLimit: GAS_LIMIT,
|
|
817
|
+
...(s.chainId !== undefined ? { chainId: s.chainId } : {}),
|
|
751
818
|
});
|
|
752
819
|
// Persist the hash BEFORE waiting, same reasoning as fundAndIndex: a
|
|
753
820
|
// crash mid-confirmation must resume onto THIS tx, not broadcast another.
|
|
@@ -757,7 +824,7 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
757
824
|
}
|
|
758
825
|
}
|
|
759
826
|
else if (txHash) {
|
|
760
|
-
if (s.
|
|
827
|
+
if (s.payment === 'relay-erc20')
|
|
761
828
|
await waitRelayed(s, txHash, record.isUserOp ?? false);
|
|
762
829
|
else
|
|
763
830
|
await walletCtx.provider.waitForTransaction(txHash);
|
|
@@ -1029,7 +1096,7 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
1029
1096
|
return f.error;
|
|
1030
1097
|
const { s, payFrom } = f;
|
|
1031
1098
|
// requireFunding returns an error on 0G without a wallet, so walletCtx is
|
|
1032
|
-
// non-null whenever s.
|
|
1099
|
+
// non-null whenever s.payment is 'local-native' from here on.
|
|
1033
1100
|
let detail;
|
|
1034
1101
|
try {
|
|
1035
1102
|
detail = await loadTask(s, task);
|
|
@@ -1085,7 +1152,7 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
1085
1152
|
}
|
|
1086
1153
|
const tx = sub.unsignedSubmitEvidence;
|
|
1087
1154
|
await verifyTarget(s, tx.to, 'submitEvidence');
|
|
1088
|
-
if (s.
|
|
1155
|
+
if (s.payment === 'relay-erc20') {
|
|
1089
1156
|
const sent = await relaySend(s, { to: tx.to, data: tx.data });
|
|
1090
1157
|
submitTxHash = sent.hash;
|
|
1091
1158
|
gas = sent.gas;
|
|
@@ -1130,7 +1197,7 @@ export function registerRentTools(server, cfg, walletCtx) {
|
|
|
1130
1197
|
paidTo: done ? payFrom : undefined,
|
|
1131
1198
|
submissionAttempts: after.submissionAttempts,
|
|
1132
1199
|
hint: done
|
|
1133
|
-
? `Escrow released: ${formatUnits(BigInt(after.amount), after.decimals ??
|
|
1200
|
+
? `Escrow released: ${formatUnits(BigInt(after.amount), after.decimals ?? s.decimals)} ${s.symbol} minus the marketplace fee is now in ${payFrom}.`
|
|
1134
1201
|
: fin.awaitingPosterApproval
|
|
1135
1202
|
? 'Manual-verification task: the poster must approve via verify_task before the escrow releases.'
|
|
1136
1203
|
: `Verification did not pass (${(fin.verificationResult?.reasons ?? []).join('; ') || 'no reasons given'}). ` +
|