@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
@@ -9304,6 +9304,10 @@ var import_StateMask = require("@unicitylabs/state-transition-sdk/lib/transactio
9304
9304
  var import_MintJustificationVerifierService = require("@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js");
9305
9305
  var import_TokenIssuanceVerifierService = require("@unicitylabs/state-transition-sdk/lib/transaction/verification/TokenIssuanceVerifierService.js");
9306
9306
  var import_VerificationContext = require("@unicitylabs/state-transition-sdk/lib/transaction/verification/VerificationContext.js");
9307
+ var import_UnicityCertificateVerifier = require("@unicitylabs/state-transition-sdk/lib/api/bft/verification/UnicityCertificateVerifier.js");
9308
+ var import_UnicitySealQuorumSignaturesVerificationRule = require("@unicitylabs/state-transition-sdk/lib/api/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.js");
9309
+ var import_VerifiedSealCache = require("@unicitylabs/state-transition-sdk/lib/api/bft/verification/VerifiedSealCache.js");
9310
+ var import_Secp256k1SignatureVerifier = require("@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Secp256k1SignatureVerifier.js");
9307
9311
  var import_WorkerTokenVerifier = require("@unicitylabs/state-transition-sdk/lib/transaction/verification/worker/WorkerTokenVerifier.js");
9308
9312
  var import_EncodedPredicate = require("@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js");
9309
9313
  var import_SignaturePredicate = require("@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js");
@@ -9523,6 +9527,7 @@ async function awaitProofBounded(deps, transaction, signal) {
9523
9527
  deps.client,
9524
9528
  deps.trustBase,
9525
9529
  deps.predicateVerifier,
9530
+ deps.unicityCertificateVerifier,
9526
9531
  transaction,
9527
9532
  signal,
9528
9533
  deps.intervalMs
@@ -9729,7 +9734,7 @@ async function burntTokenFromCheckpoint(deps, storedBytes, reDerivedBurnTx, sour
9729
9734
  }
9730
9735
  let burnCertified;
9731
9736
  try {
9732
- burnCertified = await reDerivedBurnTx.toCertifiedTransaction(deps.trustBase, deps.predicateVerifier, burnProof);
9737
+ burnCertified = await reDerivedBurnTx.toCertifiedTransaction(deps.trustBase, deps.predicateVerifier, deps.unicityCertificateVerifier, burnProof);
9733
9738
  } catch (err) {
9734
9739
  if (isTrustbaseFailure(err)) {
9735
9740
  throw new CheckpointTrustbaseMismatchError(
@@ -9837,7 +9842,7 @@ var SphereTokenEngine = class {
9837
9842
  "AGGREGATOR_ERROR",
9838
9843
  options
9839
9844
  );
9840
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9845
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9841
9846
  const token = await import_Token.Token.mint(certified, this.deps.verificationContext);
9842
9847
  return this.wrapToken(token);
9843
9848
  }
@@ -9854,7 +9859,7 @@ var SphereTokenEngine = class {
9854
9859
  "AGGREGATOR_ERROR",
9855
9860
  options
9856
9861
  );
9857
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9862
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9858
9863
  const token = await import_Token.Token.mint(certified, this.deps.verificationContext);
9859
9864
  return this.wrapToken(token);
9860
9865
  }
@@ -9875,7 +9880,7 @@ var SphereTokenEngine = class {
9875
9880
  "TRANSFER_FAILED",
9876
9881
  options
9877
9882
  );
9878
- const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9883
+ const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9879
9884
  const transferred = await params.token.sdkToken.transfer(certified, this.deps.verificationContext);
9880
9885
  return this.wrapToken(transferred);
9881
9886
  }
@@ -9962,7 +9967,7 @@ var SphereTokenEngine = class {
9962
9967
  const burnUnlock = await import_SignaturePredicateUnlockScript.SignaturePredicateUnlockScript.create(burnTx, this.deps.signingService);
9963
9968
  const burnCert = await import_CertificationData.CertificationData.fromTransaction(burnTx, burnUnlock);
9964
9969
  const burnProof = await this.submitAndAwaitProof(burnCert, burnTx, "Split burn failed", "TRANSFER_FAILED", ctx.options);
9965
- const burnCertified = await burnTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, burnProof);
9970
+ const burnCertified = await burnTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, burnProof);
9966
9971
  const burntToken = await ctx.params.token.sdkToken.transfer(burnCertified, this.deps.verificationContext);
9967
9972
  return { burntToken, burnProof };
9968
9973
  }
@@ -10001,7 +10006,7 @@ var SphereTokenEngine = class {
10001
10006
  );
10002
10007
  const certData = await import_CertificationData.CertificationData.fromMintTransaction(mintTx);
10003
10008
  const proof = await this.submitSplitMintLeg(certData, mintTx, options);
