@unicitylabs/sphere-sdk 0.14.4 → 0.14.6-dev.1
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/dist/connect/index.cjs +1 -1
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +1 -1
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +101 -79
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +101 -79
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +1 -1
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.js +1 -1
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
- package/dist/impl/wallet-api-v2/index.js.map +1 -1
- package/dist/index.cjs +101 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +101 -79
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +20 -14
- package/dist/modules/payments-v2/index.cjs.map +1 -1
- package/dist/modules/payments-v2/index.js +20 -14
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/dist/token-engine/index.cjs +94 -78
- package/dist/token-engine/index.cjs.map +1 -1
- package/dist/token-engine/index.js +94 -78
- package/dist/token-engine/index.js.map +1 -1
- package/package.json +2 -2
package/dist/core/index.js
CHANGED
|
@@ -9380,41 +9380,35 @@ function resolveProofTimeoutMs(configured) {
|
|
|
9380
9380
|
}
|
|
9381
9381
|
return configured;
|
|
9382
9382
|
}
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
if (prop === "addEventListener") {
|
|
9387
|
-
return (type, listener, options) => {
|
|
9388
|
-
if (type === "abort" && target.aborted) {
|
|
9389
|
-
queueMicrotask(() => {
|
|
9390
|
-
listener.call(target, new Event("abort"));
|
|
9391
|
-
});
|
|
9392
|
-
return;
|
|
9393
|
-
}
|
|
9394
|
-
target.addEventListener(type, listener, options);
|
|
9395
|
-
};
|
|
9396
|
-
}
|
|
9397
|
-
const value = Reflect.get(target, prop, target);
|
|
9398
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
9399
|
-
}
|
|
9400
|
-
});
|
|
9383
|
+
var TRANSIENT_HTTP_STATUSES = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
9384
|
+
function isTransientGatewayError(err) {
|
|
9385
|
+
return err instanceof JsonRpcNetworkError && TRANSIENT_HTTP_STATUSES.has(err.status);
|
|
9401
9386
|
}
|
|
9402
|
-
function
|
|
9403
|
-
return new Promise((
|
|
9404
|
-
|
|
9405
|
-
reject(signal.reason);
|
|
9406
|
-
return;
|
|
9407
|
-
}
|
|
9387
|
+
function pause(ms, signal) {
|
|
9388
|
+
return new Promise((resolve) => {
|
|
9389
|
+
const timer = setTimeout(resolve, ms);
|
|
9408
9390
|
signal.addEventListener(
|
|
9409
9391
|
"abort",
|
|
9410
9392
|
() => {
|
|
9411
|
-
|
|
9393
|
+
clearTimeout(timer);
|
|
9394
|
+
resolve();
|
|
9412
9395
|
},
|
|
9413
9396
|
{ once: true }
|
|
9414
9397
|
);
|
|
9415
9398
|
});
|
|
9416
9399
|
}
|
|
9417
|
-
async function
|
|
9400
|
+
async function retryTransient(attempt, signal, intervalMs) {
|
|
9401
|
+
for (; ; ) {
|
|
9402
|
+
try {
|
|
9403
|
+
return await attempt();
|
|
9404
|
+
} catch (err) {
|
|
9405
|
+
if (signal.aborted) throw err;
|
|
9406
|
+
if (!isTransientGatewayError(err)) throw err;
|
|
9407
|
+
await pause(intervalMs, signal);
|
|
9408
|
+
}
|
|
9409
|
+
}
|
|
9410
|
+
}
|
|
9411
|
+
function certificationDeadline(timeoutMs, external, label) {
|
|
9418
9412
|
const controller = new AbortController();
|
|
9419
9413
|
const relayAbort = () => {
|
|
9420
9414
|
controller.abort(external?.reason);
|
|
@@ -9424,28 +9418,29 @@ async function awaitProofBounded(deps, transaction, external) {
|
|
|
9424
9418
|
else external.addEventListener("abort", relayAbort, { once: true });
|
|
9425
9419
|
}
|
|
9426
9420
|
const timer = setTimeout(() => {
|
|
9427
|
-
controller.abort(
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9421
|
+
controller.abort(new SphereError(`${label} within ${String(timeoutMs)} ms`, "AGGREGATOR_ERROR"));
|
|
9422
|
+
}, timeoutMs);
|
|
9423
|
+
return {
|
|
9424
|
+
signal: controller.signal,
|
|
9425
|
+
dispose: () => {
|
|
9426
|
+
clearTimeout(timer);
|
|
9427
|
+
external?.removeEventListener("abort", relayAbort);
|
|
9428
|
+
}
|
|
9429
|
+
};
|
|
9430
|
+
}
|
|
9431
|
+
async function awaitProofBounded(deps, transaction, signal) {
|
|
9432
|
+
return retryTransient(
|
|
9433
|
+
() => waitInclusionProof(
|
|
9434
|
+
deps.client,
|
|
9435
|
+
deps.trustBase,
|
|
9436
|
+
deps.predicateVerifier,
|
|
9437
|
+
transaction,
|
|
9438
|
+
signal,
|
|
9439
|
+
deps.intervalMs
|
|
9440
|
+
),
|
|
9441
|
+
signal,
|
|
9440
9442
|
deps.intervalMs
|
|
9441
9443
|
);
|
|
9442
|
-
wait.catch(() => void 0);
|
|
9443
|
-
try {
|
|
9444
|
-
return await Promise.race([wait, rejectOnAbort(controller.signal)]);
|
|
9445
|
-
} finally {
|
|
9446
|
-
clearTimeout(timer);
|
|
9447
|
-
external?.removeEventListener("abort", relayAbort);
|
|
9448
|
-
}
|
|
9449
9444
|
}
|
|
9450
9445
|
|
|
9451
9446
|
// token-engine/errors.ts
|
|
@@ -9487,6 +9482,21 @@ var CheckpointTrustbaseMismatchError = class extends SphereError {
|
|
|
9487
9482
|
}
|
|
9488
9483
|
};
|
|
9489
9484
|
|
|
9485
|
+
// token-engine/certification-outcome.ts
|
|
9486
|
+
function certificationFailure(err, failLabel, submitError, submitRejectedCleanly) {
|
|
9487
|
+
if (err instanceof Error && err.message === `Invalid inclusion proof status: ${InclusionProofVerificationStatus.TRANSACTION_HASH_MISMATCH}`) {
|
|
9488
|
+
return new TransferConflictError(
|
|
9489
|
+
`${failLabel}: the source state was already consumed by a different transaction (lost race \u2014 abort this intent and re-plan under a new transferId)`,
|
|
9490
|
+
err
|
|
9491
|
+
);
|
|
9492
|
+
}
|
|
9493
|
+
if (submitRejectedCleanly) return submitError;
|
|
9494
|
+
return new ProofUnconfirmedError(
|
|
9495
|
+
`${failLabel}: certification unconfirmed \u2014 the source spend may be on-chain; keep the intent open and resume under the same transferId`,
|
|
9496
|
+
submitError ?? err
|
|
9497
|
+
);
|
|
9498
|
+
}
|
|
9499
|
+
|
|
9490
9500
|
// token-engine/token-blob.ts
|
|
9491
9501
|
var TOKEN_BLOB_TAG = 39051n;
|
|
9492
9502
|
var TOKEN_BLOB_VERSION = 1;
|
|
@@ -10009,10 +10019,28 @@ var SphereTokenEngine = class {
|
|
|
10009
10019
|
* failure, not a resume case).
|
|
10010
10020
|
*/
|
|
10011
10021
|
async submitAndAwaitProof(certificationData, transaction, failLabel, failCode, options) {
|
|
10022
|
+
const deadline = certificationDeadline(
|
|
10023
|
+
this.proofTimeoutMs,
|
|
10024
|
+
options?.signal,
|
|
10025
|
+
`${failLabel}: not certified`
|
|
10026
|
+
);
|
|
10027
|
+
try {
|
|
10028
|
+
return await this.submitThenProve(certificationData, transaction, failLabel, failCode, deadline.signal);
|
|
10029
|
+
} finally {
|
|
10030
|
+
deadline.dispose();
|
|
10031
|
+
}
|
|
10032
|
+
}
|
|
10033
|
+
/** One certification attempt under one deadline: the metered submit retries
|
|
10034
|
+
* transient answers instead of falling through to a proof that cannot exist. */
|
|
10035
|
+
async submitThenProve(certificationData, transaction, failLabel, failCode, signal) {
|
|
10012
10036
|
let submitError = null;
|
|
10013
10037
|
let submitRejectedCleanly = false;
|
|
10014
10038
|
try {
|
|
10015
|
-
const response = await
|
|
10039
|
+
const response = await retryTransient(
|
|
10040
|
+
() => this.deps.client.submitCertificationRequest(certificationData),
|
|
10041
|
+
signal,
|
|
10042
|
+
this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
|
|
10043
|
+
);
|
|
10016
10044
|
if (response.status !== CertificationStatus.SUCCESS) {
|
|
10017
10045
|
submitError = new SphereError(`${failLabel}: ${response.status}`, failCode);
|
|
10018
10046
|
submitRejectedCleanly = CLEAN_REJECT_STATUSES.has(response.status);
|
|
@@ -10026,33 +10054,21 @@ var SphereTokenEngine = class {
|
|
|
10026
10054
|
client: this.deps.client,
|
|
10027
10055
|
trustBase: this.deps.trustBase,
|
|
10028
10056
|
predicateVerifier: this.deps.predicateVerifier,
|
|
10029
|
-
timeoutMs: this.proofTimeoutMs,
|
|
10030
10057
|
intervalMs: this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
|
|
10031
10058
|
// #683
|
|
10032
10059
|
},
|
|
10033
10060
|
transaction,
|
|
10034
|
-
|
|
10061
|
+
signal
|
|
10035
10062
|
);
|
|
10036
10063
|
} catch (err) {
|
|
10037
|
-
|
|
10038
|
-
throw new TransferConflictError(
|
|
10039
|
-
`${failLabel}: the source state was already consumed by a different transaction (lost race \u2014 abort this intent and re-plan under a new transferId)`,
|
|
10040
|
-
err
|
|
10041
|
-
);
|
|
10042
|
-
}
|
|
10043
|
-
if (submitRejectedCleanly) throw submitError;
|
|
10044
|
-
throw new ProofUnconfirmedError(
|
|
10045
|
-
`${failLabel}: certification unconfirmed \u2014 the source spend may be on-chain; keep the intent open and resume under the same transferId`,
|
|
10046
|
-
submitError ?? err
|
|
10047
|
-
);
|
|
10064
|
+
throw certificationFailure(err, failLabel, submitError, submitRejectedCleanly);
|
|
10048
10065
|
}
|
|
10049
10066
|
}
|
|
10050
10067
|
/**
|
|
10051
|
-
* One inclusion-proof wait, with the deadline OWNED here (#739)
|
|
10052
|
-
*
|
|
10053
|
-
*
|
|
10054
|
-
*
|
|
10055
|
-
* (keep-open), which is the correct posture: the spend may be on-chain.
|
|
10068
|
+
* One inclusion-proof wait, with the deadline OWNED here (#739) so it is bounded
|
|
10069
|
+
* and configurable rather than the SDK's fixed default. A hit throws, and the
|
|
10070
|
+
* caller maps it to ProofUnconfirmedError (keep-open) — the correct posture,
|
|
10071
|
+
* since the spend may be on-chain.
|
|
10056
10072
|
*/
|
|
10057
10073
|
/** Fail fast if this engine's key does not own the token's current state. */
|
|
10058
10074
|
assertOwned(token) {
|
|
@@ -11387,7 +11403,7 @@ function createMachineStores(kv) {
|
|
|
11387
11403
|
}
|
|
11388
11404
|
|
|
11389
11405
|
// modules/payments-v2/machine/TransferMachine.ts
|
|
11390
|
-
var CERTIFY_WIDTH =
|
|
11406
|
+
var CERTIFY_WIDTH = 20;
|
|
11391
11407
|
var KEEP_OPEN_CODES = /* @__PURE__ */ new Set([
|
|
11392
11408
|
"CERTIFICATION_UNCONFIRMED",
|
|
11393
11409
|
"CHECKPOINT_PERSIST_FAILED",
|
|
@@ -11483,12 +11499,15 @@ var TransferMachine = class {
|
|
|
11483
11499
|
payload: r.payload,
|
|
11484
11500
|
...r.payload.memo !== void 0 ? { memo: r.payload.memo } : {}
|
|
11485
11501
|
};
|
|
11486
|
-
const
|
|
11502
|
+
const outcomes = await this.resumeOps(ctx, engine, r);
|
|
11503
|
+
const { committed, cls, firstError } = summarize(outcomes);
|
|
11504
|
+
const conflicts = this.toConflicts(engine, r, outcomes);
|
|
11487
11505
|
if (committed.length === 0 && conflicts.length > 0) throw conflicts[0].error;
|
|
11488
11506
|
let deliveryPending = false;
|
|
11489
11507
|
if (committed.length > 0) {
|
|
11490
11508
|
deliveryPending = await this.deliverSet(r.transferId, r.payload.recipient, r.payload.memo, committed);
|
|
11491
11509
|
}
|
|
11510
|
+
if (cls !== null && cls !== "conflict") throw firstError;
|
|
11492
11511
|
await this.applyCommitted(engine, r.transferId, committed);
|
|
11493
11512
|
if (conflicts.length > 0) {
|
|
11494
11513
|
return { deliveryPending, partial: await this.settlePartial(engine, r, committed, conflicts) };
|
|
@@ -11573,25 +11592,28 @@ var TransferMachine = class {
|
|
|
11573
11592
|
);
|
|
11574
11593
|
return { recipientBlob: outputs[0].blob.token, changeBlob: outputs[1].blob.token };
|
|
11575
11594
|
}
|
|
11595
|
+
/** Raw outcomes; `summarize` alone decides what they mean — same as the send path. */
|
|
11576
11596
|
async resumeOps(ctx, engine, r) {
|
|
11577
|
-
const
|
|
11578
|
-
const conflicts = [];
|
|
11597
|
+
const outcomes = [];
|
|
11579
11598
|
for (const op of buildOps(r.payload)) {
|
|
11580
11599
|
const journaled = op.kind === "direct" ? r.journaled.get(op.opIndex) : void 0;
|
|
11581
11600
|
if (journaled !== void 0) {
|
|
11582
|
-
|
|
11583
|
-
continue;
|
|
11584
|
-
}
|
|
11585
|
-
const source = r.sources.get(op.sourceTokenId);
|
|
11586
|
-
const outcome = await this.certifyOne(ctx, engine, op, source);
|
|
11587
|
-
if (outcome.certified) {
|
|
11588
|
-
committed.push(outcome);
|
|
11601
|
+
outcomes.push({ op, certified: true, recipientBlob: hexToBytes4(journaled.blobHex) });
|
|
11589
11602
|
continue;
|
|
11590
11603
|
}
|
|
11591
|
-
|
|
11592
|
-
|
|
11604
|
+
outcomes.push(await this.certifyOne(ctx, engine, op, r.sources.get(op.sourceTokenId)));
|
|
11605
|
+
const last = outcomes[outcomes.length - 1];
|
|
11606
|
+
if (last.error !== void 0 && classifyError(last.error) !== "conflict") break;
|
|
11593
11607
|
}
|
|
11594
|
-
return
|
|
11608
|
+
return outcomes;
|
|
11609
|
+
}
|
|
11610
|
+
/** Conflicted ops with the amount each leaves undelivered (#690 shortfall math). */
|
|
11611
|
+
toConflicts(engine, r, outcomes) {
|
|
11612
|
+
return outcomes.filter((o) => !o.certified && classifyError(o.error) === "conflict").map((o) => ({
|
|
11613
|
+
op: o.op,
|
|
11614
|
+
error: o.error,
|
|
11615
|
+
amount: conflictAmount(engine, r.payload, o.op, r.sources.get(o.op.sourceTokenId))
|
|
11616
|
+
}));
|
|
11595
11617
|
}
|
|
11596
11618
|
async deliverSet(transferId, recipientPubkey, memo, committed) {
|
|
11597
11619
|
const journaled = committed.filter((o) => o.error === void 0 && o.recipientBlob !== void 0);
|