@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.cjs
CHANGED
|
@@ -9466,41 +9466,35 @@ function resolveProofTimeoutMs(configured) {
|
|
|
9466
9466
|
}
|
|
9467
9467
|
return configured;
|
|
9468
9468
|
}
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
if (prop === "addEventListener") {
|
|
9473
|
-
return (type, listener, options) => {
|
|
9474
|
-
if (type === "abort" && target.aborted) {
|
|
9475
|
-
queueMicrotask(() => {
|
|
9476
|
-
listener.call(target, new Event("abort"));
|
|
9477
|
-
});
|
|
9478
|
-
return;
|
|
9479
|
-
}
|
|
9480
|
-
target.addEventListener(type, listener, options);
|
|
9481
|
-
};
|
|
9482
|
-
}
|
|
9483
|
-
const value = Reflect.get(target, prop, target);
|
|
9484
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
9485
|
-
}
|
|
9486
|
-
});
|
|
9469
|
+
var TRANSIENT_HTTP_STATUSES = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
9470
|
+
function isTransientGatewayError(err) {
|
|
9471
|
+
return err instanceof import_JsonRpcNetworkError.JsonRpcNetworkError && TRANSIENT_HTTP_STATUSES.has(err.status);
|
|
9487
9472
|
}
|
|
9488
|
-
function
|
|
9489
|
-
return new Promise((
|
|
9490
|
-
|
|
9491
|
-
reject(signal.reason);
|
|
9492
|
-
return;
|
|
9493
|
-
}
|
|
9473
|
+
function pause(ms, signal) {
|
|
9474
|
+
return new Promise((resolve) => {
|
|
9475
|
+
const timer = setTimeout(resolve, ms);
|
|
9494
9476
|
signal.addEventListener(
|
|
9495
9477
|
"abort",
|
|
9496
9478
|
() => {
|
|
9497
|
-
|
|
9479
|
+
clearTimeout(timer);
|
|
9480
|
+
resolve();
|
|
9498
9481
|
},
|
|
9499
9482
|
{ once: true }
|
|
9500
9483
|
);
|
|
9501
9484
|
});
|
|
9502
9485
|
}
|
|
9503
|
-
async function
|
|
9486
|
+
async function retryTransient(attempt, signal, intervalMs) {
|
|
9487
|
+
for (; ; ) {
|
|
9488
|
+
try {
|
|
9489
|
+
return await attempt();
|
|
9490
|
+
} catch (err) {
|
|
9491
|
+
if (signal.aborted) throw err;
|
|
9492
|
+
if (!isTransientGatewayError(err)) throw err;
|
|
9493
|
+
await pause(intervalMs, signal);
|
|
9494
|
+
}
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
9497
|
+
function certificationDeadline(timeoutMs, external, label) {
|
|
9504
9498
|
const controller = new AbortController();
|
|
9505
9499
|
const relayAbort = () => {
|
|
9506
9500
|
controller.abort(external?.reason);
|
|
@@ -9510,28 +9504,29 @@ async function awaitProofBounded(deps, transaction, external) {
|
|
|
9510
9504
|
else external.addEventListener("abort", relayAbort, { once: true });
|
|
9511
9505
|
}
|
|
9512
9506
|
const timer = setTimeout(() => {
|
|
9513
|
-
controller.abort(
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9507
|
+
controller.abort(new SphereError(`${label} within ${String(timeoutMs)} ms`, "AGGREGATOR_ERROR"));
|
|
9508
|
+
}, timeoutMs);
|
|
9509
|
+
return {
|
|
9510
|
+
signal: controller.signal,
|
|
9511
|
+
dispose: () => {
|
|
9512
|
+
clearTimeout(timer);
|
|
9513
|
+
external?.removeEventListener("abort", relayAbort);
|
|
9514
|
+
}
|
|
9515
|
+
};
|
|
9516
|
+
}
|
|
9517
|
+
async function awaitProofBounded(deps, transaction, signal) {
|
|
9518
|
+
return retryTransient(
|
|
9519
|
+
() => (0, import_InclusionProofUtils.waitInclusionProof)(
|
|
9520
|
+
deps.client,
|
|
9521
|
+
deps.trustBase,
|
|
9522
|
+
deps.predicateVerifier,
|
|
9523
|
+
transaction,
|
|
9524
|
+
signal,
|
|
9525
|
+
deps.intervalMs
|
|
9526
|
+
),
|
|
9527
|
+
signal,
|
|
9526
9528
|
deps.intervalMs
|
|
9527
9529
|
);
|
|
9528
|
-
wait.catch(() => void 0);
|
|
9529
|
-
try {
|
|
9530
|
-
return await Promise.race([wait, rejectOnAbort(controller.signal)]);
|
|
9531
|
-
} finally {
|
|
9532
|
-
clearTimeout(timer);
|
|
9533
|
-
external?.removeEventListener("abort", relayAbort);
|
|
9534
|
-
}
|
|
9535
9530
|
}
|
|
9536
9531
|
|
|
9537
9532
|
// token-engine/errors.ts
|
|
@@ -9573,6 +9568,21 @@ var CheckpointTrustbaseMismatchError = class extends SphereError {
|
|
|
9573
9568
|
}
|
|
9574
9569
|
};
|
|
9575
9570
|
|
|
9571
|
+
// token-engine/certification-outcome.ts
|
|
9572
|
+
function certificationFailure(err, failLabel, submitError, submitRejectedCleanly) {
|
|
9573
|
+
if (err instanceof Error && err.message === `Invalid inclusion proof status: ${import_InclusionProofVerificationRule.InclusionProofVerificationStatus.TRANSACTION_HASH_MISMATCH}`) {
|
|
9574
|
+
return new TransferConflictError(
|
|
9575
|
+
`${failLabel}: the source state was already consumed by a different transaction (lost race \u2014 abort this intent and re-plan under a new transferId)`,
|
|
9576
|
+
err
|
|
9577
|
+
);
|
|
9578
|
+
}
|
|
9579
|
+
if (submitRejectedCleanly) return submitError;
|
|
9580
|
+
return new ProofUnconfirmedError(
|
|
9581
|
+
`${failLabel}: certification unconfirmed \u2014 the source spend may be on-chain; keep the intent open and resume under the same transferId`,
|
|
9582
|
+
submitError ?? err
|
|
9583
|
+
);
|
|
9584
|
+
}
|
|
9585
|
+
|
|
9576
9586
|
// token-engine/token-blob.ts
|
|
9577
9587
|
var TOKEN_BLOB_TAG = 39051n;
|
|
9578
9588
|
var TOKEN_BLOB_VERSION = 1;
|
|
@@ -10095,10 +10105,28 @@ var SphereTokenEngine = class {
|
|
|
10095
10105
|
* failure, not a resume case).
|
|
10096
10106
|
*/
|
|
10097
10107
|
async submitAndAwaitProof(certificationData, transaction, failLabel, failCode, options) {
|
|
10108
|
+
const deadline = certificationDeadline(
|
|
10109
|
+
this.proofTimeoutMs,
|
|
10110
|
+
options?.signal,
|
|
10111
|
+
`${failLabel}: not certified`
|
|
10112
|
+
);
|
|
10113
|
+
try {
|
|
10114
|
+
return await this.submitThenProve(certificationData, transaction, failLabel, failCode, deadline.signal);
|
|
10115
|
+
} finally {
|
|
10116
|
+
deadline.dispose();
|
|
10117
|
+
}
|
|
10118
|
+
}
|
|
10119
|
+
/** One certification attempt under one deadline: the metered submit retries
|
|
10120
|
+
* transient answers instead of falling through to a proof that cannot exist. */
|
|
10121
|
+
async submitThenProve(certificationData, transaction, failLabel, failCode, signal) {
|
|
10098
10122
|
let submitError = null;
|
|
10099
10123
|
let submitRejectedCleanly = false;
|
|
10100
10124
|
try {
|
|
10101
|
-
const response = await
|
|
10125
|
+
const response = await retryTransient(
|
|
10126
|
+
() => this.deps.client.submitCertificationRequest(certificationData),
|
|
10127
|
+
signal,
|
|
10128
|
+
this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
|
|
10129
|
+
);
|
|
10102
10130
|
if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
|
|
10103
10131
|
submitError = new SphereError(`${failLabel}: ${response.status}`, failCode);
|
|
10104
10132
|
submitRejectedCleanly = CLEAN_REJECT_STATUSES.has(response.status);
|
|
@@ -10112,33 +10140,21 @@ var SphereTokenEngine = class {
|
|
|
10112
10140
|
client: this.deps.client,
|
|
10113
10141
|
trustBase: this.deps.trustBase,
|
|
10114
10142
|
predicateVerifier: this.deps.predicateVerifier,
|
|
10115
|
-
timeoutMs: this.proofTimeoutMs,
|
|
10116
10143
|
intervalMs: this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
|
|
10117
10144
|
// #683
|
|
10118
10145
|
},
|
|
10119
10146
|
transaction,
|
|
10120
|
-
|
|
10147
|
+
signal
|
|
10121
10148
|
);
|
|
10122
10149
|
} catch (err) {
|
|
10123
|
-
|
|
10124
|
-
throw new TransferConflictError(
|
|
10125
|
-
`${failLabel}: the source state was already consumed by a different transaction (lost race \u2014 abort this intent and re-plan under a new transferId)`,
|
|
10126
|
-
err
|
|
10127
|
-
);
|
|
10128
|
-
}
|
|
10129
|
-
if (submitRejectedCleanly) throw submitError;
|
|
10130
|
-
throw new ProofUnconfirmedError(
|
|
10131
|
-
`${failLabel}: certification unconfirmed \u2014 the source spend may be on-chain; keep the intent open and resume under the same transferId`,
|
|
10132
|
-
submitError ?? err
|
|
10133
|
-
);
|
|
10150
|
+
throw certificationFailure(err, failLabel, submitError, submitRejectedCleanly);
|
|
10134
10151
|
}
|
|
10135
10152
|
}
|
|
10136
10153
|
/**
|
|
10137
|
-
* One inclusion-proof wait, with the deadline OWNED here (#739)
|
|
10138
|
-
*
|
|
10139
|
-
*
|
|
10140
|
-
*
|
|
10141
|
-
* (keep-open), which is the correct posture: the spend may be on-chain.
|
|
10154
|
+
* One inclusion-proof wait, with the deadline OWNED here (#739) so it is bounded
|
|
10155
|
+
* and configurable rather than the SDK's fixed default. A hit throws, and the
|
|
10156
|
+
* caller maps it to ProofUnconfirmedError (keep-open) — the correct posture,
|
|
10157
|
+
* since the spend may be on-chain.
|
|
10142
10158
|
*/
|
|
10143
10159
|
/** Fail fast if this engine's key does not own the token's current state. */
|
|
10144
10160
|
assertOwned(token) {
|
|
@@ -11473,7 +11489,7 @@ function createMachineStores(kv) {
|
|
|
11473
11489
|
}
|
|
11474
11490
|
|
|
11475
11491
|
// modules/payments-v2/machine/TransferMachine.ts
|
|
11476
|
-
var CERTIFY_WIDTH =
|
|
11492
|
+
var CERTIFY_WIDTH = 20;
|
|
11477
11493
|
var KEEP_OPEN_CODES = /* @__PURE__ */ new Set([
|
|
11478
11494
|
"CERTIFICATION_UNCONFIRMED",
|
|
11479
11495
|
"CHECKPOINT_PERSIST_FAILED",
|
|
@@ -11569,12 +11585,15 @@ var TransferMachine = class {
|
|
|
11569
11585
|
payload: r.payload,
|
|
11570
11586
|
...r.payload.memo !== void 0 ? { memo: r.payload.memo } : {}
|
|
11571
11587
|
};
|
|
11572
|
-
const
|
|
11588
|
+
const outcomes = await this.resumeOps(ctx, engine, r);
|
|
11589
|
+
const { committed, cls, firstError } = summarize(outcomes);
|
|
11590
|
+
const conflicts = this.toConflicts(engine, r, outcomes);
|
|
11573
11591
|
if (committed.length === 0 && conflicts.length > 0) throw conflicts[0].error;
|
|
11574
11592
|
let deliveryPending = false;
|
|
11575
11593
|
if (committed.length > 0) {
|
|
11576
11594
|
deliveryPending = await this.deliverSet(r.transferId, r.payload.recipient, r.payload.memo, committed);
|
|
11577
11595
|
}
|
|
11596
|
+
if (cls !== null && cls !== "conflict") throw firstError;
|
|
11578
11597
|
await this.applyCommitted(engine, r.transferId, committed);
|
|
11579
11598
|
if (conflicts.length > 0) {
|
|
11580
11599
|
return { deliveryPending, partial: await this.settlePartial(engine, r, committed, conflicts) };
|
|
@@ -11659,25 +11678,28 @@ var TransferMachine = class {
|
|
|
11659
11678
|
);
|
|
11660
11679
|
return { recipientBlob: outputs[0].blob.token, changeBlob: outputs[1].blob.token };
|
|
11661
11680
|
}
|
|
11681
|
+
/** Raw outcomes; `summarize` alone decides what they mean — same as the send path. */
|
|
11662
11682
|
async resumeOps(ctx, engine, r) {
|
|
11663
|
-
const
|
|
11664
|
-
const conflicts = [];
|
|
11683
|
+
const outcomes = [];
|
|
11665
11684
|
for (const op of buildOps(r.payload)) {
|
|
11666
11685
|
const journaled = op.kind === "direct" ? r.journaled.get(op.opIndex) : void 0;
|
|
11667
11686
|
if (journaled !== void 0) {
|
|
11668
|
-
|
|
11669
|
-
continue;
|
|
11670
|
-
}
|
|
11671
|
-
const source = r.sources.get(op.sourceTokenId);
|
|
11672
|
-
const outcome = await this.certifyOne(ctx, engine, op, source);
|
|
11673
|
-
if (outcome.certified) {
|
|
11674
|
-
committed.push(outcome);
|
|
11687
|
+
outcomes.push({ op, certified: true, recipientBlob: hexToBytes4(journaled.blobHex) });
|
|
11675
11688
|
continue;
|
|
11676
11689
|
}
|
|
11677
|
-
|
|
11678
|
-
|
|
11690
|
+
outcomes.push(await this.certifyOne(ctx, engine, op, r.sources.get(op.sourceTokenId)));
|
|
11691
|
+
const last = outcomes[outcomes.length - 1];
|
|
11692
|
+
if (last.error !== void 0 && classifyError(last.error) !== "conflict") break;
|
|
11679
11693
|
}
|
|
11680
|
-
return
|
|
11694
|
+
return outcomes;
|
|
11695
|
+
}
|
|
11696
|
+
/** Conflicted ops with the amount each leaves undelivered (#690 shortfall math). */
|
|
11697
|
+
toConflicts(engine, r, outcomes) {
|
|
11698
|
+
return outcomes.filter((o) => !o.certified && classifyError(o.error) === "conflict").map((o) => ({
|
|
11699
|
+
op: o.op,
|
|
11700
|
+
error: o.error,
|
|
11701
|
+
amount: conflictAmount(engine, r.payload, o.op, r.sources.get(o.op.sourceTokenId))
|
|
11702
|
+
}));
|
|
11681
11703
|
}
|
|
11682
11704
|
async deliverSet(transferId, recipientPubkey, memo, committed) {
|
|
11683
11705
|
const journaled = committed.filter((o) => o.error === void 0 && o.recipientBlob !== void 0);
|