@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.js
CHANGED
|
@@ -9396,41 +9396,35 @@ function resolveProofTimeoutMs(configured) {
|
|
|
9396
9396
|
}
|
|
9397
9397
|
return configured;
|
|
9398
9398
|
}
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
if (prop === "addEventListener") {
|
|
9403
|
-
return (type, listener, options) => {
|
|
9404
|
-
if (type === "abort" && target.aborted) {
|
|
9405
|
-
queueMicrotask(() => {
|
|
9406
|
-
listener.call(target, new Event("abort"));
|
|
9407
|
-
});
|
|
9408
|
-
return;
|
|
9409
|
-
}
|
|
9410
|
-
target.addEventListener(type, listener, options);
|
|
9411
|
-
};
|
|
9412
|
-
}
|
|
9413
|
-
const value = Reflect.get(target, prop, target);
|
|
9414
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
9415
|
-
}
|
|
9416
|
-
});
|
|
9399
|
+
var TRANSIENT_HTTP_STATUSES = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
9400
|
+
function isTransientGatewayError(err) {
|
|
9401
|
+
return err instanceof JsonRpcNetworkError && TRANSIENT_HTTP_STATUSES.has(err.status);
|
|
9417
9402
|
}
|
|
9418
|
-
function
|
|
9419
|
-
return new Promise((
|
|
9420
|
-
|
|
9421
|
-
reject(signal.reason);
|
|
9422
|
-
return;
|
|
9423
|
-
}
|
|
9403
|
+
function pause(ms, signal) {
|
|
9404
|
+
return new Promise((resolve) => {
|
|
9405
|
+
const timer = setTimeout(resolve, ms);
|
|
9424
9406
|
signal.addEventListener(
|
|
9425
9407
|
"abort",
|
|
9426
9408
|
() => {
|
|
9427
|
-
|
|
9409
|
+
clearTimeout(timer);
|
|
9410
|
+
resolve();
|
|
9428
9411
|
},
|
|
9429
9412
|
{ once: true }
|
|
9430
9413
|
);
|
|
9431
9414
|
});
|
|
9432
9415
|
}
|
|
9433
|
-
async function
|
|
9416
|
+
async function retryTransient(attempt, signal, intervalMs) {
|
|
9417
|
+
for (; ; ) {
|
|
9418
|
+
try {
|
|
9419
|
+
return await attempt();
|
|
9420
|
+
} catch (err) {
|
|
9421
|
+
if (signal.aborted) throw err;
|
|
9422
|
+
if (!isTransientGatewayError(err)) throw err;
|
|
9423
|
+
await pause(intervalMs, signal);
|
|
9424
|
+
}
|
|
9425
|
+
}
|
|
9426
|
+
}
|
|
9427
|
+
function certificationDeadline(timeoutMs, external, label) {
|
|
9434
9428
|
const controller = new AbortController();
|
|
9435
9429
|
const relayAbort = () => {
|
|
9436
9430
|
controller.abort(external?.reason);
|
|
@@ -9440,28 +9434,29 @@ async function awaitProofBounded(deps, transaction, external) {
|
|
|
9440
9434
|
else external.addEventListener("abort", relayAbort, { once: true });
|
|
9441
9435
|
}
|
|
9442
9436
|
const timer = setTimeout(() => {
|
|
9443
|
-
controller.abort(
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9437
|
+
controller.abort(new SphereError(`${label} within ${String(timeoutMs)} ms`, "AGGREGATOR_ERROR"));
|
|
9438
|
+
}, timeoutMs);
|
|
9439
|
+
return {
|
|
9440
|
+
signal: controller.signal,
|
|
9441
|
+
dispose: () => {
|
|
9442
|
+
clearTimeout(timer);
|
|
9443
|
+
external?.removeEventListener("abort", relayAbort);
|
|
9444
|
+
}
|
|
9445
|
+
};
|
|
9446
|
+
}
|
|
9447
|
+
async function awaitProofBounded(deps, transaction, signal) {
|
|
9448
|
+
return retryTransient(
|
|
9449
|
+
() => waitInclusionProof(
|
|
9450
|
+
deps.client,
|
|
9451
|
+
deps.trustBase,
|
|
9452
|
+
deps.predicateVerifier,
|
|
9453
|
+
transaction,
|
|
9454
|
+
signal,
|
|
9455
|
+
deps.intervalMs
|
|
9456
|
+
),
|
|
9457
|
+
signal,
|
|
9456
9458
|
deps.intervalMs
|
|
9457
9459
|
);
|
|
9458
|
-
wait.catch(() => void 0);
|
|
9459
|
-
try {
|
|
9460
|
-
return await Promise.race([wait, rejectOnAbort(controller.signal)]);
|
|
9461
|
-
} finally {
|
|
9462
|
-
clearTimeout(timer);
|
|
9463
|
-
external?.removeEventListener("abort", relayAbort);
|
|
9464
|
-
}
|
|
9465
9460
|
}
|
|
9466
9461
|
|
|
9467
9462
|
// token-engine/errors.ts
|
|
@@ -9503,6 +9498,21 @@ var CheckpointTrustbaseMismatchError = class extends SphereError {
|
|
|
9503
9498
|
}
|
|
9504
9499
|
};
|
|
9505
9500
|
|
|
9501
|
+
// token-engine/certification-outcome.ts
|
|
9502
|
+
function certificationFailure(err, failLabel, submitError, submitRejectedCleanly) {
|
|
9503
|
+
if (err instanceof Error && err.message === `Invalid inclusion proof status: ${InclusionProofVerificationStatus.TRANSACTION_HASH_MISMATCH}`) {
|
|
9504
|
+
return new TransferConflictError(
|
|
9505
|
+
`${failLabel}: the source state was already consumed by a different transaction (lost race \u2014 abort this intent and re-plan under a new transferId)`,
|
|
9506
|
+
err
|
|
9507
|
+
);
|
|
9508
|
+
}
|
|
9509
|
+
if (submitRejectedCleanly) return submitError;
|
|
9510
|
+
return new ProofUnconfirmedError(
|
|
9511
|
+
`${failLabel}: certification unconfirmed \u2014 the source spend may be on-chain; keep the intent open and resume under the same transferId`,
|
|
9512
|
+
submitError ?? err
|
|
9513
|
+
);
|
|
9514
|
+
}
|
|
9515
|
+
|
|
9506
9516
|
// token-engine/token-blob.ts
|
|
9507
9517
|
var TOKEN_BLOB_TAG = 39051n;
|
|
9508
9518
|
var TOKEN_BLOB_VERSION = 1;
|
|
@@ -10025,10 +10035,28 @@ var SphereTokenEngine = class {
|
|
|
10025
10035
|
* failure, not a resume case).
|
|
10026
10036
|
*/
|
|
10027
10037
|
async submitAndAwaitProof(certificationData, transaction, failLabel, failCode, options) {
|
|
10038
|
+
const deadline = certificationDeadline(
|
|
10039
|
+
this.proofTimeoutMs,
|
|
10040
|
+
options?.signal,
|
|
10041
|
+
`${failLabel}: not certified`
|
|
10042
|
+
);
|
|
10043
|
+
try {
|
|
10044
|
+
return await this.submitThenProve(certificationData, transaction, failLabel, failCode, deadline.signal);
|
|
10045
|
+
} finally {
|
|
10046
|
+
deadline.dispose();
|
|
10047
|
+
}
|
|
10048
|
+
}
|
|
10049
|
+
/** One certification attempt under one deadline: the metered submit retries
|
|
10050
|
+
* transient answers instead of falling through to a proof that cannot exist. */
|
|
10051
|
+
async submitThenProve(certificationData, transaction, failLabel, failCode, signal) {
|
|
10028
10052
|
let submitError = null;
|
|
10029
10053
|
let submitRejectedCleanly = false;
|
|
10030
10054
|
try {
|
|
10031
|
-
const response = await
|
|
10055
|
+
const response = await retryTransient(
|
|
10056
|
+
() => this.deps.client.submitCertificationRequest(certificationData),
|
|
10057
|
+
signal,
|
|
10058
|
+
this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
|
|
10059
|
+
);
|
|
10032
10060
|
if (response.status !== CertificationStatus.SUCCESS) {
|
|
10033
10061
|
submitError = new SphereError(`${failLabel}: ${response.status}`, failCode);
|
|
10034
10062
|
submitRejectedCleanly = CLEAN_REJECT_STATUSES.has(response.status);
|
|
@@ -10042,33 +10070,21 @@ var SphereTokenEngine = class {
|
|
|
10042
10070
|
client: this.deps.client,
|
|
10043
10071
|
trustBase: this.deps.trustBase,
|
|
10044
10072
|
predicateVerifier: this.deps.predicateVerifier,
|
|
10045
|
-
timeoutMs: this.proofTimeoutMs,
|
|
10046
10073
|
intervalMs: this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
|
|
10047
10074
|
// #683
|
|
10048
10075
|
},
|
|
10049
10076
|
transaction,
|
|
10050
|
-
|
|
10077
|
+
signal
|
|
10051
10078
|
);
|
|
10052
10079
|
} catch (err) {
|
|
10053
|
-
|
|
10054
|
-
throw new TransferConflictError(
|
|
10055
|
-
`${failLabel}: the source state was already consumed by a different transaction (lost race \u2014 abort this intent and re-plan under a new transferId)`,
|
|
10056
|
-
err
|
|
10057
|
-
);
|
|
10058
|
-
}
|
|
10059
|
-
if (submitRejectedCleanly) throw submitError;
|
|
10060
|
-
throw new ProofUnconfirmedError(
|
|
10061
|
-
`${failLabel}: certification unconfirmed \u2014 the source spend may be on-chain; keep the intent open and resume under the same transferId`,
|
|
10062
|
-
submitError ?? err
|
|
10063
|
-
);
|
|
10080
|
+
throw certificationFailure(err, failLabel, submitError, submitRejectedCleanly);
|
|
10064
10081
|
}
|
|
10065
10082
|
}
|
|
10066
10083
|
/**
|
|
10067
|
-
* One inclusion-proof wait, with the deadline OWNED here (#739)
|
|
10068
|
-
*
|
|
10069
|
-
*
|
|
10070
|
-
*
|
|
10071
|
-
* (keep-open), which is the correct posture: the spend may be on-chain.
|
|
10084
|
+
* One inclusion-proof wait, with the deadline OWNED here (#739) so it is bounded
|
|
10085
|
+
* and configurable rather than the SDK's fixed default. A hit throws, and the
|
|
10086
|
+
* caller maps it to ProofUnconfirmedError (keep-open) — the correct posture,
|
|
10087
|
+
* since the spend may be on-chain.
|
|
10072
10088
|
*/
|
|
10073
10089
|
/** Fail fast if this engine's key does not own the token's current state. */
|
|
10074
10090
|
assertOwned(token) {
|
|
@@ -11403,7 +11419,7 @@ function createMachineStores(kv) {
|
|
|
11403
11419
|
}
|
|
11404
11420
|
|
|
11405
11421
|
// modules/payments-v2/machine/TransferMachine.ts
|
|
11406
|
-
var CERTIFY_WIDTH =
|
|
11422
|
+
var CERTIFY_WIDTH = 20;
|
|
11407
11423
|
var KEEP_OPEN_CODES = /* @__PURE__ */ new Set([
|
|
11408
11424
|
"CERTIFICATION_UNCONFIRMED",
|
|
11409
11425
|
"CHECKPOINT_PERSIST_FAILED",
|
|
@@ -11499,12 +11515,15 @@ var TransferMachine = class {
|
|
|
11499
11515
|
payload: r.payload,
|
|
11500
11516
|
...r.payload.memo !== void 0 ? { memo: r.payload.memo } : {}
|
|
11501
11517
|
};
|
|
11502
|
-
const
|
|
11518
|
+
const outcomes = await this.resumeOps(ctx, engine, r);
|
|
11519
|
+
const { committed, cls, firstError } = summarize(outcomes);
|
|
11520
|
+
const conflicts = this.toConflicts(engine, r, outcomes);
|
|
11503
11521
|
if (committed.length === 0 && conflicts.length > 0) throw conflicts[0].error;
|
|
11504
11522
|
let deliveryPending = false;
|
|
11505
11523
|
if (committed.length > 0) {
|
|
11506
11524
|
deliveryPending = await this.deliverSet(r.transferId, r.payload.recipient, r.payload.memo, committed);
|
|
11507
11525
|
}
|
|
11526
|
+
if (cls !== null && cls !== "conflict") throw firstError;
|
|
11508
11527
|
await this.applyCommitted(engine, r.transferId, committed);
|
|
11509
11528
|
if (conflicts.length > 0) {
|
|
11510
11529
|
return { deliveryPending, partial: await this.settlePartial(engine, r, committed, conflicts) };
|
|
@@ -11589,25 +11608,28 @@ var TransferMachine = class {
|
|
|
11589
11608
|
);
|
|
11590
11609
|
return { recipientBlob: outputs[0].blob.token, changeBlob: outputs[1].blob.token };
|
|
11591
11610
|
}
|
|
11611
|
+
/** Raw outcomes; `summarize` alone decides what they mean — same as the send path. */
|
|
11592
11612
|
async resumeOps(ctx, engine, r) {
|
|
11593
|
-
const
|
|
11594
|
-
const conflicts = [];
|
|
11613
|
+
const outcomes = [];
|
|
11595
11614
|
for (const op of buildOps(r.payload)) {
|
|
11596
11615
|
const journaled = op.kind === "direct" ? r.journaled.get(op.opIndex) : void 0;
|
|
11597
11616
|
if (journaled !== void 0) {
|
|
11598
|
-
|
|
11599
|
-
continue;
|
|
11600
|
-
}
|
|
11601
|
-
const source = r.sources.get(op.sourceTokenId);
|
|
11602
|
-
const outcome = await this.certifyOne(ctx, engine, op, source);
|
|
11603
|
-
if (outcome.certified) {
|
|
11604
|
-
committed.push(outcome);
|
|
11617
|
+
outcomes.push({ op, certified: true, recipientBlob: hexToBytes4(journaled.blobHex) });
|
|
11605
11618
|
continue;
|
|
11606
11619
|
}
|
|
11607
|
-
|
|
11608
|
-
|
|
11620
|
+
outcomes.push(await this.certifyOne(ctx, engine, op, r.sources.get(op.sourceTokenId)));
|
|
11621
|
+
const last = outcomes[outcomes.length - 1];
|
|
11622
|
+
if (last.error !== void 0 && classifyError(last.error) !== "conflict") break;
|
|
11609
11623
|
}
|
|
11610
|
-
return
|
|
11624
|
+
return outcomes;
|
|
11625
|
+
}
|
|
11626
|
+
/** Conflicted ops with the amount each leaves undelivered (#690 shortfall math). */
|
|
11627
|
+
toConflicts(engine, r, outcomes) {
|
|
11628
|
+
return outcomes.filter((o) => !o.certified && classifyError(o.error) === "conflict").map((o) => ({
|
|
11629
|
+
op: o.op,
|
|
11630
|
+
error: o.error,
|
|
11631
|
+
amount: conflictAmount(engine, r.payload, o.op, r.sources.get(o.op.sourceTokenId))
|
|
11632
|
+
}));
|
|
11611
11633
|
}
|
|
11612
11634
|
async deliverSet(transferId, recipientPubkey, memo, committed) {
|
|
11613
11635
|
const journaled = committed.filter((o) => o.error === void 0 && o.recipientBlob !== void 0);
|