10004
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
10009
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
10005
10010
  const token = await import_Token.Token.mint(certified, this.deps.verificationContext);
10006
10011
  return this.wrapToken(token);
10007
10012
  }
@@ -10144,6 +10149,7 @@ var SphereTokenEngine = class {
10144
10149
  client: this.deps.client,
10145
10150
  trustBase: this.deps.trustBase,
10146
10151
  predicateVerifier: this.deps.predicateVerifier,
10152
+ unicityCertificateVerifier: this.deps.unicityCertificateVerifier,
10147
10153
  intervalMs: this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
10148
10154
  // #683
10149
10155
  },
@@ -10235,6 +10241,9 @@ async function createSphereTokenEngine(config) {
10235
10241
  }
10236
10242
  const trustBase = import_RootTrustBase.RootTrustBase.fromJSON(config.trustBaseJson);
10237
10243
  const predicateVerifier = import_PredicateVerifierService.PredicateVerifierService.create();
10244
+ const unicityCertificateVerifier = new import_UnicityCertificateVerifier.UnicityCertificateVerifier(
10245
+ new import_UnicitySealQuorumSignaturesVerificationRule.UnicitySealQuorumSignaturesVerificationRule(new import_Secp256k1SignatureVerifier.Secp256k1SignatureVerifier(), new import_VerifiedSealCache.VerifiedSealCache(256))
10246
+ );
10238
10247
  const mintJustificationVerifier = new import_MintJustificationVerifierService.MintJustificationVerifierService();
