@unicitylabs/sphere-sdk 0.14.9 → 0.14.11-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.
Files changed (35) hide show
  1. package/dist/connect/index.cjs +1 -1
  2. package/dist/connect/index.cjs.map +1 -1
  3. package/dist/connect/index.js +1 -1
  4. package/dist/connect/index.js.map +1 -1
  5. package/dist/core/index.cjs +156 -22
  6. package/dist/core/index.cjs.map +1 -1
  7. package/dist/core/index.d.cts +2 -0
  8. package/dist/core/index.d.ts +2 -0
  9. package/dist/core/index.js +156 -22
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/impl/browser/connect/index.cjs +1 -1
  12. package/dist/impl/browser/connect/index.cjs.map +1 -1
  13. package/dist/impl/browser/connect/index.js +1 -1
  14. package/dist/impl/browser/connect/index.js.map +1 -1
  15. package/dist/impl/wallet-api-v2/index.cjs +4 -0
  16. package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
  17. package/dist/impl/wallet-api-v2/index.js +4 -0
  18. package/dist/impl/wallet-api-v2/index.js.map +1 -1
  19. package/dist/index.cjs +156 -22
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +2 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.js +156 -22
  24. package/dist/index.js.map +1 -1
  25. package/dist/modules/payments-v2/index.cjs +143 -16
  26. package/dist/modules/payments-v2/index.cjs.map +1 -1
  27. package/dist/modules/payments-v2/index.d.cts +6 -2
  28. package/dist/modules/payments-v2/index.d.ts +6 -2
  29. package/dist/modules/payments-v2/index.js +143 -16
  30. package/dist/modules/payments-v2/index.js.map +1 -1
  31. package/dist/token-engine/index.cjs +17 -6
  32. package/dist/token-engine/index.cjs.map +1 -1
  33. package/dist/token-engine/index.js +17 -6
  34. package/dist/token-engine/index.js.map +1 -1
  35. package/package.json +2 -2
package/dist/index.d.cts CHANGED
@@ -166,6 +166,8 @@ interface PendingTransfer {
166
166
  }
167
167
  type ConnectionStatus$1 = 'connected' | 'degraded' | 'offline';
