@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/index.cjs
CHANGED
|
@@ -9524,41 +9524,35 @@ function resolveProofTimeoutMs(configured) {
|
|
|
9524
9524
|
}
|
|
9525
9525
|
return configured;
|
|
9526
9526
|
}
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
if (prop === "addEventListener") {
|
|
9531
|
-
return (type, listener, options) => {
|
|
9532
|
-
if (type === "abort" && target.aborted) {
|
|
9533
|
-
queueMicrotask(() => {
|
|
9534
|
-
listener.call(target, new Event("abort"));
|
|
9535
|
-
});
|
|
9536
|
-
return;
|
|
9537
|
-
}
|
|
9538
|
-
target.addEventListener(type, listener, options);
|
|
9539
|
-
};
|
|
9540
|
-
}
|
|
9541
|
-
const value = Reflect.get(target, prop, target);
|
|
9542
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
9543
|
-
}
|
|
9544
|
-
});
|
|
9527
|
+
var TRANSIENT_HTTP_STATUSES = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
9528
|
+
function isTransientGatewayError(err) {
|
|
9529
|
+
return err instanceof import_JsonRpcNetworkError.JsonRpcNetworkError && TRANSIENT_HTTP_STATUSES.has(err.status);
|
|
9545
9530
|
}
|
|
9546
|
-
function
|
|
9547
|
-
return new Promise((
|
|
9548
|
-
|
|
9549
|
-
reject(signal.reason);
|
|
9550
|
-
return;
|
|
9551
|
-
}
|
|
9531
|
+
function pause(ms, signal) {
|
|
9532
|
+
return new Promise((resolve) => {
|
|
9533
|
+
const timer = setTimeout(resolve, ms);
|
|
9552
9534
|
signal.addEventListener(
|
|
9553
9535
|
"abort",
|
|
9554
9536
|
() => {
|
|
9555
|
-
|
|
9537
|
+
clearTimeout(timer);
|
|
9538
|
+
resolve();
|
|
9556
9539
|
},
|
|
9557
9540
|
{ once: true }
|
|
9558
9541
|
);
|
|
9559
9542
|
});
|
|
9560
9543
|
}
|
|
9561
|
-
async function
|
|
9544
|
+
async function retryTransient(attempt, signal, intervalMs) {
|
|
9545
|
+
for (; ; ) {
|
|
9546
|
+
try {
|
|
9547
|
+
return await attempt();
|
|
9548
|
+
} catch (err) {
|
|
9549
|
+
if (signal.aborted) throw err;
|
|
9550
|
+
if (!isTransientGatewayError(err)) throw err;
|
|
9551
|
+
await pause(intervalMs, signal);
|
|
9552
|
+
}
|
|
9553
|
+
}
|
|
9554
|
+
}
|
|
9555
|
+
function certificationDeadline(timeoutMs, external, label) {
|
|
9562
9556
|
const controller = new AbortController();
|
|
9563
9557
|
const relayAbort = () => {
|
|
9564
9558
|
controller.abort(external?.reason);
|
|
@@ -9568,28 +9562,29 @@ async function awaitProofBounded(deps, transaction, external) {
|
|
|
9568
9562
|
else external.addEventListener("abort", relayAbort, { once: true });
|
|
9569
9563
|
}
|
|
9570
9564
|
const timer = setTimeout(() => {
|
|
9571
|
-
controller.abort(
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9565
|
+
controller.abort(new SphereError(`${label} within ${String(timeoutMs)} ms`, "AGGREGATOR_ERROR"));
|
|
9566
|
+
}, timeoutMs);
|
|
9567
|
+
return {
|
|
9568
|
+
signal: controller.signal,
|
|
9569
|
+
dispose: () => {
|
|
9570
|
+
clearTimeout(timer);
|
|
9571
|
+
external?.removeEventListener("abort", relayAbort);
|
|
9572
|
+
}
|
|
9573
|
+
};
|
|
9574
|
+
}
|
|
9575
|
+
async function awaitProofBounded(deps, transaction, signal) {
|
|
9576
|
+
return retryTransient(
|
|
9577
|
+
() => (0, import_InclusionProofUtils.waitInclusionProof)(
|
|
9578
|
+
deps.client,
|
|
9579
|
+
deps.trustBase,
|
|
9580
|
+
deps.predicateVerifier,
|
|
9581
|
+
transaction,
|
|
9582
|
+
signal,
|
|
9583
|
+
deps.intervalMs
|
|
9584
|
+
),
|
|
9585
|
+
signal,
|
|
9584
9586
|
deps.intervalMs
|
|
9585
9587
|
);
|
|
9586
|
-
wait.catch(() => void 0);
|
|
9587
|
-
try {
|
|
9588
|
-
return await Promise.race([wait, rejectOnAbort(controller.signal)]);
|
|
9589
|
-
} finally {
|
|
9590
|
-
clearTimeout(timer);
|
|
9591
|
-
external?.removeEventListener("abort", relayAbort);
|
|
9592
|
-
}
|
|
9593
9588
|
}
|
|
9594
9589
|
|
|
9595
9590
|
// token-engine/errors.ts
|
|
@@ -9631,6 +9626,21 @@ var CheckpointTrustbaseMismatchError = class extends SphereError {
|
|
|
9631
9626
|
}
|
|
9632
9627
|
};
|
|
9633
9628
|
|
|
9629
|
+
// token-engine/certification-outcome.ts
|
|
9630
|
+
function certificationFailure(err, failLabel, submitError, submitRejectedCleanly) {
|
|
9631
|
+
if (err instanceof Error && err.message === `Invalid inclusion proof status: ${import_InclusionProofVerificationRule.InclusionProofVerificationStatus.TRANSACTION_HASH_MISMATCH}`) {
|
|
9632
|
+
return new TransferConflictError(
|
|
9633
|
+
`${failLabel}: the source state was already consumed by a different transaction (lost race \u2014 abort this intent and re-plan under a new transferId)`,
|
|
9634
|
+
err
|
|
9635
|
+
);
|
|
9636
|
+
}
|
|
9637
|
+
if (submitRejectedCleanly) return submitError;
|
|
9638
|
+
return new ProofUnconfirmedError(
|
|
9639
|
+
`${failLabel}: certification unconfirmed \u2014 the source spend may be on-chain; keep the intent open and resume under the same transferId`,
|
|
9640
|
+
submitError ?? err
|
|
9641
|
+
);
|
|
9642
|
+
}
|
|
9643
|
+
|
|
9634
9644
|
// token-engine/token-blob.ts
|
|
9635
9645
|
var TOKEN_BLOB_TAG = 39051n;
|
|
9636
9646
|
var TOKEN_BLOB_VERSION = 1;
|
|
@@ -10153,10 +10163,28 @@ var SphereTokenEngine = class {
|
|
|
10153
10163
|
* failure, not a resume case).
|
|
10154
10164
|
*/
|
|
10155
10165
|
async submitAndAwaitProof(certificationData, transaction, failLabel, failCode, options) {
|
|
10166
|
+
const deadline = certificationDeadline(
|
|
10167
|
+
this.proofTimeoutMs,
|
|
10168
|
+
options?.signal,
|
|
10169
|
+
`${failLabel}: not certified`
|
|
10170
|
+
);
|
|
10171
|
+
try {
|
|
10172
|
+
return await this.submitThenProve(certificationData, transaction, failLabel, failCode, deadline.signal);
|
|
10173
|
+
} finally {
|
|
10174
|
+
deadline.dispose();
|
|
10175
|
+
}
|
|
10176
|
+
}
|
|
10177
|
+
/** One certification attempt under one deadline: the metered submit retries
|
|
10178
|
+
* transient answers instead of falling through to a proof that cannot exist. */
|
|
10179
|
+
async submitThenProve(certificationData, transaction, failLabel, failCode, signal) {
|
|
10156
10180
|
let submitError = null;
|
|
10157
10181
|
let submitRejectedCleanly = false;
|
|
10158
10182
|
try {
|
|
10159
|
-
const response = await
|
|
10183
|
+
const response = await retryTransient(
|
|
10184
|
+
() => this.deps.client.submitCertificationRequest(certificationData),
|
|
10185
|
+
signal,
|
|
10186
|
+
this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
|
|
10187
|
+
);
|
|
10160
10188
|
if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
|
|
10161
10189
|
submitError = new SphereError(`${failLabel}: ${response.status}`, failCode);
|
|
10162
10190
|
submitRejectedCleanly = CLEAN_REJECT_STATUSES.has(response.status);
|
|
@@ -10170,33 +10198,21 @@ var SphereTokenEngine = class {
|
|
|
10170
10198
|
client: this.deps.client,
|
|
10171
10199
|
trustBase: this.deps.trustBase,
|
|
10172
10200
|
predicateVerifier: this.deps.predicateVerifier,
|
|
10173
|
-
timeoutMs: this.proofTimeoutMs,
|
|
10174
10201
|
intervalMs: this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
|
|
10175
10202
|
// #683
|
|
10176
10203
|
},
|
|
10177
10204
|
transaction,
|
|
10178
|
-
|
|
10205
|
+
signal
|
|
10179
10206
|
);
|
|
10180
10207
|
} catch (err) {
|
|
10181
|
-
|
|
10182
|
-
throw new TransferConflictError(
|
|
10183
|
-
`${failLabel}: the source state was already consumed by a different transaction (lost race \u2014 abort this intent and re-plan under a new transferId)`,
|
|
10184
|
-
err
|
|
10185
|
-
);
|
|
10186
|
-
}
|
|
10187
|
-
if (submitRejectedCleanly) throw submitError;
|
|
10188
|
-
throw new ProofUnconfirmedError(
|
|
10189
|
-
`${failLabel}: certification unconfirmed \u2014 the source spend may be on-chain; keep the intent open and resume under the same transferId`,
|
|
10190
|
-
submitError ?? err
|
|
10191
|
-
);
|
|
10208
|
+
throw certificationFailure(err, failLabel, submitError, submitRejectedCleanly);
|
|
10192
10209
|
}
|
|
10193
10210
|
}
|
|
10194
10211
|
/**
|
|
10195
|
-
* One inclusion-proof wait, with the deadline OWNED here (#739)
|
|
10196
|
-
*
|
|
10197
|
-
*
|
|
10198
|
-
*
|
|
10199
|
-
* (keep-open), which is the correct posture: the spend may be on-chain.
|
|
10212
|
+
* One inclusion-proof wait, with the deadline OWNED here (#739) so it is bounded
|
|
10213
|
+
* and configurable rather than the SDK's fixed default. A hit throws, and the
|
|
10214
|
+
* caller maps it to ProofUnconfirmedError (keep-open) — the correct posture,
|
|
10215
|
+
* since the spend may be on-chain.
|
|
10200
10216
|
*/
|
|
10201
10217
|
/** Fail fast if this engine's key does not own the token's current state. */
|
|
10202
10218
|
assertOwned(token) {
|
|
@@ -11531,7 +11547,7 @@ function createMachineStores(kv) {
|
|
|
11531
11547
|
}
|
|
11532
11548
|
|
|
11533
11549
|
// modules/payments-v2/machine/TransferMachine.ts
|
|
11534
|
-
var CERTIFY_WIDTH =
|
|
11550
|
+
var CERTIFY_WIDTH = 20;
|
|
11535
11551
|
var KEEP_OPEN_CODES = /* @__PURE__ */ new Set([
|
|
11536
11552
|
"CERTIFICATION_UNCONFIRMED",
|
|
11537
11553
|
"CHECKPOINT_PERSIST_FAILED",
|
|
@@ -11627,12 +11643,15 @@ var TransferMachine = class {
|
|
|
11627
11643
|
payload: r.payload,
|
|
11628
11644
|
...r.payload.memo !== void 0 ? { memo: r.payload.memo } : {}
|
|
11629
11645
|
};
|
|
11630
|
-
const
|
|
11646
|
+
const outcomes = await this.resumeOps(ctx, engine, r);
|
|
11647
|
+
const { committed, cls, firstError } = summarize(outcomes);
|
|
11648
|
+
const conflicts = this.toConflicts(engine, r, outcomes);
|
|
11631
11649
|
if (committed.length === 0 && conflicts.length > 0) throw conflicts[0].error;
|
|
11632
11650
|
let deliveryPending = false;
|
|
11633
11651
|
if (committed.length > 0) {
|
|
11634
11652
|
deliveryPending = await this.deliverSet(r.transferId, r.payload.recipient, r.payload.memo, committed);
|
|
11635
11653
|
}
|
|
11654
|
+
if (cls !== null && cls !== "conflict") throw firstError;
|
|
11636
11655
|
await this.applyCommitted(engine, r.transferId, committed);
|
|
11637
11656
|
if (conflicts.length > 0) {
|
|
11638
11657
|
return { deliveryPending, partial: await this.settlePartial(engine, r, committed, conflicts) };
|
|
@@ -11717,25 +11736,28 @@ var TransferMachine = class {
|
|
|
11717
11736
|
);
|
|
11718
11737
|
return { recipientBlob: outputs[0].blob.token, changeBlob: outputs[1].blob.token };
|
|
11719
11738
|
}
|
|
11739
|
+
/** Raw outcomes; `summarize` alone decides what they mean — same as the send path. */
|
|
11720
11740
|
async resumeOps(ctx, engine, r) {
|
|
11721
|
-
const
|
|
11722
|
-
const conflicts = [];
|
|
11741
|
+
const outcomes = [];
|
|
11723
11742
|
for (const op of buildOps(r.payload)) {
|
|
11724
11743
|
const journaled = op.kind === "direct" ? r.journaled.get(op.opIndex) : void 0;
|
|
11725
11744
|
if (journaled !== void 0) {
|
|
11726
|
-
|
|
11727
|
-
continue;
|
|
11728
|
-
}
|
|
11729
|
-
const source = r.sources.get(op.sourceTokenId);
|
|
11730
|
-
const outcome = await this.certifyOne(ctx, engine, op, source);
|
|
11731
|
-
if (outcome.certified) {
|
|
11732
|
-
committed.push(outcome);
|
|
11745
|
+
outcomes.push({ op, certified: true, recipientBlob: hexToBytes4(journaled.blobHex) });
|
|
11733
11746
|
continue;
|
|
11734
11747
|
}
|
|
11735
|
-
|
|
11736
|
-
|
|
11748
|
+
outcomes.push(await this.certifyOne(ctx, engine, op, r.sources.get(op.sourceTokenId)));
|
|
11749
|
+
const last = outcomes[outcomes.length - 1];
|
|
11750
|
+
if (last.error !== void 0 && classifyError(last.error) !== "conflict") break;
|
|
11737
11751
|
}
|
|
11738
|
-
return
|
|
11752
|
+
return outcomes;
|
|
11753
|
+
}
|
|
11754
|
+
/** Conflicted ops with the amount each leaves undelivered (#690 shortfall math). */
|
|
11755
|
+
toConflicts(engine, r, outcomes) {
|
|
11756
|
+
return outcomes.filter((o) => !o.certified && classifyError(o.error) === "conflict").map((o) => ({
|
|
11757
|
+
op: o.op,
|
|
11758
|
+
error: o.error,
|
|
11759
|
+
amount: conflictAmount(engine, r.payload, o.op, r.sources.get(o.op.sourceTokenId))
|
|
11760
|
+
}));
|
|
11739
11761
|
}
|
|
11740
11762
|
async deliverSet(transferId, recipientPubkey, memo, committed) {
|
|
11741
11763
|
const journaled = committed.filter((o) => o.error === void 0 && o.recipientBlob !== void 0);
|