10239
10248
  mintJustificationVerifier.register(
10240
10249
  new import_SplitMintJustificationVerifier.SplitMintJustificationVerifier(decodeSpherePaymentData)
@@ -10243,10 +10252,12 @@ async function createSphereTokenEngine(config) {
10243
10252
  client: new import_StateTransitionClient.StateTransitionClient(new import_AggregatorClient.AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
10244
10253
  trustBase,
10245
10254
  predicateVerifier,
10255
+ unicityCertificateVerifier,
10246
10256
  mintJustificationVerifier,
10247
10257
  verificationContext: new import_VerificationContext.VerificationContext(
10248
10258
  trustBase,
10249
10259
  predicateVerifier,
10260
+ unicityCertificateVerifier,
10250
10261
  mintJustificationVerifier,
10251
10262
  new import_TokenIssuanceVerifierService.TokenIssuanceVerifierService(false)
10252
10263
  ),
@@ -12348,6 +12359,90 @@ async function splitOpIndex(deps, envelope) {
12348
12359
  return Array.isArray(payload.direct) ? payload.direct.length : 0;
12349
12360
  }
12350
12361
 
12362
+ // modules/payments-v2/mint-params.ts
12363
+ function mintParams(recipientPubkey, coinId, amount) {
12364
+ return { recipientPubkey, value: { assets: [{ coinId, amount }] } };
12365
+ }
12366
+
12367
+ // modules/payments-v2/prewarm-cache.ts
12368
+ var PrewarmCache = class _PrewarmCache {
12369
+ entries = /* @__PURE__ */ new Map();
12370
+ generation = 0;
12371
+ static key(tokenId, stateHash) {
12372
+ return `${tokenId}:${stateHash}`;
12373
+ }
12374
+ /** Claim the cache for a warm about to start; supersedes any warm already running. */
12375
+ begin() {
12376
+ this.entries.clear();
12377
+ this.generation += 1;
12378
+ return this.generation;
12379
+ }
12380
+ /**
12381
+ * Publish a warm's result, unless it was cancelled or superseded while its
12382
+ * fetch was in flight — a cancel that only cleared what had landed so far
12383
+ * would be undone by the continuation of the very warm it cancelled.
12384
+ */
12385
+ commit(generation, warmed) {
12386
+ if (generation !== this.generation) return;
12387
+ for (const [tokenId, stateHash, bytes] of warmed) {
12388
+ this.entries.set(_PrewarmCache.key(tokenId, stateHash), bytes);
12389
+ }
12390
+ }
12391
+ get(tokenId, stateHash) {
12392
+ if (stateHash === void 0) return void 0;
12393
+ return this.entries.get(_PrewarmCache.key(tokenId, stateHash));
12394
+ }
12395
+ clear() {
12396
+ this.entries.clear();
12397
+ this.generation += 1;
12398
+ }
12399
+ get size() {
12400
+ return this.entries.size;
12401
+ }
12402
+ };
12403
+ async function warmSendSources(deps, request) {
12404
+ const ids = deps.queue.previewSelection(request);
12405
+ if (ids.length === 0) return;
12406
+ const generation = deps.cache.begin();
12407
+ const at = new Map(ids.map((id) => [id, deps.view.stateHashOf(id)]));
12408
+ let blobs;
12409
+ try {
12410
+ blobs = await deps.storagePort.getBlobs([...ids]);
12411
+ } catch {
12412
+ return;
12413
+ }
12414
+ const warmed = [];
12415
+ for (const [tokenId, bytes] of blobs) {
12416
+ const stateHash = at.get(tokenId);
12417
+ if (stateHash !== void 0) warmed.push([tokenId, stateHash, bytes]);
12418
+ }
12419
+ deps.cache.commit(generation, warmed);
12420
+ }
12421
+ async function takeSourceBlobs(deps, sourceIds) {
12422
+ const blobs = /* @__PURE__ */ new Map();
12423
+ const missing = [];
12424
+ let trustCache = deps.cache.size > 0;
12425
+ if (trustCache) {
12426
+ try {
12427
+ await deps.view.delta();
12428
+ } catch {
12429
+ trustCache = false;
12430
+ }
12431
+ }
12432
+ for (const tokenId of sourceIds) {
12433
+ const warm = trustCache ? deps.cache.get(tokenId, deps.view.stateHashOf(tokenId)) : void 0;
12434
+ if (warm === void 0) missing.push(tokenId);
12435
+ else blobs.set(tokenId, warm);
12436
+ }
12437
+ deps.cache.clear();
12438
+ if (missing.length > 0) {
12439
+ for (const [tokenId, bytes] of await deps.storagePort.getBlobs(missing)) {
12440
+ blobs.set(tokenId, bytes);
12441
+ }
12442
+ }
12443
+ return blobs;
12444
+ }
12445
+
12351
12446
  // modules/payments-v2/inventory/presentation.ts
12352
12447
  function toToken(tokenId, snapshot, asset, registry, flags) {
12353
12448
  const iconUrl = registry.getIconUrl(asset.coinId);
@@ -12434,6 +12529,13 @@ function priceOne(asset, quote) {
12434
12529
  fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
12435
12530
  };
12436
12531
  }
12532
+ function transferringToken(tokenId, coinId, amount, registry, now) {
12533
+ const snapshot = { assets: [], createdAt: now, updatedAt: now };
12534
+ return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
12535
+ transferring: true,
12536
+ suspectedSpent: false
12537
+ });
12538
+ }
12437
12539
 
12438
12540
  // modules/payments-v2/machine/payload.ts
12439
12541
  function buildPayload(recipientPubkey, request, spend, spentStates) {
@@ -12672,6 +12774,15 @@ var InventoryView = class {
12672
12774
  }
12673
12775
  return out;
12674
12776
  }
12777
+ /**
12778
+ * Current mirrored state of an ACTIVE token, or undefined when it is unknown
12779
+ * or tombstoned. Exists so a cached blob can be state-scoped (F6): a blob is
12780
+ * only reusable while the token still sits at the state it was fetched at.
12781
+ */
12782
+ stateHashOf(tokenId) {
12783
+ const entry = this.mirror.get(tokenId);
12784
+ return entry !== void 0 && entry.status === "active" ? entry.stateHash : void 0;
12785
+ }
12675
12786
  tokens(registry, filter) {
12676
12787
  const out = [];
12677
12788
  for (const [tokenId, entry] of this.mirror) {
@@ -13591,6 +13702,31 @@ var SpendQueue = class {
13591
13702
  }
13592
13703
  return { kind: "queued", settled: this.enqueue(reservationId, request.coinId, amount) };
13593
13704
  }
13705
+ /**
13706
+ * The sources `plan()` WOULD pick right now, reserving nothing and queueing
13707
+ * nothing — a read-only probe used to warm blobs while the user is still
13708
+ * looking at the confirm screen.
13709
+ *
13710
+ * Deliberately side-effect free: reserving here would pin the user's balance
13711
+ * across think-time and strand it if they never press send. The consequence is
13712
+ * that the real `plan()` may pick differently (inventory moves, another send
13713
+ * reserves first); callers must treat the result as a HINT and tolerate a miss,
13714
+ * never as a commitment that these sources will be spent.
13715
+ */
13716
+ previewSelection(request) {
13717
+ if (this.destroyed) return [];
13718
+ let amount;
13719
+ try {
13720
+ amount = parseAmount(request.amount);
13721
+ } catch {
13722
+ return [];
13723
+ }
13724
+ const { freeView, freeTotal } = this.freeView(request.coinId);
13725
+ if (freeTotal < amount) return [];
13726
+ const plan = selectCoins(freeView, amount, { workBudget: this.deps.workBudget });
13727
+ if (plan === null) return [];
13728
+ return [...plan.direct, ...plan.split !== void 0 ? [plan.split.tokenId] : []];
13729
+ }
13594
13730
  // Coverage math: free + Σ declared expected change of in-flight sends —
13595
13731
  // never the full amount of in-flight sources (§5.3).
13596
13732
  declareExpectedChange(transferId, coinId, amount) {
@@ -13940,6 +14076,7 @@ var PaymentsFacade = class {
13940
14076
  now: () => this.nowMs()
13941
14077
  });
13942
14078
  }
14079
+ prewarmed = new PrewarmCache();
13943
14080
  view;
13944
14081
  ledger;
13945
14082
  queue;
@@ -14033,6 +14170,15 @@ var PaymentsFacade = class {
14033
14170
  return derivePendingTransfers(this.machineStores, this.machineDeps.decryptPayload);
14034
14171
  }
14035
14172
  // ── money movement ─────────────────────────────────────────────────────────
14173
+ async prewarmSend(request) {
14174
+ await warmSendSources(this.warmDeps(), request);
14175
+ }
14176
+ discardPrewarm() {
14177
+ this.prewarmed.clear();
14178
+ }
14179
+ warmDeps() {
14180
+ return { queue: this.queue, view: this.view, storagePort: this.deps.storagePort, cache: this.prewarmed };
14181
+ }
14036
14182
  send(request) {
14037
14183
  return this.track(this.sendOutcome(request));
14038
14184
  }
@@ -14242,7 +14388,7 @@ var PaymentsFacade = class {
14242
14388
  }
14243
14389
  async materialize(transferId, recipientPubkey, request, spend, sourceIds) {
14244
14390
  const engine = this.engine();
14245
- const blobs = await this.deps.storagePort.getBlobs([...sourceIds]);
14391
+ const blobs = await takeSourceBlobs(this.warmDeps(), sourceIds);
14246
14392
  const sources = /* @__PURE__ */ new Map();
14247
14393
  const sourceTokens = [];
14248
14394
  const spentStates = {};
@@ -14255,7 +14401,7 @@ var PaymentsFacade = class {
14255
14401
  const keys = await engine.deliveryKeys(bytes);
14256
14402
  spentStates[tokenId] = { local: keys.stateHash, protocol: keys.stateHash };
14257
14403
  sources.set(tokenId, token);
14258
- sourceTokens.push(this.toUiToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId)));
14404
+ sourceTokens.push(transferringToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId), this.deps.registry, this.nowMs()));
14259
14405
  }
