@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
@@ -167,6 +167,8 @@ interface PendingTransfer {
167
167
  }
168
168
  type ConnectionStatus$1 = 'connected' | 'degraded' | 'offline';
169
169
  interface PaymentsV2 {
170
+ prewarmSend(request: SendRequest): Promise<void>;
171
+ discardPrewarm(): void;
170
172
  assets(coinId?: string): Promise<Asset[]>;
171
173
  tokens(filter?: {
172
174
  coinId?: string;
@@ -167,6 +167,8 @@ interface PendingTransfer {
167
167
  }
168
168
  type ConnectionStatus$1 = 'connected' | 'degraded' | 'offline';
169
169
  interface PaymentsV2 {
170
+ prewarmSend(request: SendRequest): Promise<void>;
171
+ discardPrewarm(): void;
170
172
  assets(coinId?: string): Promise<Asset[]>;
171
173
  tokens(filter?: {
172
174
  coinId?: string;
@@ -9218,6 +9218,10 @@ import { StateMask } from "@unicitylabs/state-transition-sdk/lib/transaction/Sta
9218
9218
  import { MintJustificationVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js";
9219
9219
  import { TokenIssuanceVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/TokenIssuanceVerifierService.js";
9220
9220
  import { VerificationContext } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/VerificationContext.js";
9221
+ import { UnicityCertificateVerifier } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/UnicityCertificateVerifier.js";
9222
+ import { UnicitySealQuorumSignaturesVerificationRule } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.js";
9223
+ import { VerifiedSealCache } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/VerifiedSealCache.js";
9224
+ import { Secp256k1SignatureVerifier } from "@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Secp256k1SignatureVerifier.js";
9221
9225
  import { WorkerTokenVerifier } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/worker/WorkerTokenVerifier.js";
9222
9226
  import { EncodedPredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js";
9223
9227
  import { SignaturePredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js";
@@ -9437,6 +9441,7 @@ async function awaitProofBounded(deps, transaction, signal) {
9437
9441
  deps.client,
9438
9442
  deps.trustBase,
9439
9443
  deps.predicateVerifier,
9444
+ deps.unicityCertificateVerifier,
9440
9445
  transaction,
9441
9446
  signal,
9442
9447
  deps.intervalMs
@@ -9643,7 +9648,7 @@ async function burntTokenFromCheckpoint(deps, storedBytes, reDerivedBurnTx, sour
9643
9648
  }
9644
9649
  let burnCertified;
9645
9650
  try {
9646
- burnCertified = await reDerivedBurnTx.toCertifiedTransaction(deps.trustBase, deps.predicateVerifier, burnProof);
9651
+ burnCertified = await reDerivedBurnTx.toCertifiedTransaction(deps.trustBase, deps.predicateVerifier, deps.unicityCertificateVerifier, burnProof);
9647
9652
  } catch (err) {
9648
9653
  if (isTrustbaseFailure(err)) {
9649
9654
  throw new CheckpointTrustbaseMismatchError(
@@ -9751,7 +9756,7 @@ var SphereTokenEngine = class {
9751
9756
  "AGGREGATOR_ERROR",
9752
9757
  options
9753
9758
  );
9754
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9759
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9755
9760
  const token = await Token.mint(certified, this.deps.verificationContext);
9756
9761
  return this.wrapToken(token);
9757
9762
  }
@@ -9768,7 +9773,7 @@ var SphereTokenEngine = class {
9768
9773
  "AGGREGATOR_ERROR",
9769
9774
  options
9770
9775
  );
9771
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9776
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9772
9777
  const token = await Token.mint(certified, this.deps.verificationContext);
9773
9778
  return this.wrapToken(token);
9774
9779
  }
@@ -9789,7 +9794,7 @@ var SphereTokenEngine = class {
9789
9794
  "TRANSFER_FAILED",
9790
9795
  options
9791
9796
  );
9792
- const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9797
+ const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9793
9798
  const transferred = await params.token.sdkToken.transfer(certified, this.deps.verificationContext);
9794
9799
  return this.wrapToken(transferred);
9795
9800
  }
@@ -9876,7 +9881,7 @@ var SphereTokenEngine = class {
9876
9881
  const burnUnlock = await SignaturePredicateUnlockScript.create(burnTx, this.deps.signingService);
9877
9882
  const burnCert = await CertificationData.fromTransaction(burnTx, burnUnlock);
9878
9883
  const burnProof = await this.submitAndAwaitProof(burnCert, burnTx, "Split burn failed", "TRANSFER_FAILED", ctx.options);
9879
- const burnCertified = await burnTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, burnProof);
9884
+ const burnCertified = await burnTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, burnProof);
9880
9885
  const burntToken = await ctx.params.token.sdkToken.transfer(burnCertified, this.deps.verificationContext);
9881
9886
  return { burntToken, burnProof };
9882
9887
  }
@@ -9915,7 +9920,7 @@ var SphereTokenEngine = class {
9915
9920
  );
9916
9921
  const certData = await CertificationData.fromMintTransaction(mintTx);
9917
9922
  const proof = await this.submitSplitMintLeg(certData, mintTx, options);
9918
- const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
9923
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
9919
9924
  const token = await Token.mint(certified, this.deps.verificationContext);
9920
9925
  return this.wrapToken(token);
9921
9926
  }
@@ -10058,6 +10063,7 @@ var SphereTokenEngine = class {
10058
10063
  client: this.deps.client,
10059
10064
  trustBase: this.deps.trustBase,
10060
10065
  predicateVerifier: this.deps.predicateVerifier,
10066
+ unicityCertificateVerifier: this.deps.unicityCertificateVerifier,
10061
10067
  intervalMs: this.deps.proofPollIntervalMs ?? DEFAULT_PROOF_POLL_INTERVAL_MS
10062
10068
  // #683
10063
10069
  },
@@ -10149,6 +10155,9 @@ async function createSphereTokenEngine(config) {
10149
10155
  }
10150
10156
  const trustBase = RootTrustBase.fromJSON(config.trustBaseJson);
10151
10157
  const predicateVerifier = PredicateVerifierService.create();
10158
+ const unicityCertificateVerifier = new UnicityCertificateVerifier(
10159
+ new UnicitySealQuorumSignaturesVerificationRule(new Secp256k1SignatureVerifier(), new VerifiedSealCache(256))
10160
+ );
10152
10161
  const mintJustificationVerifier = new MintJustificationVerifierService();
10153
10162
  mintJustificationVerifier.register(
10154
10163
  new SplitMintJustificationVerifier(decodeSpherePaymentData)
@@ -10157,10 +10166,12 @@ async function createSphereTokenEngine(config) {
10157
10166
  client: new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
10158
10167
  trustBase,
10159
10168
  predicateVerifier,
10169
+ unicityCertificateVerifier,
10160
10170
  mintJustificationVerifier,
10161
10171
  verificationContext: new VerificationContext(
10162
10172
  trustBase,
10163
10173
  predicateVerifier,
10174
+ unicityCertificateVerifier,
10164
10175
  mintJustificationVerifier,
10165
10176
  new TokenIssuanceVerifierService(false)
10166
10177
  ),
@@ -12262,6 +12273,90 @@ async function splitOpIndex(deps, envelope) {
12262
12273
  return Array.isArray(payload.direct) ? payload.direct.length : 0;
12263
12274
  }
12264
12275
 
12276
+ // modules/payments-v2/mint-params.ts
12277
+ function mintParams(recipientPubkey, coinId, amount) {
12278
+ return { recipientPubkey, value: { assets: [{ coinId, amount }] } };
12279
+ }
12280
+
12281
+ // modules/payments-v2/prewarm-cache.ts
12282
+ var PrewarmCache = class _PrewarmCache {
12283
+ entries = /* @__PURE__ */ new Map();
12284
+ generation = 0;
12285
+ static key(tokenId, stateHash) {
12286
+ return `${tokenId}:${stateHash}`;
12287
+ }
12288
+ /** Claim the cache for a warm about to start; supersedes any warm already running. */
12289
+ begin() {
12290
+ this.entries.clear();
12291
+ this.generation += 1;
12292
+ return this.generation;
12293
+ }
12294
+ /**
12295
+ * Publish a warm's result, unless it was cancelled or superseded while its
12296
+ * fetch was in flight — a cancel that only cleared what had landed so far
12297
+ * would be undone by the continuation of the very warm it cancelled.
12298
+ */
12299
+ commit(generation, warmed) {
12300
+ if (generation !== this.generation) return;
12301
+ for (const [tokenId, stateHash, bytes] of warmed) {
12302
+ this.entries.set(_PrewarmCache.key(tokenId, stateHash), bytes);
12303
+ }
12304
+ }
12305
+ get(tokenId, stateHash) {
12306
+ if (stateHash === void 0) return void 0;
12307
+ return this.entries.get(_PrewarmCache.key(tokenId, stateHash));
12308
+ }
12309
+ clear() {
12310
+ this.entries.clear();
12311
+ this.generation += 1;
12312
+ }
12313
+ get size() {
12314
+ return this.entries.size;
12315
+ }
12316
+ };
12317
+ async function warmSendSources(deps, request) {
12318
+ const ids = deps.queue.previewSelection(request);
12319
+ if (ids.length === 0) return;
12320
+ const generation = deps.cache.begin();
12321
+ const at = new Map(ids.map((id) => [id, deps.view.stateHashOf(id)]));
12322
+ let blobs;
12323
+ try {
12324
+ blobs = await deps.storagePort.getBlobs([...ids]);
12325
+ } catch {
12326
+ return;
12327
+ }
12328
+ const warmed = [];
12329
+ for (const [tokenId, bytes] of blobs) {
12330
+ const stateHash = at.get(tokenId);
12331
+ if (stateHash !== void 0) warmed.push([tokenId, stateHash, bytes]);
12332
+ }
12333
+ deps.cache.commit(generation, warmed);
12334
+ }
12335
+ async function takeSourceBlobs(deps, sourceIds) {
12336
+ const blobs = /* @__PURE__ */ new Map();
12337
+ const missing = [];
12338
+ let trustCache = deps.cache.size > 0;
12339
+ if (trustCache) {
12340
+ try {
12341
+ await deps.view.delta();
12342
+ } catch {
12343
+ trustCache = false;
12344
+ }
12345
+ }
12346
+ for (const tokenId of sourceIds) {
12347
+ const warm = trustCache ? deps.cache.get(tokenId, deps.view.stateHashOf(tokenId)) : void 0;
12348
+ if (warm === void 0) missing.push(tokenId);
12349
+ else blobs.set(tokenId, warm);
12350
+ }
12351
+ deps.cache.clear();
12352
+ if (missing.length > 0) {
12353
+ for (const [tokenId, bytes] of await deps.storagePort.getBlobs(missing)) {
12354
+ blobs.set(tokenId, bytes);
12355
+ }
12356
+ }
12357
+ return blobs;
12358
+ }
12359
+
12265
12360
  // modules/payments-v2/inventory/presentation.ts
12266
12361
  function toToken(tokenId, snapshot, asset, registry, flags) {
12267
12362
  const iconUrl = registry.getIconUrl(asset.coinId);
@@ -12348,6 +12443,13 @@ function priceOne(asset, quote) {
12348
12443
  fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
12349
12444
  };
12350
12445
  }
12446
+ function transferringToken(tokenId, coinId, amount, registry, now) {
12447
+ const snapshot = { assets: [], createdAt: now, updatedAt: now };
12448
+ return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
12449
+ transferring: true,
12450
+ suspectedSpent: false
12451
+ });
12452
+ }
12351
12453
 
12352
12454
  // modules/payments-v2/machine/payload.ts
12353
12455
  function buildPayload(recipientPubkey, request, spend, spentStates) {
@@ -12586,6 +12688,15 @@ var InventoryView = class {
12586
12688
  }
12587
12689
  return out;
12588
12690
  }
12691
+ /**
12692
+ * Current mirrored state of an ACTIVE token, or undefined when it is unknown
12693
+ * or tombstoned. Exists so a cached blob can be state-scoped (F6): a blob is
12694
+ * only reusable while the token still sits at the state it was fetched at.
12695
+ */
12696
+ stateHashOf(tokenId) {
12697
+ const entry = this.mirror.get(tokenId);
12698
+ return entry !== void 0 && entry.status === "active" ? entry.stateHash : void 0;
12699
+ }
12589
12700
  tokens(registry, filter) {
12590
12701
  const out = [];
12591
12702
  for (const [tokenId, entry] of this.mirror) {
@@ -13505,6 +13616,31 @@ var SpendQueue = class {
13505
13616
  }
13506
13617
  return { kind: "queued", settled: this.enqueue(reservationId, request.coinId, amount) };
13507
13618
  }
13619
+ /**
13620
+ * The sources `plan()` WOULD pick right now, reserving nothing and queueing
13621
+ * nothing — a read-only probe used to warm blobs while the user is still
13622
+ * looking at the confirm screen.
13623
+ *
13624
+ * Deliberately side-effect free: reserving here would pin the user's balance
13625
+ * across think-time and strand it if they never press send. The consequence is
13626
+ * that the real `plan()` may pick differently (inventory moves, another send
13627
+ * reserves first); callers must treat the result as a HINT and tolerate a miss,
13628
+ * never as a commitment that these sources will be spent.
13629
+ */
13630
+ previewSelection(request) {
13631
+ if (this.destroyed) return [];
13632
+ let amount;
13633
+ try {
13634
+ amount = parseAmount(request.amount);
13635
+ } catch {
13636
+ return [];
13637
+ }
13638
+ const { freeView, freeTotal } = this.freeView(request.coinId);
13639
+ if (freeTotal < amount) return [];
13640
+ const plan = selectCoins(freeView, amount, { workBudget: this.deps.workBudget });
13641
+ if (plan === null) return [];
13642
+ return [...plan.direct, ...plan.split !== void 0 ? [plan.split.tokenId] : []];
13643
+ }
13508
13644
  // Coverage math: free + Σ declared expected change of in-flight sends —
13509
13645
  // never the full amount of in-flight sources (§5.3).
13510
13646
  declareExpectedChange(transferId, coinId, amount) {
@@ -13854,6 +13990,7 @@ var PaymentsFacade = class {
13854
13990
  now: () => this.nowMs()
13855
13991
  });
13856
13992
  }
13993
+ prewarmed = new PrewarmCache();
13857
13994
  view;
13858
13995
  ledger;
13859
13996
  queue;
@@ -13947,6 +14084,15 @@ var PaymentsFacade = class {
13947
14084
  return derivePendingTransfers(this.machineStores, this.machineDeps.decryptPayload);
13948
14085
  }
13949
14086
  // ── money movement ─────────────────────────────────────────────────────────
14087
+ async prewarmSend(request) {
14088
+ await warmSendSources(this.warmDeps(), request);
14089
+ }
14090
+ discardPrewarm() {
14091
+ this.prewarmed.clear();
14092
+ }
14093
+ warmDeps() {
14094
+ return { queue: this.queue, view: this.view, storagePort: this.deps.storagePort, cache: this.prewarmed };
14095
+ }
13950
14096
  send(request) {
13951
14097
  return this.track(this.sendOutcome(request));
13952
14098
  }
@@ -14156,7 +14302,7 @@ var PaymentsFacade = class {
14156
14302
  }
14157
14303
  async materialize(transferId, recipientPubkey, request, spend, sourceIds) {
14158
14304
  const engine = this.engine();
14159
- const blobs = await this.deps.storagePort.getBlobs([...sourceIds]);
14305
+ const blobs = await takeSourceBlobs(this.warmDeps(), sourceIds);
14160
14306
  const sources = /* @__PURE__ */ new Map();
14161
14307
  const sourceTokens = [];
14162
14308
  const spentStates = {};
@@ -14169,7 +14315,7 @@ var PaymentsFacade = class {
14169
14315
  const keys = await engine.deliveryKeys(bytes);
14170
14316
  spentStates[tokenId] = { local: keys.stateHash, protocol: keys.stateHash };
14171
14317
  sources.set(tokenId, token);
14172
- sourceTokens.push(this.toUiToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId)));
14318
+ sourceTokens.push(transferringToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId), this.deps.registry, this.nowMs()));
14173
14319
  }
14174
14320
  const payload = buildPayload(recipientPubkey, request, spend, spentStates);
14175
14321
  const plan = {
@@ -14313,7 +14459,7 @@ var PaymentsFacade = class {
14313
14459
  await this.machineStores.mintJournal.upsert(entry);
14314
14460
  let token;
14315
14461
  try {
14316
- token = await engine.mint(this.mintParams(coinId, amount), { transferId: mintId });
14462
+ token = await engine.mint(mintParams(this.ownPubkeyBytes, coinId, amount), { transferId: mintId });
14317
14463
  } catch (err) {
14318
14464
  this.heartbeat.arm();
14319
14465
  return { success: false, error: messageOf(err) };
@@ -14372,7 +14518,7 @@ var PaymentsFacade = class {
14372
14518
  });
14373
14519
  return false;
14374
14520
  }
14375
- const token = await engine.mint(this.mintParams(entry.coinId, BigInt(entry.amount)), {
14521
+ const token = await engine.mint(mintParams(this.ownPubkeyBytes, entry.coinId, BigInt(entry.amount)), {
14376
14522
  transferId: entry.mintId
14377
14523
  });
14378
14524
  if (token.blob.tokenId !== entry.tokenId) {
@@ -14381,12 +14527,6 @@ var PaymentsFacade = class {
14381
14527
  await this.finalizeMint(engine, entry.mintId, token, entry.coinId, entry.amount);
14382
14528
  return true;
14383
14529
  }
14384
- mintParams(coinId, amount) {
14385
- return {
14386
- recipientPubkey: this.ownPubkeyBytes,
14387
- value: { assets: [{ coinId, amount }] }
14388
- };
14389
- }
14390
14530
  async tokenInServerInventory(tokenId) {
14391
14531
  let page = await this.deps.storagePort.listInventory();
14392
14532
  for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
@@ -14415,12 +14555,6 @@ var PaymentsFacade = class {
14415
14555
  page = await this.deps.storagePort.listInventory(page.cursor);
14416
14556
  }
14417
14557
  }
14418
- toUiToken(tokenId, coinId, amount) {
14419
- const now = this.nowMs();
14420
- const snapshot = { assets: [], createdAt: now, updatedAt: now };
14421
- const flags = { transferring: true, suspectedSpent: false };
14422
- return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, this.deps.registry, flags);
14423
- }
14424
14558
  track(op) {
14425
14559
  const settled = op.then(() => void 0, () => void 0);
14426
14560
  this.pendingOps.add(settled);