168
168
  interface PaymentsV2 {
169
+ prewarmSend(request: SendRequest): Promise<void>;
170
+ discardPrewarm(): void;
169
171
  assets(coinId?: string): Promise<Asset[]>;
170
172
  tokens(filter?: {
171
173
  coinId?: string;
package/dist/index.d.ts CHANGED
@@ -166,6 +166,8 @@ interface PendingTransfer {
166
166
  }
167
167
  type ConnectionStatus$1 = 'connected' | 'degraded' | 'offline';
168
168
  interface PaymentsV2 {
169
+ prewarmSend(request: SendRequest): Promise<void>;
170
+ discardPrewarm(): void;
169
171
  assets(coinId?: string): Promise<Asset[]>;
170
172
  tokens(filter?: {
171
173
  coinId?: string;
package/dist/index.js CHANGED
@@ -9234,6 +9234,10 @@ import { StateMask } from "@unicitylabs/state-transition-sdk/lib/transaction/Sta
9234
9234
  import { MintJustificationVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js";
9235
9235
  import { TokenIssuanceVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/TokenIssuanceVerifierService.js";
9236
9236
  import { VerificationContext } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/VerificationContext.js";
9237
+ import { UnicityCertificateVerifier } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/UnicityCertificateVerifier.js";
9238
+ import { UnicitySealQuorumSignaturesVerificationRule } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.js";
9239
+ import { VerifiedSealCache } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/VerifiedSealCache.js";
9240
+ import { Secp256k1SignatureVerifier } from "@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Secp256k1SignatureVerifier.js";
9237
9241
  import { WorkerTokenVerifier } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/worker/WorkerTokenVerifier.js";
9238
9242
  import { EncodedPredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js";
9239
9243
  import { SignaturePredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js";
@@ -9453,6 +9457,7 @@ async function awaitProofBounded(deps, transaction, signal) {
9453
9457
  deps.client,
9454
9458
  deps.trustBase,
9455
9459
  deps.predicateVerifier,
9460
+ deps.unicityCertificateVerifier,
9456
9461
  transaction,
9457
9462
  signal,
9458
9463
  deps.intervalMs
@@ -9659,7 +9664,7 @@ async function burntTokenFromCheckpoint(deps, storedBytes, reDerivedBurnTx, sour
9659
9664
  }
9660
9665
  let burnCertified;
9661
9666
  try {
9662
- burnCertified = await reDerivedBurnTx.toCertifiedTransaction(deps.trustBase, deps.predicateVerifier, burnProof);
9667
+ burnCertified = await reDerivedBurnTx.toCertifiedTransaction(deps.trustBase, deps.predicateVerifier, deps.unicityCertificateVerifier, burnProof);
9663
9668
  } catch (err) {
9664
9669
  if (isTrustbaseFailure(err)) {
9665
9670
  throw new CheckpointTrustbaseMismatchError(
@@ -9767,7 +9772,7 @@ var SphereTokenEngine = class {
9767
9772
  "AGGREGATOR_ERROR",
9768
9773
  options
9769
9774
  );
9770
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9775
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9771
9776
  const token = await Token.mint(certified, this.deps.verificationContext);
9772
9777
  return this.wrapToken(token);
9773
9778
  }
@@ -9784,7 +9789,7 @@ var SphereTokenEngine = class {
9784
9789
  "AGGREGATOR_ERROR",
9785
9790
  options
9786
9791
  );
9787
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9792
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9788
9793
  const token = await Token.mint(certified, this.deps.verificationContext);
9789
9794
  return this.wrapToken(token);
9790
9795
  }
@@ -9805,7 +9810,7 @@ var SphereTokenEngine = class {
9805
9810
  "TRANSFER_FAILED",
9806
9811
  options
9807
9812
  );
9808
- const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9813
+ const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9809
9814
  const transferred = await params.token.sdkToken.transfer(certified, this.deps.verificationContext);
9810
9815
  return this.wrapToken(transferred);
9811
9816
  }
@@ -9892,7 +9897,7 @@ var SphereTokenEngine = class {
9892
9897
  const burnUnlock = await SignaturePredicateUnlockScript.create(burnTx, this.deps.signingService);
9893
9898
  const burnCert = await CertificationData.fromTransaction(burnTx, burnUnlock);
9894
9899
  const burnProof = await this.submitAndAwaitProof(burnCert, burnTx, "Split burn failed", "TRANSFER_FAILED", ctx.options);
9895
- const burnCertified = await burnTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, burnProof);
9900
+ const burnCertified = await burnTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, burnProof);
9896
9901
  const burntToken = await ctx.params.token.sdkToken.transfer(burnCertified, this.deps.verificationContext);
9897
9902
  return { burntToken, burnProof };
9898
9903
  }
@@ -9931,7 +9936,7 @@ var SphereTokenEngine = class {
9931
9936
  );
9932
9937
  const certData = await CertificationData.fromMintTransaction(mintTx);
9933
9938
  const proof = await this.submitSplitMintLeg(certData, mintTx, options);
9934
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9939
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9935
9940
  const token = await Token.mint(certified, this.deps.verificationContext);
9936
9941
  return this.wrapToken(token);
9937
9942
  }
@@ -10074,6 +10079,7 @@ var SphereTokenEngine = class {
10074
10079
  client: this.deps.client,
10075
10080
  trustBase: this.deps.trustBase,
10076
10081
  predicateVerifier: this.deps.predicateVerifier,
10082
+ unicityCertificateVerifier: this.deps.unicityCertificateVerifier,
10077
10083
  intervalMs: this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
10078
10084
  // #683
10079
10085
  },
@@ -10165,6 +10171,9 @@ async function createSphereTokenEngine(config) {
10165
10171
  }
10166
10172
  const trustBase = RootTrustBase.fromJSON(config.trustBaseJson);
10167
10173
  const predicateVerifier = PredicateVerifierService.create();
10174
+ const unicityCertificateVerifier = new UnicityCertificateVerifier(
10175
+ new UnicitySealQuorumSignaturesVerificationRule(new Secp256k1SignatureVerifier(), new VerifiedSealCache(256))
10176
+ );
10168
10177
  const mintJustificationVerifier = new MintJustificationVerifierService();
10169
10178
  mintJustificationVerifier.register(
10170
10179
  new SplitMintJustificationVerifier(decodeSpherePaymentData)
@@ -10173,10 +10182,12 @@ async function createSphereTokenEngine(config) {
10173
10182
  client: new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
10174
10183
  trustBase,
10175
10184
  predicateVerifier,
10185
+ unicityCertificateVerifier,
10176
10186
  mintJustificationVerifier,
10177
10187
  verificationContext: new VerificationContext(
10178
10188
  trustBase,
10179
10189
  predicateVerifier,
10190
+ unicityCertificateVerifier,
10180
10191
  mintJustificationVerifier,
10181
10192
  new TokenIssuanceVerifierService(false)
10182
10193
  ),
@@ -12278,6 +12289,90 @@ async function splitOpIndex(deps, envelope) {
12278
12289
  return Array.isArray(payload.direct) ? payload.direct.length : 0;
12279
12290
  }
12280
12291
 
12292
+ // modules/payments-v2/mint-params.ts
12293
+ function mintParams(recipientPubkey, coinId, amount) {
12294
+ return { recipientPubkey, value: { assets: [{ coinId, amount }] } };
12295
+ }
12296
+
12297
+ // modules/payments-v2/prewarm-cache.ts
12298
+ var PrewarmCache = class _PrewarmCache {
12299
+ entries = /* @__PURE__ */ new Map();
12300
+ generation = 0;
12301
+ static key(tokenId, stateHash) {
12302
+ return `${tokenId}:${stateHash}`;
12303
+ }
12304
+ /** Claim the cache for a warm about to start; supersedes any warm already running. */
12305
+ begin() {
12306
+ this.entries.clear();
12307
+ this.generation += 1;
12308
+ return this.generation;
12309
+ }
12310
+ /**
12311
+ * Publish a warm's result, unless it was cancelled or superseded while its
12312
+ * fetch was in flight — a cancel that only cleared what had landed so far
12313
+ * would be undone by the continuation of the very warm it cancelled.
12314
+ */
12315
+ commit(generation, warmed) {
12316
+ if (generation !== this.generation) return;
12317
+ for (const [tokenId, stateHash, bytes] of warmed) {
12318
+ this.entries.set(_PrewarmCache.key(tokenId, stateHash), bytes);
12319
+ }
12320
+ }
12321
+ get(tokenId, stateHash) {
12322
+ if (stateHash === void 0) return void 0;
12323
+ return this.entries.get(_PrewarmCache.key(tokenId, stateHash));
12324
+ }
12325
+ clear() {
12326
+ this.entries.clear();
12327
+ this.generation += 1;
12328
+ }
12329
+ get size() {
12330
+ return this.entries.size;
12331
+ }
12332
+ };
12333
+ async function warmSendSources(deps, request) {
12334
+ const ids = deps.queue.previewSelection(request);
12335
+ if (ids.length === 0) return;
12336
+ const generation = deps.cache.begin();
12337
+ const at = new Map(ids.map((id) => [id, deps.view.stateHashOf(id)]));
12338
+ let blobs;
12339
+ try {
12340
+ blobs = await deps.storagePort.getBlobs([...ids]);
12341
+ } catch {
12342
+ return;
12343
+ }
12344
+ const warmed = [];
12345
+ for (const [tokenId, bytes] of blobs) {
12346
+ const stateHash = at.get(tokenId);
12347
+ if (stateHash !== void 0) warmed.push([tokenId, stateHash, bytes]);
12348
+ }
12349
+ deps.cache.commit(generation, warmed);
12350
+ }
12351
+ async function takeSourceBlobs(deps, sourceIds) {
12352
+ const blobs = /* @__PURE__ */ new Map();
12353
+ const missing = [];
12354
+ let trustCache = deps.cache.size > 0;
12355
+ if (trustCache) {
12356
+ try {
12357
+ await deps.view.delta();
12358
+ } catch {
12359
+ trustCache = false;
12360
+ }
12361
+ }
12362
+ for (const tokenId of sourceIds) {
12363
+ const warm = trustCache ? deps.cache.get(tokenId, deps.view.stateHashOf(tokenId)) : void 0;
12364
+ if (warm === void 0) missing.push(tokenId);
12365
+ else blobs.set(tokenId, warm);
12366
+ }
12367
+ deps.cache.clear();
12368
+ if (missing.length > 0) {
12369
+ for (const [tokenId, bytes] of await deps.storagePort.getBlobs(missing)) {
12370
+ blobs.set(tokenId, bytes);
12371
+ }
12372
+ }
12373
+ return blobs;
12374
+ }
12375
+
12281
12376
  // modules/payments-v2/inventory/presentation.ts
12282
12377
  function toToken(tokenId, snapshot, asset, registry, flags) {
12283
12378
  const iconUrl = registry.getIconUrl(asset.coinId);
@@ -12364,6 +12459,13 @@ function priceOne(asset, quote) {
12364
12459
  fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
12365
12460
  };
12366
12461
  }
12462
+ function transferringToken(tokenId, coinId, amount, registry, now) {
12463
+ const snapshot = { assets: [], createdAt: now, updatedAt: now };
12464
+ return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
12465
+ transferring: true,
12466
+ suspectedSpent: false
12467
+ });
12468
+ }
12367
12469
 
12368
12470
  // modules/payments-v2/machine/payload.ts
12369
12471
  function buildPayload(recipientPubkey, request, spend, spentStates) {
@@ -12602,6 +12704,15 @@ var InventoryView = class {
12602
12704
  }
12603
12705
  return out;
12604
12706
  }
12707
+ /**
12708
+ * Current mirrored state of an ACTIVE token, or undefined when it is unknown
12709
+ * or tombstoned. Exists so a cached blob can be state-scoped (F6): a blob is
12710
+ * only reusable while the token still sits at the state it was fetched at.
12711
+ */
12712
+ stateHashOf(tokenId) {
12713
+ const entry = this.mirror.get(tokenId);
12714
+ return entry !== void 0 && entry.status === "active" ? entry.stateHash : void 0;
12715
+ }
12605
12716
  tokens(registry, filter) {
12606
12717
  const out = [];
12607
12718
  for (const [tokenId, entry] of this.mirror) {
@@ -13521,6 +13632,31 @@ var SpendQueue = class {
13521
13632
  }
13522
13633
  return { kind: "queued", settled: this.enqueue(reservationId, request.coinId, amount) };
13523
13634
  }
13635
+ /**
13636
+ * The sources `plan()` WOULD pick right now, reserving nothing and queueing
13637
+ * nothing — a read-only probe used to warm blobs while the user is still
13638
+ * looking at the confirm screen.
13639
+ *
13640
+ * Deliberately side-effect free: reserving here would pin the user's balance
13641
+ * across think-time and strand it if they never press send. The consequence is
13642
+ * that the real `plan()` may pick differently (inventory moves, another send
13643
+ * reserves first); callers must treat the result as a HINT and tolerate a miss,
13644
+ * never as a commitment that these sources will be spent.
13645
+ */
13646
+ previewSelection(request) {
13647
+ if (this.destroyed) return [];
13648
+ let amount;
13649
+ try {
13650
+ amount = parseAmount(request.amount);
13651
+ } catch {
13652
+ return [];
13653
+ }
13654
+ const { freeView, freeTotal } = this.freeView(request.coinId);
13655
+ if (freeTotal < amount) return [];
13656
+ const plan = selectCoins(freeView, amount, { workBudget: this.deps.workBudget });
13657
+ if (plan === null) return [];
13658
+ return [...plan.direct, ...plan.split !== void 0 ? [plan.split.tokenId] : []];
13659
+ }
13524
13660
  // Coverage math: free + Σ declared expected change of in-flight sends —
13525
13661
  // never the full amount of in-flight sources (§5.3).
13526
13662
  declareExpectedChange(transferId, coinId, amount) {
@@ -13870,6 +14006,7 @@ var PaymentsFacade = class {
13870
14006
  now: () => this.nowMs()
13871
14007
  });
13872
14008
  }
14009
+ prewarmed = new PrewarmCache();
13873
14010
  view;
13874
14011
  ledger;
13875
14012
  queue;
@@ -13963,6 +14100,15 @@ var PaymentsFacade = class {
13963
14100
  return derivePendingTransfers(this.machineStores, this.machineDeps.decryptPayload);
13964
14101
  }
13965
14102
  // ── money movement ─────────────────────────────────────────────────────────
14103
+ async prewarmSend(request) {
14104
+ await warmSendSources(this.warmDeps(), request);
14105
+ }
14106
+ discardPrewarm() {
14107
+ this.prewarmed.clear();
14108
+ }
14109
+ warmDeps() {
14110
+ return { queue: this.queue, view: this.view, storagePort: this.deps.storagePort, cache: this.prewarmed };
14111
+ }
13966
14112
  send(request) {
13967
14113
  return this.track(this.sendOutcome(request));
13968
14114
  }
@@ -14172,7 +14318,7 @@ var PaymentsFacade = class {
14172
14318
  }
14173
14319
  async materialize(transferId, recipientPubkey, request, spend, sourceIds) {
14174
14320
  const engine = this.engine();
14175
- const blobs = await this.deps.storagePort.getBlobs([...sourceIds]);
14321
+ const blobs = await takeSourceBlobs(this.warmDeps(), sourceIds);
14176
14322
  const sources = /* @__PURE__ */ new Map();
14177
14323
  const sourceTokens = [];
14178
14324
  const spentStates = {};
@@ -14185,7 +14331,7 @@ var PaymentsFacade = class {
14185
14331
  const keys = await engine.deliveryKeys(bytes);
14186
14332
  spentStates[tokenId] = { local: keys.stateHash, protocol: keys.stateHash };
14187
14333
  sources.set(tokenId, token);
14188
- sourceTokens.push(this.toUiToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId)));
14334
+ sourceTokens.push(transferringToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId), this.deps.registry, this.nowMs()));
14189
14335
  }
14190
14336
  const payload = buildPayload(recipientPubkey, request, spend, spentStates);
14191
14337
  const plan = {
@@ -14329,7 +14475,7 @@ var PaymentsFacade = class {
14329
14475
  await this.machineStores.mintJournal.upsert(entry);
14330
14476
  let token;
14331
14477
  try {
14332
- token = await engine.mint(this.mintParams(coinId, amount), { transferId: mintId });
14478
+ token = await engine.mint(mintParams(this.ownPubkeyBytes, coinId, amount), { transferId: mintId });
14333
14479
  } catch (err) {
14334
14480
  this.heartbeat.arm();
14335
14481
  return { success: false, error: messageOf(err) };
@@ -14388,7 +14534,7 @@ var PaymentsFacade = class {
14388
14534
  });
14389
14535
  return false;
14390
14536
  }
14391
- const token = await engine.mint(this.mintParams(entry.coinId, BigInt(entry.amount)), {
14537
+ const token = await engine.mint(mintParams(this.ownPubkeyBytes, entry.coinId, BigInt(entry.amount)), {
14392
14538
  transferId: entry.mintId
14393
14539
  });
14394
14540
  if (token.blob.tokenId !== entry.tokenId) {
@@ -14397,12 +14543,6 @@ var PaymentsFacade = class {
14397
14543
  await this.finalizeMint(engine, entry.mintId, token, entry.coinId, entry.amount);
14398
14544
  return true;
14399
14545
  }
14400
- mintParams(coinId, amount) {
14401
- return {
14402
- recipientPubkey: this.ownPubkeyBytes,
14403
- value: { assets: [{ coinId, amount }] }
14404
- };
14405
- }
14406
14546
  async tokenInServerInventory(tokenId) {
14407
14547
  let page = await this.deps.storagePort.listInventory();
14408
14548
  for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
@@ -14431,12 +14571,6 @@ var PaymentsFacade = class {
14431
14571
  page = await this.deps.storagePort.listInventory(page.cursor);
14432
14572
  }
14433
14573
  }
14434
- toUiToken(tokenId, coinId, amount) {
14435
- const now = this.nowMs();
14436
- const snapshot = { assets: [], createdAt: now, updatedAt: now };
14437
- const flags = { transferring: true, suspectedSpent: false };
14438
- return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, this.deps.registry, flags);
14439
- }
14440
14574
  track(op) {
14441
14575
  const settled = op.then(() => void 0, () => void 0);
14442
14576
  this.pendingOps.add(settled);