14260
14406
  const payload = buildPayload(recipientPubkey, request, spend, spentStates);
14261
14407
  const plan = {
@@ -14399,7 +14545,7 @@ var PaymentsFacade = class {
14399
14545
  await this.machineStores.mintJournal.upsert(entry);
14400
14546
  let token;
14401
14547
  try {
14402
- token = await engine.mint(this.mintParams(coinId, amount), { transferId: mintId });
14548
+ token = await engine.mint(mintParams(this.ownPubkeyBytes, coinId, amount), { transferId: mintId });
14403
14549
  } catch (err) {
14404
14550
  this.heartbeat.arm();
14405
14551
  return { success: false, error: messageOf(err) };
@@ -14458,7 +14604,7 @@ var PaymentsFacade = class {
14458
14604
  });
14459
14605
  return false;
14460
14606
  }
14461
- const token = await engine.mint(this.mintParams(entry.coinId, BigInt(entry.amount)), {
14607
+ const token = await engine.mint(mintParams(this.ownPubkeyBytes, entry.coinId, BigInt(entry.amount)), {
14462
14608
  transferId: entry.mintId
14463
14609
  });
14464
14610
  if (token.blob.tokenId !== entry.tokenId) {
@@ -14467,12 +14613,6 @@ var PaymentsFacade = class {
14467
14613
  await this.finalizeMint(engine, entry.mintId, token, entry.coinId, entry.amount);
14468
14614
  return true;
14469
14615
  }
14470
- mintParams(coinId, amount) {
14471
- return {
14472
- recipientPubkey: this.ownPubkeyBytes,
14473
- value: { assets: [{ coinId, amount }] }
14474
- };
14475
- }
14476
14616
  async tokenInServerInventory(tokenId) {
14477
14617
  let page = await this.deps.storagePort.listInventory();
14478
14618
  for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
@@ -14501,12 +14641,6 @@ var PaymentsFacade = class {
14501
14641
  page = await this.deps.storagePort.listInventory(page.cursor);
14502
14642
  }
14503
14643
  }
14504
- toUiToken(tokenId, coinId, amount) {
14505
- const now = this.nowMs();
14506
- const snapshot = { assets: [], createdAt: now, updatedAt: now };
14507
- const flags = { transferring: true, suspectedSpent: false };
14508
- return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, this.deps.registry, flags);
14509
- }
14510
14644
  track(op) {
14511
14645
  const settled = op.then(() => void 0, () => void 0);
14512
14646
  this.pendingOps.add(settled);