@escro/sdk 0.2.0 → 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 +6 -14
- package/dist/index.cjs +384 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +239 -119
- package/dist/index.d.ts +239 -119
- package/dist/index.js +378 -108
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
package/dist/index.cjs
CHANGED
|
@@ -40,6 +40,7 @@ __export(index_exports, {
|
|
|
40
40
|
ApiClient: () => ApiClient,
|
|
41
41
|
ENABLED_RAILS: () => ENABLED_RAILS,
|
|
42
42
|
Escro: () => Escro,
|
|
43
|
+
EscroConfirmError: () => EscroConfirmError,
|
|
43
44
|
EscroError: () => EscroError,
|
|
44
45
|
EscroRailNotEnabledError: () => EscroRailNotEnabledError,
|
|
45
46
|
EscrowNotFoundError: () => EscrowNotFoundError,
|
|
@@ -133,7 +134,7 @@ __export(index_exports, {
|
|
|
133
134
|
module.exports = __toCommonJS(index_exports);
|
|
134
135
|
|
|
135
136
|
// src/client.ts
|
|
136
|
-
var
|
|
137
|
+
var import_web33 = require("@solana/web3.js");
|
|
137
138
|
|
|
138
139
|
// ../shared/dist/index.js
|
|
139
140
|
var import_crypto = require("crypto");
|
|
@@ -152,20 +153,10 @@ var EscrowState = /* @__PURE__ */ ((EscrowState2) => {
|
|
|
152
153
|
EscrowState2["REFUNDED"] = "REFUNDED";
|
|
153
154
|
return EscrowState2;
|
|
154
155
|
})(EscrowState || {});
|
|
155
|
-
function canonicalize(value) {
|
|
156
|
-
if (value === null) return "null";
|
|
157
|
-
if (Array.isArray(value)) {
|
|
158
|
-
return "[" + value.map((v) => v === void 0 ? "null" : canonicalize(v)).join(",") + "]";
|
|
159
|
-
}
|
|
160
|
-
if (typeof value === "object") {
|
|
161
|
-
const obj = value;
|
|
162
|
-
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
163
|
-
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k])).join(",") + "}";
|
|
164
|
-
}
|
|
165
|
-
return JSON.stringify(value);
|
|
166
|
-
}
|
|
167
156
|
var PROGRAM_ID_DEVNET = "EjLpJ3obUYk1f2KDensU7UdGBZTxSdxufm7CSnMA3H5C";
|
|
168
157
|
var PROGRAM_ID_MAINNET = "4CwLNRF4mFXutiBGjtMHQBxh8whW8T8EyvJXdRhY1rvr";
|
|
158
|
+
var CURRENT_ACCOUNT_VERSION = 1;
|
|
159
|
+
var MAX_DISPUTE_REASON_BYTES = 200;
|
|
169
160
|
var ONCHAIN_REFUND_GRACE_SECONDS = 900;
|
|
170
161
|
var ONCHAIN_TIMEOUT_SECONDS = 86400;
|
|
171
162
|
var CLOCK_SKEW_SECONDS = 60;
|
|
@@ -185,6 +176,26 @@ function assertProgramIdNotPlaceholder(id, cluster) {
|
|
|
185
176
|
);
|
|
186
177
|
}
|
|
187
178
|
}
|
|
179
|
+
function canonicalize(value) {
|
|
180
|
+
if (value === null) return "null";
|
|
181
|
+
if (Array.isArray(value)) {
|
|
182
|
+
return "[" + value.map((v) => v === void 0 ? "null" : canonicalize(v)).join(",") + "]";
|
|
183
|
+
}
|
|
184
|
+
if (typeof value === "object") {
|
|
185
|
+
const obj = value;
|
|
186
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
187
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k])).join(",") + "}";
|
|
188
|
+
}
|
|
189
|
+
return JSON.stringify(value);
|
|
190
|
+
}
|
|
191
|
+
function disputeReasonError(reason) {
|
|
192
|
+
if (reason.length === 0) return "reason must not be empty";
|
|
193
|
+
const bytes = new TextEncoder().encode(reason).length;
|
|
194
|
+
if (bytes > MAX_DISPUTE_REASON_BYTES) {
|
|
195
|
+
return `reason is ${String(bytes)} bytes in UTF-8; the program accepts at most ${String(MAX_DISPUTE_REASON_BYTES)}`;
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
188
199
|
function hashBody(rawBody) {
|
|
189
200
|
return (0, import_crypto.createHash)("sha256").update(rawBody, "utf8").digest("hex");
|
|
190
201
|
}
|
|
@@ -230,6 +241,26 @@ function verifyBotDescriptorSignature(descriptor) {
|
|
|
230
241
|
return false;
|
|
231
242
|
}
|
|
232
243
|
}
|
|
244
|
+
function spec(discriminator, escrowAccountIndex, cause) {
|
|
245
|
+
return {
|
|
246
|
+
discriminator: Uint8Array.from(discriminator),
|
|
247
|
+
escrowAccountIndex,
|
|
248
|
+
signerAccountIndex: 0,
|
|
249
|
+
cause
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
var ESCROW_INSTRUCTIONS = {
|
|
253
|
+
create_escrow: spec([253, 215, 165, 116, 36, 108, 68, 80], 4, null),
|
|
254
|
+
claim_task: spec([49, 222, 219, 238, 155, 68, 221, 136], 1, null),
|
|
255
|
+
submit_deliverable: spec([38, 137, 64, 44, 237, 11, 125, 101], 1, null),
|
|
256
|
+
raise_dispute: spec([41, 243, 1, 51, 150, 95, 246, 73], 1, null),
|
|
257
|
+
cancel_escrow: spec([156, 203, 54, 179, 38, 72, 33, 21], 3, "CANCELLED"),
|
|
258
|
+
release_payment: spec([24, 34, 191, 86, 145, 160, 183, 233], 3, "EXPLICIT_RELEASE"),
|
|
259
|
+
timeout_release: spec([98, 176, 26, 12, 83, 105, 154, 68], 3, "TIMEOUT_RELEASE"),
|
|
260
|
+
refund_buyer: spec([199, 139, 203, 146, 192, 150, 53, 218], 3, "DEADLINE_REFUND"),
|
|
261
|
+
arbitration_release: spec([118, 157, 141, 26, 104, 117, 94, 229], 4, "ARBITRATION_RELEASE"),
|
|
262
|
+
arbitration_refund: spec([255, 194, 84, 248, 185, 249, 120, 122], 4, "ARBITRATION_REFUND")
|
|
263
|
+
};
|
|
233
264
|
|
|
234
265
|
// src/payments.ts
|
|
235
266
|
var RAILS = ["escrow", "x402"];
|
|
@@ -244,9 +275,10 @@ var EscrowPaymentRouter = class {
|
|
|
244
275
|
|
|
245
276
|
// src/errors.ts
|
|
246
277
|
var EscroError = class extends Error {
|
|
247
|
-
constructor(message, code = "ESCRO_ERROR") {
|
|
278
|
+
constructor(message, code = "ESCRO_ERROR", retryAfterMs2) {
|
|
248
279
|
super(message);
|
|
249
280
|
__publicField(this, "code", code);
|
|
281
|
+
__publicField(this, "retryAfterMs", retryAfterMs2);
|
|
250
282
|
this.name = "EscroError";
|
|
251
283
|
}
|
|
252
284
|
};
|
|
@@ -277,6 +309,18 @@ var EscrowTimeoutError = class extends EscroError {
|
|
|
277
309
|
this.name = "EscrowTimeoutError";
|
|
278
310
|
}
|
|
279
311
|
};
|
|
312
|
+
var EscroConfirmError = class extends EscroError {
|
|
313
|
+
constructor(address, signature, code, message, retryAfterMs2) {
|
|
314
|
+
super(
|
|
315
|
+
`Confirm failed for escrow ${address} (signature ${signature}): ${message}`,
|
|
316
|
+
code,
|
|
317
|
+
retryAfterMs2
|
|
318
|
+
);
|
|
319
|
+
__publicField(this, "address", address);
|
|
320
|
+
__publicField(this, "signature", signature);
|
|
321
|
+
this.name = "EscroConfirmError";
|
|
322
|
+
}
|
|
323
|
+
};
|
|
280
324
|
var UnauthorizedError = class extends EscroError {
|
|
281
325
|
constructor(message = "Unauthorized") {
|
|
282
326
|
super(message, "UNAUTHORIZED");
|
|
@@ -307,7 +351,10 @@ async function handleError(res, address) {
|
|
|
307
351
|
if (res.status === 404) throw new EscrowNotFoundError(address ?? msg);
|
|
308
352
|
if (res.status === 409) throw new EscrowStateError(msg);
|
|
309
353
|
if (res.status === 401 || res.status === 403) throw new UnauthorizedError(msg);
|
|
310
|
-
throw new EscroError(msg, body.code ?? "UNKNOWN");
|
|
354
|
+
throw new EscroError(msg, body.code ?? "UNKNOWN", retryAfterMs(res.headers.get("retry-after")));
|
|
355
|
+
}
|
|
356
|
+
function retryAfterMs(header) {
|
|
357
|
+
return header !== null && /^\d+$/.test(header.trim()) ? Number(header.trim()) * 1e3 : void 0;
|
|
311
358
|
}
|
|
312
359
|
var ApiClient = class {
|
|
313
360
|
constructor(baseUrl, wallet) {
|
|
@@ -383,6 +430,22 @@ var ApiClient = class {
|
|
|
383
430
|
const body = await res.json();
|
|
384
431
|
return body.wallets;
|
|
385
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* GET /rep/:wallet : one wallet's EscroRep RAW components (ARCHITECTURE §9).
|
|
435
|
+
* Unauthenticated public read (no auth headers). Unknown wallets return zeroed
|
|
436
|
+
* components, never 404; a 404 means the deployment mounted no reputation
|
|
437
|
+
* source and surfaces as {@link ReputationUnavailableError}. Shares the public
|
|
438
|
+
* per-wallet rate budget with every other caller, so fan-outs should use
|
|
439
|
+
* {@link listReputations} instead.
|
|
440
|
+
*/
|
|
441
|
+
async getReputation(wallet) {
|
|
442
|
+
const res = await fetch(this.url(`/rep/${encodeURIComponent(wallet)}`));
|
|
443
|
+
if (!res.ok) {
|
|
444
|
+
if (res.status === 404) throw new ReputationUnavailableError();
|
|
445
|
+
await handleError(res);
|
|
446
|
+
}
|
|
447
|
+
return await res.json();
|
|
448
|
+
}
|
|
386
449
|
/**
|
|
387
450
|
* POST /v1/escrows — create escrow, returns unsigned tx + ids.
|
|
388
451
|
* Requires auth.
|
|
@@ -442,8 +505,11 @@ var ApiClient = class {
|
|
|
442
505
|
return res.json();
|
|
443
506
|
}
|
|
444
507
|
/**
|
|
445
|
-
* POST /v1/escrows/:address/release
|
|
446
|
-
* Requires auth (buyer). No on-chain tx returned.
|
|
508
|
+
* POST /v1/escrows/:address/release: records the landed release once the API's RPC shows
|
|
509
|
+
* the escrow closed. Requires auth (buyer). No on-chain tx returned. A 409
|
|
510
|
+
* `ESCROW_NOT_SETTLED`, a 5xx or a 429 throws an {@link EscroError} with the server's code,
|
|
511
|
+
* or `HTTP_<status>` when the answer carries none; {@link Escro.releasePaymentWithOutcome}
|
|
512
|
+
* decides which to retry. Other refusals throw as every route does.
|
|
447
513
|
*/
|
|
448
514
|
async releasePaymentDb(address) {
|
|
449
515
|
const path = `/v1/escrows/${address}/release`;
|
|
@@ -451,7 +517,19 @@ var ApiClient = class {
|
|
|
451
517
|
method: "POST",
|
|
452
518
|
headers: authHeaders(this.wallet, "POST", path)
|
|
453
519
|
});
|
|
454
|
-
if (
|
|
520
|
+
if (res.ok) return;
|
|
521
|
+
if (res.status !== 409 && res.status !== 429 && res.status < 500) {
|
|
522
|
+
await handleError(res, address);
|
|
523
|
+
}
|
|
524
|
+
const body = await res.json().catch(() => ({}));
|
|
525
|
+
if (res.status === 409 && body.code !== "ESCROW_NOT_SETTLED") {
|
|
526
|
+
throw new EscrowStateError(body.error ?? `HTTP ${String(res.status)}`);
|
|
527
|
+
}
|
|
528
|
+
throw new EscroError(
|
|
529
|
+
body.error ?? `HTTP ${String(res.status)}`,
|
|
530
|
+
body.code ?? `HTTP_${String(res.status)}`,
|
|
531
|
+
retryAfterMs(res.headers.get("retry-after"))
|
|
532
|
+
);
|
|
455
533
|
}
|
|
456
534
|
/**
|
|
457
535
|
* POST /v1/escrows/:address/cancel — returns unsigned tx.
|
|
@@ -472,7 +550,7 @@ var ApiClient = class {
|
|
|
472
550
|
*/
|
|
473
551
|
async raiseDispute(address, params) {
|
|
474
552
|
const path = `/v1/escrows/${address}/dispute`;
|
|
475
|
-
const rawBody = JSON.stringify({ reason: params.reason
|
|
553
|
+
const rawBody = JSON.stringify({ reason: params.reason });
|
|
476
554
|
const res = await fetch(this.url(path), {
|
|
477
555
|
method: "POST",
|
|
478
556
|
headers: {
|
|
@@ -484,6 +562,35 @@ var ApiClient = class {
|
|
|
484
562
|
if (!res.ok) await handleError(res, address);
|
|
485
563
|
return res.json();
|
|
486
564
|
}
|
|
565
|
+
/**
|
|
566
|
+
* POST /v1/escrows/:address/confirm: the API records the create, claim, submit,
|
|
567
|
+
* cancel or dispute the landed `signature` carries, after verifying it on chain.
|
|
568
|
+
* A non-2xx answer throws an {@link EscroError} with the server's code, or
|
|
569
|
+
* `HTTP_<status>` when the answer carries none; a network failure rejects with
|
|
570
|
+
* fetch's own error. {@link Escro.confirm} decides which to retry.
|
|
571
|
+
*/
|
|
572
|
+
async confirm(address, signature, evidence) {
|
|
573
|
+
const path = `/v1/escrows/${address}/confirm`;
|
|
574
|
+
const rawBody = JSON.stringify({ signature, evidence });
|
|
575
|
+
const res = await fetch(this.url(path), {
|
|
576
|
+
method: "POST",
|
|
577
|
+
headers: {
|
|
578
|
+
...authHeaders(this.wallet, "POST", path, rawBody),
|
|
579
|
+
"content-type": "application/json"
|
|
580
|
+
},
|
|
581
|
+
body: rawBody
|
|
582
|
+
});
|
|
583
|
+
if (!res.ok) {
|
|
584
|
+
const body2 = await res.json().catch(() => ({}));
|
|
585
|
+
throw new EscroError(
|
|
586
|
+
body2.error ?? `HTTP ${String(res.status)}`,
|
|
587
|
+
body2.code ?? `HTTP_${String(res.status)}`,
|
|
588
|
+
retryAfterMs(res.headers.get("retry-after"))
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
const body = await res.json();
|
|
592
|
+
return deserialize(body.escrow);
|
|
593
|
+
}
|
|
487
594
|
/**
|
|
488
595
|
* GET /v1/stats — public protocol-wide stats.
|
|
489
596
|
* No auth required. Upstream typing in {@link Escro.getStats} narrows the
|
|
@@ -529,8 +636,61 @@ async function pollUntil(address, fetcher, predicate, timeoutMs = MS_24_H) {
|
|
|
529
636
|
|
|
530
637
|
// src/solana.ts
|
|
531
638
|
var import_node_crypto = require("crypto");
|
|
532
|
-
var
|
|
639
|
+
var import_web32 = require("@solana/web3.js");
|
|
533
640
|
var import_spl_token = require("@solana/spl-token");
|
|
641
|
+
|
|
642
|
+
// ../shared/dist/solana.js
|
|
643
|
+
var import_web3 = require("@solana/web3.js");
|
|
644
|
+
var import_bs583 = __toESM(require("bs58"), 1);
|
|
645
|
+
var utf8 = (s) => new TextEncoder().encode(s);
|
|
646
|
+
function findPlatformConfigPda(programId) {
|
|
647
|
+
return import_web3.PublicKey.findProgramAddressSync([utf8("config")], programId);
|
|
648
|
+
}
|
|
649
|
+
function derivePlatformConfigPda(programId) {
|
|
650
|
+
return findPlatformConfigPda(programId)[0];
|
|
651
|
+
}
|
|
652
|
+
var PLATFORM_CONFIG_LAYOUT = {
|
|
653
|
+
version: 8,
|
|
654
|
+
fee_bps: 9,
|
|
655
|
+
oracle_authority: 11,
|
|
656
|
+
usdc_mint: 43,
|
|
657
|
+
treasury: 75,
|
|
658
|
+
bump: 107,
|
|
659
|
+
length: 108
|
|
660
|
+
};
|
|
661
|
+
var PLATFORM_CONFIG_DISCRIMINATOR = [
|
|
662
|
+
160,
|
|
663
|
+
78,
|
|
664
|
+
128,
|
|
665
|
+
0,
|
|
666
|
+
248,
|
|
667
|
+
83,
|
|
668
|
+
230,
|
|
669
|
+
160
|
|
670
|
+
];
|
|
671
|
+
function decodePlatformConfig(data) {
|
|
672
|
+
const L = PLATFORM_CONFIG_LAYOUT;
|
|
673
|
+
if (data.length !== L.length) {
|
|
674
|
+
throw new Error(
|
|
675
|
+
`PlatformConfig account is ${String(data.length)} bytes, expected ${String(L.length)}`
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
if (PLATFORM_CONFIG_DISCRIMINATOR.some((byte2, i) => data[i] !== byte2)) {
|
|
679
|
+
throw new Error("PlatformConfig discriminator mismatch: not a PlatformConfig account");
|
|
680
|
+
}
|
|
681
|
+
const byte = (at) => data[at] ?? 0;
|
|
682
|
+
const key = (at) => import_bs583.default.encode(data.subarray(at, at + 32));
|
|
683
|
+
return {
|
|
684
|
+
version: byte(L.version),
|
|
685
|
+
feeBps: byte(L.fee_bps) | byte(L.fee_bps + 1) << 8,
|
|
686
|
+
oracleAuthority: key(L.oracle_authority),
|
|
687
|
+
usdcMint: key(L.usdc_mint),
|
|
688
|
+
treasury: key(L.treasury),
|
|
689
|
+
bump: byte(L.bump)
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/solana.ts
|
|
534
694
|
function disc(name) {
|
|
535
695
|
return Buffer.from((0, import_node_crypto.createHash)("sha256").update(`global:${name}`).digest()).subarray(0, 8);
|
|
536
696
|
}
|
|
@@ -538,14 +698,14 @@ var DISC_RELEASE_PAYMENT = disc("release_payment");
|
|
|
538
698
|
function resolveProgramId(cluster) {
|
|
539
699
|
const id = getProgramId(cluster);
|
|
540
700
|
assertProgramIdNotPlaceholder(id, cluster);
|
|
541
|
-
return new
|
|
701
|
+
return new import_web32.PublicKey(id);
|
|
542
702
|
}
|
|
543
703
|
function allowedProgramIds(programId) {
|
|
544
704
|
return /* @__PURE__ */ new Set([
|
|
545
705
|
programId.toBase58(),
|
|
546
706
|
import_spl_token.TOKEN_PROGRAM_ID.toBase58(),
|
|
547
707
|
import_spl_token.ASSOCIATED_TOKEN_PROGRAM_ID.toBase58(),
|
|
548
|
-
|
|
708
|
+
import_web32.ComputeBudgetProgram.programId.toBase58()
|
|
549
709
|
]);
|
|
550
710
|
}
|
|
551
711
|
var MAX_INSTRUCTIONS = 6;
|
|
@@ -571,25 +731,17 @@ var FORBIDDEN_TOKEN_IX_TAGS = /* @__PURE__ */ new Set([
|
|
|
571
731
|
15
|
|
572
732
|
// BurnChecked
|
|
573
733
|
]);
|
|
574
|
-
var EXPECTED_PLATFORM_CONFIG_VERSION = 1;
|
|
575
|
-
var VERSION_OFFSET = 8;
|
|
576
|
-
var MINT_OFFSET = 43;
|
|
577
|
-
var TREASURY_OFFSET = 75;
|
|
578
734
|
async function fetchPlatformConfig(connection, cluster) {
|
|
579
|
-
const
|
|
580
|
-
const [pda] = import_web3.PublicKey.findProgramAddressSync([Buffer.from("config")], programId);
|
|
735
|
+
const pda = derivePlatformConfigPda(resolveProgramId(cluster));
|
|
581
736
|
const info = await connection.getAccountInfo(pda);
|
|
582
737
|
if (!info) throw new Error("PlatformConfig account not found on-chain");
|
|
583
|
-
const
|
|
584
|
-
if (version !==
|
|
738
|
+
const config = decodePlatformConfig(info.data);
|
|
739
|
+
if (config.version !== CURRENT_ACCOUNT_VERSION) {
|
|
585
740
|
throw new Error(
|
|
586
|
-
`PlatformConfig version mismatch: expected ${String(
|
|
741
|
+
`PlatformConfig version mismatch: expected ${String(CURRENT_ACCOUNT_VERSION)}, got ${String(config.version)}`
|
|
587
742
|
);
|
|
588
743
|
}
|
|
589
|
-
return {
|
|
590
|
-
mint: new import_web3.PublicKey(info.data.subarray(MINT_OFFSET, MINT_OFFSET + 32)),
|
|
591
|
-
treasury: new import_web3.PublicKey(info.data.subarray(TREASURY_OFFSET, TREASURY_OFFSET + 32))
|
|
592
|
-
};
|
|
744
|
+
return { mint: new import_web32.PublicKey(config.usdcMint), treasury: new import_web32.PublicKey(config.treasury) };
|
|
593
745
|
}
|
|
594
746
|
function validateTransaction(tx, cluster) {
|
|
595
747
|
const programId = resolveProgramId(cluster);
|
|
@@ -619,7 +771,7 @@ function validateTransaction(tx, cluster) {
|
|
|
619
771
|
}
|
|
620
772
|
async function signAndSend(base64Tx, wallet, connection, cluster) {
|
|
621
773
|
const buf = Buffer.from(base64Tx, "base64");
|
|
622
|
-
const tx =
|
|
774
|
+
const tx = import_web32.Transaction.from(buf);
|
|
623
775
|
validateTransaction(tx, cluster);
|
|
624
776
|
tx.partialSign(wallet);
|
|
625
777
|
const raw = tx.serialize();
|
|
@@ -633,7 +785,7 @@ async function signAndSend(base64Tx, wallet, connection, cluster) {
|
|
|
633
785
|
async function buildReleasePaymentTx(params) {
|
|
634
786
|
const { buyer, escrowPda, worker, mint, treasury, connection, cluster } = params;
|
|
635
787
|
const programId = resolveProgramId(cluster);
|
|
636
|
-
const
|
|
788
|
+
const platformConfig = derivePlatformConfigPda(programId);
|
|
637
789
|
const vault = (0, import_spl_token.getAssociatedTokenAddressSync)(
|
|
638
790
|
mint,
|
|
639
791
|
escrowPda,
|
|
@@ -655,7 +807,7 @@ async function buildReleasePaymentTx(params) {
|
|
|
655
807
|
import_spl_token.TOKEN_PROGRAM_ID,
|
|
656
808
|
import_spl_token.ASSOCIATED_TOKEN_PROGRAM_ID
|
|
657
809
|
);
|
|
658
|
-
const ix = new
|
|
810
|
+
const ix = new import_web32.TransactionInstruction({
|
|
659
811
|
programId,
|
|
660
812
|
keys: [
|
|
661
813
|
{ pubkey: buyer, isSigner: true, isWritable: true },
|
|
@@ -673,7 +825,7 @@ async function buildReleasePaymentTx(params) {
|
|
|
673
825
|
data: DISC_RELEASE_PAYMENT
|
|
674
826
|
});
|
|
675
827
|
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
676
|
-
const tx = new
|
|
828
|
+
const tx = new import_web32.Transaction();
|
|
677
829
|
tx.recentBlockhash = blockhash;
|
|
678
830
|
tx.feePayer = buyer;
|
|
679
831
|
tx.add((0, import_spl_token.createAssociatedTokenAccountIdempotentInstruction)(buyer, workerAta, worker, mint));
|
|
@@ -683,6 +835,33 @@ async function buildReleasePaymentTx(params) {
|
|
|
683
835
|
}
|
|
684
836
|
|
|
685
837
|
// src/client.ts
|
|
838
|
+
var CONFIRM_WINDOW_MS = 3e4;
|
|
839
|
+
var CONFIRM_FIRST_DELAY_MS = 500;
|
|
840
|
+
var CONFIRM_MAX_DELAY_MS = 4e3;
|
|
841
|
+
var RETRYABLE_CONFIRM_CODES = [
|
|
842
|
+
"TX_NOT_FOUND",
|
|
843
|
+
"RPC_UNAVAILABLE",
|
|
844
|
+
"HTTP_502",
|
|
845
|
+
"HTTP_503",
|
|
846
|
+
"HTTP_504",
|
|
847
|
+
"RATE_LIMITED",
|
|
848
|
+
"HTTP_429"
|
|
849
|
+
];
|
|
850
|
+
function isRetryableConfirmCode(code) {
|
|
851
|
+
return RETRYABLE_CONFIRM_CODES.includes(code);
|
|
852
|
+
}
|
|
853
|
+
function isRetryableReleaseCode(code) {
|
|
854
|
+
return code === "ESCROW_NOT_SETTLED" || isRetryableConfirmCode(code);
|
|
855
|
+
}
|
|
856
|
+
function isUrl(value) {
|
|
857
|
+
try {
|
|
858
|
+
new URL(value);
|
|
859
|
+
return true;
|
|
860
|
+
} catch {
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
686
865
|
var Escro = class {
|
|
687
866
|
constructor(options) {
|
|
688
867
|
__publicField(this, "connection");
|
|
@@ -692,35 +871,46 @@ var Escro = class {
|
|
|
692
871
|
__publicField(this, "router", new EscrowPaymentRouter());
|
|
693
872
|
this.options = options;
|
|
694
873
|
this.cluster = options.cluster;
|
|
695
|
-
this.connection = new
|
|
874
|
+
this.connection = new import_web33.Connection(options.rpcUrl, "confirmed");
|
|
696
875
|
this.api = new ApiClient(options.apiUrl, options.wallet);
|
|
697
876
|
}
|
|
698
877
|
// ── Buyer Bot ────────────────────────────────────────────────────────────────
|
|
699
878
|
/**
|
|
700
879
|
* Create a new escrow: registers with the API, signs and submits the
|
|
701
|
-
* on-chain `create_escrow` transaction.
|
|
880
|
+
* on-chain `create_escrow` transaction, then confirms it with the API.
|
|
702
881
|
*
|
|
703
882
|
* @returns `{ escrowId, escrowPda, signature }` — use `escrowPda` for all subsequent calls.
|
|
704
883
|
*/
|
|
705
884
|
async createEscrow(params) {
|
|
706
885
|
const { escrowId, escrowPda, unsignedTx } = await this.api.createEscrow(params);
|
|
707
|
-
const signature = await
|
|
708
|
-
|
|
709
|
-
this.options.wallet,
|
|
710
|
-
this.connection,
|
|
711
|
-
this.cluster
|
|
712
|
-
);
|
|
886
|
+
const signature = await this.send(unsignedTx);
|
|
887
|
+
await this.confirm(escrowPda, signature);
|
|
713
888
|
return { escrowId, escrowPda, signature };
|
|
714
889
|
}
|
|
715
890
|
/**
|
|
716
891
|
* Release payment to the worker. Buyer must be the wallet in the constructor.
|
|
717
892
|
* Builds and submits the `release_payment` on-chain tx FIRST (the chain is the
|
|
718
|
-
* source of truth), then
|
|
719
|
-
*
|
|
893
|
+
* source of truth), then has the API record it, retrying as
|
|
894
|
+
* {@link Escro.releasePaymentWithOutcome} does. Use that method to see whether the
|
|
895
|
+
* API recorded the release.
|
|
720
896
|
*
|
|
721
897
|
* @returns Solana transaction signature.
|
|
722
898
|
*/
|
|
723
899
|
async releasePayment(address) {
|
|
900
|
+
const { signature } = await this.releasePaymentWithOutcome(address);
|
|
901
|
+
return signature;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* {@link Escro.releasePayment}, reporting whether the API recorded the release.
|
|
905
|
+
* `ESCROW_NOT_SETTLED` (the API's RPC does not yet show the escrow closed), the codes
|
|
906
|
+
* {@link Escro.confirm} retries and a network failure are retried for about 30 s. A
|
|
907
|
+
* failure never throws: the release already landed, so the signature is returned with
|
|
908
|
+
* `confirm.landed: false` and an `EscroConfirmError` (the API's code, or
|
|
909
|
+
* `NETWORK_ERROR` when the last call never reached it).
|
|
910
|
+
*
|
|
911
|
+
* @returns `{ signature, confirm }`.
|
|
912
|
+
*/
|
|
913
|
+
async releasePaymentWithOutcome(address) {
|
|
724
914
|
const escrow = await this.api.getEscrow(address);
|
|
725
915
|
if (!escrow.taker) {
|
|
726
916
|
throw new EscrowNotFoundError(`Escrow ${address} has no assigned worker`);
|
|
@@ -728,8 +918,8 @@ var Escro = class {
|
|
|
728
918
|
const { treasury, mint } = await fetchPlatformConfig(this.connection, this.cluster);
|
|
729
919
|
const unsignedTx = await buildReleasePaymentTx({
|
|
730
920
|
buyer: this.options.wallet.publicKey,
|
|
731
|
-
escrowPda: new
|
|
732
|
-
worker: new
|
|
921
|
+
escrowPda: new import_web33.PublicKey(address),
|
|
922
|
+
worker: new import_web33.PublicKey(escrow.taker),
|
|
733
923
|
mint,
|
|
734
924
|
treasury,
|
|
735
925
|
connection: this.connection,
|
|
@@ -742,31 +932,47 @@ var Escro = class {
|
|
|
742
932
|
this.cluster
|
|
743
933
|
);
|
|
744
934
|
try {
|
|
745
|
-
await this.
|
|
746
|
-
|
|
935
|
+
await this.retryRecord(
|
|
936
|
+
address,
|
|
937
|
+
signature,
|
|
938
|
+
() => this.api.releasePaymentDb(address),
|
|
939
|
+
isRetryableReleaseCode
|
|
940
|
+
);
|
|
941
|
+
} catch (err) {
|
|
942
|
+
if (!(err instanceof EscroConfirmError)) throw err;
|
|
943
|
+
return { signature, confirm: { landed: false, error: err } };
|
|
747
944
|
}
|
|
748
|
-
return signature;
|
|
945
|
+
return { signature, confirm: { landed: true } };
|
|
749
946
|
}
|
|
750
947
|
/**
|
|
751
948
|
* Raise a dispute for an escrow. Either the buyer or the assigned worker
|
|
752
|
-
* may call this. Signs and submits the `raise_dispute` on-chain tx.
|
|
949
|
+
* may call this. Signs and submits the `raise_dispute` on-chain tx, then confirms it with the API.
|
|
753
950
|
*
|
|
754
951
|
* @returns Solana transaction signature.
|
|
755
952
|
*/
|
|
756
953
|
async raiseDispute(address, params) {
|
|
757
|
-
const
|
|
758
|
-
|
|
954
|
+
const reasonError = disputeReasonError(params.reason);
|
|
955
|
+
if (reasonError !== null) throw new EscroError(reasonError, "VALIDATION_ERROR");
|
|
956
|
+
if (params.evidence !== void 0 && !isUrl(params.evidence)) {
|
|
957
|
+
throw new EscroError(`evidence must be a URL: ${params.evidence}`, "VALIDATION_ERROR");
|
|
958
|
+
}
|
|
959
|
+
const { unsignedTx } = await this.api.raiseDispute(address, { reason: params.reason });
|
|
960
|
+
const signature = await this.send(unsignedTx);
|
|
961
|
+
await this.confirm(address, signature, {
|
|
962
|
+
...params.evidence !== void 0 && { evidence: params.evidence }
|
|
963
|
+
});
|
|
964
|
+
return signature;
|
|
759
965
|
}
|
|
760
966
|
/**
|
|
761
967
|
* Cancel a funded escrow before any worker has claimed it.
|
|
762
968
|
* Buyer must be the wallet in the constructor. Only valid when state is `FUNDED`.
|
|
763
|
-
* Signs and submits the `cancel_escrow` on-chain tx; full USDC refund to buyer.
|
|
969
|
+
* Signs and submits the `cancel_escrow` on-chain tx, then confirms it with the API; full USDC refund to buyer.
|
|
764
970
|
*
|
|
765
971
|
* @returns Solana transaction signature.
|
|
766
972
|
*/
|
|
767
973
|
async cancelEscrow(address) {
|
|
768
974
|
const { unsignedTx } = await this.api.cancelEscrow(address);
|
|
769
|
-
return
|
|
975
|
+
return this.sendAndConfirm(address, unsignedTx);
|
|
770
976
|
}
|
|
771
977
|
// ── Payments (dual-path; x402 reserved) ───────────────────────────────────────
|
|
772
978
|
/**
|
|
@@ -791,23 +997,23 @@ var Escro = class {
|
|
|
791
997
|
// ── Worker Bot ───────────────────────────────────────────────────────────────
|
|
792
998
|
/**
|
|
793
999
|
* Claim an assigned task. Worker must be the wallet in the constructor.
|
|
794
|
-
* Signs and submits the `claim_task` on-chain tx.
|
|
1000
|
+
* Signs and submits the `claim_task` on-chain tx, then confirms it with the API.
|
|
795
1001
|
*
|
|
796
1002
|
* @returns Solana transaction signature.
|
|
797
1003
|
*/
|
|
798
1004
|
async claimTask(address) {
|
|
799
1005
|
const { unsignedTx } = await this.api.claimTask(address);
|
|
800
|
-
return
|
|
1006
|
+
return this.sendAndConfirm(address, unsignedTx);
|
|
801
1007
|
}
|
|
802
1008
|
/**
|
|
803
1009
|
* Submit a deliverable for the buyer to review. Worker must be the wallet.
|
|
804
|
-
* Signs and submits the `submit_deliverable` on-chain tx.
|
|
1010
|
+
* Signs and submits the `submit_deliverable` on-chain tx, then confirms it with the API.
|
|
805
1011
|
*
|
|
806
1012
|
* @returns Solana transaction signature.
|
|
807
1013
|
*/
|
|
808
1014
|
async submitDeliverable(address, params) {
|
|
809
1015
|
const { unsignedTx } = await this.api.submitDeliverable(address, params);
|
|
810
|
-
return
|
|
1016
|
+
return this.sendAndConfirm(address, unsignedTx);
|
|
811
1017
|
}
|
|
812
1018
|
/**
|
|
813
1019
|
* List tasks assigned to the wallet's public key.
|
|
@@ -917,6 +1123,70 @@ var Escro = class {
|
|
|
917
1123
|
return data;
|
|
918
1124
|
}
|
|
919
1125
|
// ── Shared ───────────────────────────────────────────────────────────────────
|
|
1126
|
+
/**
|
|
1127
|
+
* Ask the API to record the create, claim, submit, cancel or dispute that the
|
|
1128
|
+
* landed `signature` carries; the API verifies it on chain first. Retries a
|
|
1129
|
+
* not-yet-visible transaction, an RPC failure, a gateway error or a rate limit
|
|
1130
|
+
* for about 30 s (waiting at least a sent Retry-After), never a rejection. Throws
|
|
1131
|
+
* {@link EscroConfirmError}; calling again is safe (a replay of a recorded
|
|
1132
|
+
* transition succeeds).
|
|
1133
|
+
*/
|
|
1134
|
+
async confirm(address, signature, opts = {}) {
|
|
1135
|
+
return this.retryRecord(
|
|
1136
|
+
address,
|
|
1137
|
+
signature,
|
|
1138
|
+
() => this.api.confirm(address, signature, opts.evidence),
|
|
1139
|
+
isRetryableConfirmCode
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
// Calls `record` until the API records the landed `signature`: a code `retryable` accepts or
|
|
1143
|
+
// a network failure is retried for about 30 s (waiting at least a sent Retry-After), any
|
|
1144
|
+
// other refusal is not. Throws EscroConfirmError.
|
|
1145
|
+
async retryRecord(address, signature, record, retryable) {
|
|
1146
|
+
const deadline = Date.now() + CONFIRM_WINDOW_MS;
|
|
1147
|
+
let delay = CONFIRM_FIRST_DELAY_MS;
|
|
1148
|
+
for (; ; ) {
|
|
1149
|
+
let code;
|
|
1150
|
+
let message;
|
|
1151
|
+
let retryAfterMs2;
|
|
1152
|
+
let wait = delay;
|
|
1153
|
+
try {
|
|
1154
|
+
return await record();
|
|
1155
|
+
} catch (err) {
|
|
1156
|
+
if (err instanceof EscroError) {
|
|
1157
|
+
if (!retryable(err.code)) {
|
|
1158
|
+
throw new EscroConfirmError(
|
|
1159
|
+
address,
|
|
1160
|
+
signature,
|
|
1161
|
+
err.code,
|
|
1162
|
+
err.message,
|
|
1163
|
+
err.retryAfterMs
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
code = err.code;
|
|
1167
|
+
message = err.message;
|
|
1168
|
+
retryAfterMs2 = err.retryAfterMs;
|
|
1169
|
+
wait = Math.max(delay, err.retryAfterMs ?? 0);
|
|
1170
|
+
} else {
|
|
1171
|
+
code = "NETWORK_ERROR";
|
|
1172
|
+
message = err instanceof Error ? err.message : String(err);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
if (Date.now() + wait > deadline) {
|
|
1176
|
+
throw new EscroConfirmError(address, signature, code, message, retryAfterMs2);
|
|
1177
|
+
}
|
|
1178
|
+
await sleep2(wait);
|
|
1179
|
+
delay = Math.min(delay * 2, CONFIRM_MAX_DELAY_MS);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
send(unsignedTx) {
|
|
1183
|
+
return signAndSend(unsignedTx, this.options.wallet, this.connection, this.cluster);
|
|
1184
|
+
}
|
|
1185
|
+
async sendAndConfirm(address, unsignedTx) {
|
|
1186
|
+
const signature = await this.send(unsignedTx);
|
|
1187
|
+
await this.confirm(address, signature);
|
|
1188
|
+
return signature;
|
|
1189
|
+
}
|
|
920
1190
|
/**
|
|
921
1191
|
* Fetch an escrow by its PDA address.
|
|
922
1192
|
* If `options.poll` is true, polls with exponential backoff until the escrow
|
|
@@ -1050,9 +1320,9 @@ function createBittensorSubnetAdapter(config) {
|
|
|
1050
1320
|
id,
|
|
1051
1321
|
capabilities: config.capabilities,
|
|
1052
1322
|
healthCheck: config.checkHealth,
|
|
1053
|
-
async execute(
|
|
1323
|
+
async execute(spec2) {
|
|
1054
1324
|
const budgetTao = await config.swap(config.taskBudgetMicroUsdc);
|
|
1055
|
-
const content = await config.callSubnet(
|
|
1325
|
+
const content = await config.callSubnet(spec2, budgetTao);
|
|
1056
1326
|
const contentHash = sha256Hex2(content);
|
|
1057
1327
|
const proofUri = await config.pin(content);
|
|
1058
1328
|
return {
|
|
@@ -1135,16 +1405,16 @@ function scoreGradientsDemoDeliverable(content) {
|
|
|
1135
1405
|
}
|
|
1136
1406
|
return matches / expected.length;
|
|
1137
1407
|
}
|
|
1138
|
-
function referencePlaceholder(
|
|
1408
|
+
function referencePlaceholder(spec2) {
|
|
1139
1409
|
return canonicalize({
|
|
1140
1410
|
checkpoint: "reference-sn56-checkpoint",
|
|
1141
1411
|
eval: { metric: "accuracy", score: 0 },
|
|
1142
1412
|
note: "reference stub - replace the callSubnet port with a real SN56 miner call.",
|
|
1143
|
-
taskType:
|
|
1413
|
+
taskType: spec2.taskType
|
|
1144
1414
|
});
|
|
1145
1415
|
}
|
|
1146
|
-
var defaultCallSubnet = (
|
|
1147
|
-
|
|
1416
|
+
var defaultCallSubnet = (spec2) => Promise.resolve(
|
|
1417
|
+
spec2.metadata.demo === GRADIENTS_DEMO_MARKER ? solveGradientsDemo() : referencePlaceholder(spec2)
|
|
1148
1418
|
);
|
|
1149
1419
|
function createGradientsAdapter(config = {}) {
|
|
1150
1420
|
return createBittensorSubnetAdapter({
|
|
@@ -1256,17 +1526,17 @@ function scoreRidgesDemoDeliverable(content) {
|
|
|
1256
1526
|
}
|
|
1257
1527
|
return coveredInputs.size / DEMO_INPUTS.length;
|
|
1258
1528
|
}
|
|
1259
|
-
function referencePlaceholder2(
|
|
1529
|
+
function referencePlaceholder2(spec2) {
|
|
1260
1530
|
return canonicalize({
|
|
1261
1531
|
language: "typescript",
|
|
1262
1532
|
solution: "// reference stub - replace the runAgent port with a real Ridges agent call.",
|
|
1263
1533
|
passed: false,
|
|
1264
1534
|
tests: [],
|
|
1265
|
-
taskType:
|
|
1535
|
+
taskType: spec2.taskType
|
|
1266
1536
|
});
|
|
1267
1537
|
}
|
|
1268
|
-
var defaultRunAgent = (
|
|
1269
|
-
|
|
1538
|
+
var defaultRunAgent = (spec2) => Promise.resolve(
|
|
1539
|
+
spec2.metadata.demo === RIDGES_DEMO_MARKER ? solveRidgesDemo() : referencePlaceholder2(spec2)
|
|
1270
1540
|
);
|
|
1271
1541
|
var defaultPin2 = (content) => Promise.resolve(`ipfs://placeholder-${sha256Hex3(content).slice(0, 16)}`);
|
|
1272
1542
|
function createRidgesAdapter(config = {}) {
|
|
@@ -1279,8 +1549,8 @@ function createRidgesAdapter(config = {}) {
|
|
|
1279
1549
|
id: service,
|
|
1280
1550
|
capabilities,
|
|
1281
1551
|
healthCheck: checkHealth,
|
|
1282
|
-
async execute(
|
|
1283
|
-
const content = await runAgent(
|
|
1552
|
+
async execute(spec2) {
|
|
1553
|
+
const content = await runAgent(spec2);
|
|
1284
1554
|
const contentHash = sha256Hex3(content);
|
|
1285
1555
|
const proofUri = await pin(content);
|
|
1286
1556
|
return {
|
|
@@ -1389,15 +1659,15 @@ function score404genDemoDeliverable(content) {
|
|
|
1389
1659
|
}
|
|
1390
1660
|
return Math.min(batch.assets.length / DEMO_EXPECTATION.requestedAssets, 1);
|
|
1391
1661
|
}
|
|
1392
|
-
function referencePlaceholder3(
|
|
1662
|
+
function referencePlaceholder3(spec2) {
|
|
1393
1663
|
return canonicalize({
|
|
1394
1664
|
assets: [{ format: "GLB", polygonCount: 0, uri: "ipfs://placeholder" }],
|
|
1395
1665
|
note: "reference stub - replace the callSubnet port with a real SN17 miner call.",
|
|
1396
|
-
taskType:
|
|
1666
|
+
taskType: spec2.taskType
|
|
1397
1667
|
});
|
|
1398
1668
|
}
|
|
1399
|
-
var defaultCallSubnet2 = (
|
|
1400
|
-
|
|
1669
|
+
var defaultCallSubnet2 = (spec2) => Promise.resolve(
|
|
1670
|
+
spec2.metadata.demo === GEN404_DEMO_MARKER ? solveGen404Demo() : referencePlaceholder3(spec2)
|
|
1401
1671
|
);
|
|
1402
1672
|
function create404genAdapter(config = {}) {
|
|
1403
1673
|
return createBittensorSubnetAdapter({
|
|
@@ -1497,17 +1767,17 @@ function scoreLiumDemoDeliverable(content) {
|
|
|
1497
1767
|
}
|
|
1498
1768
|
return parsed.artifact === computeLiumDemoArtifact() ? 1 : 0;
|
|
1499
1769
|
}
|
|
1500
|
-
function referencePlaceholder4(
|
|
1770
|
+
function referencePlaceholder4(spec2) {
|
|
1501
1771
|
return canonicalize({
|
|
1502
1772
|
job: "lium-sn51-generic",
|
|
1503
1773
|
status: "completed",
|
|
1504
1774
|
artifact: null,
|
|
1505
1775
|
note: "reference stub - replace the runJob port with a real SN51 GPU-rental call.",
|
|
1506
|
-
taskType:
|
|
1776
|
+
taskType: spec2.taskType
|
|
1507
1777
|
});
|
|
1508
1778
|
}
|
|
1509
|
-
var defaultRunJob = (
|
|
1510
|
-
|
|
1779
|
+
var defaultRunJob = (spec2) => Promise.resolve(
|
|
1780
|
+
spec2.metadata.demo === LIUM_DEMO_MARKER ? solveLiumDemo() : referencePlaceholder4(spec2)
|
|
1511
1781
|
);
|
|
1512
1782
|
function createLiumAdapter(config = {}) {
|
|
1513
1783
|
return createBittensorSubnetAdapter({
|
|
@@ -1616,17 +1886,17 @@ function scoreAkashDemoDeliverable(content) {
|
|
|
1616
1886
|
}
|
|
1617
1887
|
return parsed.artifact === computeAkashDemoArtifact() ? 1 : 0;
|
|
1618
1888
|
}
|
|
1619
|
-
function referencePlaceholder5(
|
|
1889
|
+
function referencePlaceholder5(spec2) {
|
|
1620
1890
|
return canonicalize({
|
|
1621
1891
|
job: "akash-generic",
|
|
1622
1892
|
status: "completed",
|
|
1623
1893
|
artifact: null,
|
|
1624
1894
|
note: "reference stub - replace the runDeployment port with a real Akash deployment call.",
|
|
1625
|
-
taskType:
|
|
1895
|
+
taskType: spec2.taskType
|
|
1626
1896
|
});
|
|
1627
1897
|
}
|
|
1628
|
-
var defaultRunDeployment = (
|
|
1629
|
-
|
|
1898
|
+
var defaultRunDeployment = (spec2) => Promise.resolve(
|
|
1899
|
+
spec2.metadata.demo === AKASH_DEMO_MARKER ? solveAkashDemo() : referencePlaceholder5(spec2)
|
|
1630
1900
|
);
|
|
1631
1901
|
function createAkashAdapter(config = {}) {
|
|
1632
1902
|
const capabilities = config.capabilities ?? [...AKASH_CAPABILITIES];
|
|
@@ -1639,9 +1909,9 @@ function createAkashAdapter(config = {}) {
|
|
|
1639
1909
|
id: AKASH_ADAPTER_ID,
|
|
1640
1910
|
capabilities,
|
|
1641
1911
|
healthCheck: checkHealth,
|
|
1642
|
-
async execute(
|
|
1912
|
+
async execute(spec2) {
|
|
1643
1913
|
const budgetAkt = await swap(taskBudgetMicroUsdc);
|
|
1644
|
-
const content = await runDeployment(
|
|
1914
|
+
const content = await runDeployment(spec2, budgetAkt);
|
|
1645
1915
|
const contentHash = sha256Hex4(content);
|
|
1646
1916
|
const proofUri = await pin(content);
|
|
1647
1917
|
return {
|
|
@@ -1758,17 +2028,17 @@ function scoreRenderDemoDeliverable(content) {
|
|
|
1758
2028
|
}
|
|
1759
2029
|
return parsed.frame === computeRenderDemoArtifact() ? 1 : 0;
|
|
1760
2030
|
}
|
|
1761
|
-
function referencePlaceholder6(
|
|
2031
|
+
function referencePlaceholder6(spec2) {
|
|
1762
2032
|
return canonicalize({
|
|
1763
2033
|
job: "render-generic",
|
|
1764
2034
|
status: "completed",
|
|
1765
2035
|
frame: null,
|
|
1766
2036
|
note: "reference stub - replace the runJob port with a real Render Network job submission.",
|
|
1767
|
-
taskType:
|
|
2037
|
+
taskType: spec2.taskType
|
|
1768
2038
|
});
|
|
1769
2039
|
}
|
|
1770
|
-
var defaultRunJob2 = (
|
|
1771
|
-
|
|
2040
|
+
var defaultRunJob2 = (spec2) => Promise.resolve(
|
|
2041
|
+
spec2.metadata.demo === RENDER_DEMO_MARKER ? solveRenderDemo() : referencePlaceholder6(spec2)
|
|
1772
2042
|
);
|
|
1773
2043
|
function createRenderAdapter(config = {}) {
|
|
1774
2044
|
const service = config.service ?? DEFAULT_SERVICE2;
|
|
@@ -1782,9 +2052,9 @@ function createRenderAdapter(config = {}) {
|
|
|
1782
2052
|
id: service,
|
|
1783
2053
|
capabilities,
|
|
1784
2054
|
healthCheck: checkHealth,
|
|
1785
|
-
async execute(
|
|
2055
|
+
async execute(spec2) {
|
|
1786
2056
|
const budgetRender = await swap(taskBudgetMicroUsdc);
|
|
1787
|
-
const content = await runJob(
|
|
2057
|
+
const content = await runJob(spec2, budgetRender);
|
|
1788
2058
|
const contentHash = sha256Hex5(content);
|
|
1789
2059
|
const proofUri = await pin(content);
|
|
1790
2060
|
return {
|
|
@@ -1811,15 +2081,15 @@ function sha256Hex6(content) {
|
|
|
1811
2081
|
var LCG_MULT = 1103515245n;
|
|
1812
2082
|
var LCG_INC = 12345n;
|
|
1813
2083
|
var LCG_MOD = 1n << 31n;
|
|
1814
|
-
function readComputeParams(
|
|
1815
|
-
const rawSeed =
|
|
1816
|
-
const rawSteps =
|
|
2084
|
+
function readComputeParams(spec2) {
|
|
2085
|
+
const rawSeed = spec2.metadata.seed;
|
|
2086
|
+
const rawSteps = spec2.metadata.steps;
|
|
1817
2087
|
const seed = typeof rawSeed === "number" && Number.isInteger(rawSeed) ? rawSeed : GENSYN_DEMO_SEED;
|
|
1818
2088
|
const steps = typeof rawSteps === "number" && Number.isInteger(rawSteps) && rawSteps > 0 ? rawSteps : GENSYN_DEMO_STEPS;
|
|
1819
2089
|
return { seed, steps };
|
|
1820
2090
|
}
|
|
1821
|
-
var reproduceGensynCompute = (
|
|
1822
|
-
const { seed, steps } = readComputeParams(
|
|
2091
|
+
var reproduceGensynCompute = (spec2) => {
|
|
2092
|
+
const { seed, steps } = readComputeParams(spec2);
|
|
1823
2093
|
const states = [];
|
|
1824
2094
|
let x = BigInt(seed) % LCG_MOD;
|
|
1825
2095
|
if (x < 0n) {
|
|
@@ -1841,10 +2111,10 @@ function commitmentOf(base) {
|
|
|
1841
2111
|
)
|
|
1842
2112
|
);
|
|
1843
2113
|
}
|
|
1844
|
-
function buildReeProof(
|
|
2114
|
+
function buildReeProof(spec2, exec, content) {
|
|
1845
2115
|
const base = {
|
|
1846
2116
|
scheme: GENSYN_REE_SCHEME,
|
|
1847
|
-
specHash: sha256Hex6(canonicalize(
|
|
2117
|
+
specHash: sha256Hex6(canonicalize(spec2)),
|
|
1848
2118
|
outputHash: sha256Hex6(content),
|
|
1849
2119
|
steps: exec.steps,
|
|
1850
2120
|
transcriptHash: sha256Hex6(canonicalize(exec.states))
|
|
@@ -1951,11 +2221,11 @@ function createGensynAdapter(config = {}) {
|
|
|
1951
2221
|
id: GENSYN_ADAPTER_ID,
|
|
1952
2222
|
capabilities,
|
|
1953
2223
|
healthCheck: checkHealth,
|
|
1954
|
-
async execute(
|
|
1955
|
-
const exec = runCompute(
|
|
2224
|
+
async execute(spec2) {
|
|
2225
|
+
const exec = runCompute(spec2);
|
|
1956
2226
|
const content = gensynDeliverable(exec);
|
|
1957
2227
|
const contentHash = sha256Hex6(content);
|
|
1958
|
-
const proof = buildReeProof(
|
|
2228
|
+
const proof = buildReeProof(spec2, exec, content);
|
|
1959
2229
|
const proofUri = await pin(content);
|
|
1960
2230
|
return {
|
|
1961
2231
|
content,
|
|
@@ -2130,21 +2400,21 @@ function solveZkmlDemo() {
|
|
|
2130
2400
|
output: { label: "cat", confidence: 0.98 }
|
|
2131
2401
|
});
|
|
2132
2402
|
}
|
|
2133
|
-
function referencePlaceholderOutput(
|
|
2403
|
+
function referencePlaceholderOutput(spec2) {
|
|
2134
2404
|
return {
|
|
2135
2405
|
content: canonicalize({
|
|
2136
2406
|
note: "reference stub - replace the runInference port with a real zk-verified inference call.",
|
|
2137
|
-
taskType:
|
|
2407
|
+
taskType: spec2.taskType
|
|
2138
2408
|
}),
|
|
2139
2409
|
modelCommitment: sha256Hex7("zkml-reference-stub-model"),
|
|
2140
|
-
inputCommitment: sha256Hex7(canonicalize(
|
|
2410
|
+
inputCommitment: sha256Hex7(canonicalize(spec2))
|
|
2141
2411
|
};
|
|
2142
2412
|
}
|
|
2143
|
-
var defaultRunInference = (
|
|
2413
|
+
var defaultRunInference = (spec2) => spec2.metadata.demo === ZKML_DEMO_MARKER ? {
|
|
2144
2414
|
content: solveZkmlDemo(),
|
|
2145
2415
|
modelCommitment: DEMO_MODEL_COMMITMENT,
|
|
2146
2416
|
inputCommitment: DEMO_INPUT_COMMITMENT
|
|
2147
|
-
} : referencePlaceholderOutput(
|
|
2417
|
+
} : referencePlaceholderOutput(spec2);
|
|
2148
2418
|
function createZkmlAdapter(config = {}) {
|
|
2149
2419
|
const id = config.id ?? ZKML_ADAPTER_ID;
|
|
2150
2420
|
const capabilities = config.capabilities ?? [...ZKML_CAPABILITIES];
|
|
@@ -2159,10 +2429,10 @@ function createZkmlAdapter(config = {}) {
|
|
|
2159
2429
|
return {
|
|
2160
2430
|
id,
|
|
2161
2431
|
capabilities,
|
|
2162
|
-
async execute(
|
|
2163
|
-
const output = await runInference(
|
|
2432
|
+
async execute(spec2) {
|
|
2433
|
+
const output = await runInference(spec2);
|
|
2164
2434
|
const contentHash = sha256Hex7(output.content);
|
|
2165
|
-
const proof = await prove({ spec, output, outputHash: contentHash });
|
|
2435
|
+
const proof = await prove({ spec: spec2, output, outputHash: contentHash });
|
|
2166
2436
|
const proofUri = await pin(proof);
|
|
2167
2437
|
return {
|
|
2168
2438
|
content: output.content,
|
|
@@ -2190,6 +2460,7 @@ function createZkmlAdapter(config = {}) {
|
|
|
2190
2460
|
ApiClient,
|
|
2191
2461
|
ENABLED_RAILS,
|
|
2192
2462
|
Escro,
|
|
2463
|
+
EscroConfirmError,
|
|
2193
2464
|
EscroError,
|
|
2194
2465
|
EscroRailNotEnabledError,
|
|
2195
2466
|
EscrowNotFoundError,
|