@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.
- 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 +156 -22
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +156 -22
- 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 +4 -0
- package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
- package/dist/impl/wallet-api-v2/index.js +4 -0
- package/dist/impl/wallet-api-v2/index.js.map +1 -1
- package/dist/index.cjs +156 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +156 -22
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +143 -16
- package/dist/modules/payments-v2/index.cjs.map +1 -1
- package/dist/modules/payments-v2/index.d.cts +6 -2
- package/dist/modules/payments-v2/index.d.ts +6 -2
- package/dist/modules/payments-v2/index.js +143 -16
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/dist/token-engine/index.cjs +17 -6
- package/dist/token-engine/index.cjs.map +1 -1
- package/dist/token-engine/index.js +17 -6
- package/dist/token-engine/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -228,6 +228,8 @@ interface PendingTransfer {
|
|
|
228
228
|
}
|
|
229
229
|
type ConnectionStatus = 'connected' | 'degraded' | 'offline';
|
|
230
230
|
interface PaymentsV2 {
|
|
231
|
+
prewarmSend(request: SendRequest): Promise<void>;
|
|
232
|
+
discardPrewarm(): void;
|
|
231
233
|
assets(coinId?: string): Promise<Asset[]>;
|
|
232
234
|
tokens(filter?: {
|
|
233
235
|
coinId?: string;
|
|
@@ -1042,6 +1044,7 @@ declare const MAX_RESELECT = 8;
|
|
|
1042
1044
|
declare const ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
1043
1045
|
declare class PaymentsFacade implements PaymentsV2 {
|
|
1044
1046
|
private readonly deps;
|
|
1047
|
+
private readonly prewarmed;
|
|
1045
1048
|
private readonly view;
|
|
1046
1049
|
private readonly ledger;
|
|
1047
1050
|
private readonly queue;
|
|
@@ -1086,6 +1089,9 @@ declare class PaymentsFacade implements PaymentsV2 {
|
|
|
1086
1089
|
connectionStatus(): ConnectionStatus;
|
|
1087
1090
|
/** §4 pending-transfers UI surface — derived on read, never cached (convergence.ts). */
|
|
1088
1091
|
pendingTransfers(): Promise<PendingTransfer[]>;
|
|
1092
|
+
prewarmSend(request: SendRequest): Promise<void>;
|
|
1093
|
+
discardPrewarm(): void;
|
|
1094
|
+
private warmDeps;
|
|
1089
1095
|
send(request: SendRequest): Promise<TransferResult>;
|
|
1090
1096
|
receive(): Promise<{
|
|
1091
1097
|
transfers: IncomingTransfer[];
|
|
@@ -1142,12 +1148,10 @@ declare class PaymentsFacade implements PaymentsV2 {
|
|
|
1142
1148
|
/** @returns how many journal entries were RESOLVED (cleared) — heartbeat progress. */
|
|
1143
1149
|
private replayMints;
|
|
1144
1150
|
private replayMint;
|
|
1145
|
-
private mintParams;
|
|
1146
1151
|
private tokenInServerInventory;
|
|
1147
1152
|
private engine;
|
|
1148
1153
|
private newId;
|
|
1149
1154
|
private seedHeldStates;
|
|
1150
|
-
private toUiToken;
|
|
1151
1155
|
private track;
|
|
1152
1156
|
private trackTail;
|
|
1153
1157
|
}
|
|
@@ -228,6 +228,8 @@ interface PendingTransfer {
|
|
|
228
228
|
}
|
|
229
229
|
type ConnectionStatus = 'connected' | 'degraded' | 'offline';
|
|
230
230
|
interface PaymentsV2 {
|
|
231
|
+
prewarmSend(request: SendRequest): Promise<void>;
|
|
232
|
+
discardPrewarm(): void;
|
|
231
233
|
assets(coinId?: string): Promise<Asset[]>;
|
|
232
234
|
tokens(filter?: {
|
|
233
235
|
coinId?: string;
|
|
@@ -1042,6 +1044,7 @@ declare const MAX_RESELECT = 8;
|
|
|
1042
1044
|
declare const ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
1043
1045
|
declare class PaymentsFacade implements PaymentsV2 {
|
|
1044
1046
|
private readonly deps;
|
|
1047
|
+
private readonly prewarmed;
|
|
1045
1048
|
private readonly view;
|
|
1046
1049
|
private readonly ledger;
|
|
1047
1050
|
private readonly queue;
|
|
@@ -1086,6 +1089,9 @@ declare class PaymentsFacade implements PaymentsV2 {
|
|
|
1086
1089
|
connectionStatus(): ConnectionStatus;
|
|
1087
1090
|
/** §4 pending-transfers UI surface — derived on read, never cached (convergence.ts). */
|
|
1088
1091
|
pendingTransfers(): Promise<PendingTransfer[]>;
|
|
1092
|
+
prewarmSend(request: SendRequest): Promise<void>;
|
|
1093
|
+
discardPrewarm(): void;
|
|
1094
|
+
private warmDeps;
|
|
1089
1095
|
send(request: SendRequest): Promise<TransferResult>;
|
|
1090
1096
|
receive(): Promise<{
|
|
1091
1097
|
transfers: IncomingTransfer[];
|
|
@@ -1142,12 +1148,10 @@ declare class PaymentsFacade implements PaymentsV2 {
|
|
|
1142
1148
|
/** @returns how many journal entries were RESOLVED (cleared) — heartbeat progress. */
|
|
1143
1149
|
private replayMints;
|
|
1144
1150
|
private replayMint;
|
|
1145
|
-
private mintParams;
|
|
1146
1151
|
private tokenInServerInventory;
|
|
1147
1152
|
private engine;
|
|
1148
1153
|
private newId;
|
|
1149
1154
|
private seedHeldStates;
|
|
1150
|
-
private toUiToken;
|
|
1151
1155
|
private track;
|
|
1152
1156
|
private trackTail;
|
|
1153
1157
|
}
|
|
@@ -474,6 +474,10 @@ import { StateMask } from "@unicitylabs/state-transition-sdk/lib/transaction/Sta
|
|
|
474
474
|
import { MintJustificationVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js";
|
|
475
475
|
import { TokenIssuanceVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/TokenIssuanceVerifierService.js";
|
|
476
476
|
import { VerificationContext } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/VerificationContext.js";
|
|
477
|
+
import { UnicityCertificateVerifier } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/UnicityCertificateVerifier.js";
|
|
478
|
+
import { UnicitySealQuorumSignaturesVerificationRule } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.js";
|
|
479
|
+
import { VerifiedSealCache } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/VerifiedSealCache.js";
|
|
480
|
+
import { Secp256k1SignatureVerifier } from "@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Secp256k1SignatureVerifier.js";
|
|
477
481
|
import { WorkerTokenVerifier } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/worker/WorkerTokenVerifier.js";
|
|
478
482
|
import { EncodedPredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js";
|
|
479
483
|
import { SignaturePredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js";
|
|
@@ -1596,6 +1600,90 @@ async function splitOpIndex(deps, envelope) {
|
|
|
1596
1600
|
return Array.isArray(payload.direct) ? payload.direct.length : 0;
|
|
1597
1601
|
}
|
|
1598
1602
|
|
|
1603
|
+
// modules/payments-v2/mint-params.ts
|
|
1604
|
+
function mintParams(recipientPubkey, coinId, amount) {
|
|
1605
|
+
return { recipientPubkey, value: { assets: [{ coinId, amount }] } };
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// modules/payments-v2/prewarm-cache.ts
|
|
1609
|
+
var PrewarmCache = class _PrewarmCache {
|
|
1610
|
+
entries = /* @__PURE__ */ new Map();
|
|
1611
|
+
generation = 0;
|
|
1612
|
+
static key(tokenId, stateHash) {
|
|
1613
|
+
return `${tokenId}:${stateHash}`;
|
|
1614
|
+
}
|
|
1615
|
+
/** Claim the cache for a warm about to start; supersedes any warm already running. */
|
|
1616
|
+
begin() {
|
|
1617
|
+
this.entries.clear();
|
|
1618
|
+
this.generation += 1;
|
|
1619
|
+
return this.generation;
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Publish a warm's result, unless it was cancelled or superseded while its
|
|
1623
|
+
* fetch was in flight — a cancel that only cleared what had landed so far
|
|
1624
|
+
* would be undone by the continuation of the very warm it cancelled.
|
|
1625
|
+
*/
|
|
1626
|
+
commit(generation, warmed) {
|
|
1627
|
+
if (generation !== this.generation) return;
|
|
1628
|
+
for (const [tokenId, stateHash, bytes] of warmed) {
|
|
1629
|
+
this.entries.set(_PrewarmCache.key(tokenId, stateHash), bytes);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
get(tokenId, stateHash) {
|
|
1633
|
+
if (stateHash === void 0) return void 0;
|
|
1634
|
+
return this.entries.get(_PrewarmCache.key(tokenId, stateHash));
|
|
1635
|
+
}
|
|
1636
|
+
clear() {
|
|
1637
|
+
this.entries.clear();
|
|
1638
|
+
this.generation += 1;
|
|
1639
|
+
}
|
|
1640
|
+
get size() {
|
|
1641
|
+
return this.entries.size;
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
async function warmSendSources(deps, request) {
|
|
1645
|
+
const ids = deps.queue.previewSelection(request);
|
|
1646
|
+
if (ids.length === 0) return;
|
|
1647
|
+
const generation = deps.cache.begin();
|
|
1648
|
+
const at = new Map(ids.map((id) => [id, deps.view.stateHashOf(id)]));
|
|
1649
|
+
let blobs;
|
|
1650
|
+
try {
|
|
1651
|
+
blobs = await deps.storagePort.getBlobs([...ids]);
|
|
1652
|
+
} catch {
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
const warmed = [];
|
|
1656
|
+
for (const [tokenId, bytes] of blobs) {
|
|
1657
|
+
const stateHash = at.get(tokenId);
|
|
1658
|
+
if (stateHash !== void 0) warmed.push([tokenId, stateHash, bytes]);
|
|
1659
|
+
}
|
|
1660
|
+
deps.cache.commit(generation, warmed);
|
|
1661
|
+
}
|
|
1662
|
+
async function takeSourceBlobs(deps, sourceIds) {
|
|
1663
|
+
const blobs = /* @__PURE__ */ new Map();
|
|
1664
|
+
const missing = [];
|
|
1665
|
+
let trustCache = deps.cache.size > 0;
|
|
1666
|
+
if (trustCache) {
|
|
1667
|
+
try {
|
|
1668
|
+
await deps.view.delta();
|
|
1669
|
+
} catch {
|
|
1670
|
+
trustCache = false;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
for (const tokenId of sourceIds) {
|
|
1674
|
+
const warm = trustCache ? deps.cache.get(tokenId, deps.view.stateHashOf(tokenId)) : void 0;
|
|
1675
|
+
if (warm === void 0) missing.push(tokenId);
|
|
1676
|
+
else blobs.set(tokenId, warm);
|
|
1677
|
+
}
|
|
1678
|
+
deps.cache.clear();
|
|
1679
|
+
if (missing.length > 0) {
|
|
1680
|
+
for (const [tokenId, bytes] of await deps.storagePort.getBlobs(missing)) {
|
|
1681
|
+
blobs.set(tokenId, bytes);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
return blobs;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1599
1687
|
// modules/payments-v2/inventory/presentation.ts
|
|
1600
1688
|
function toToken(tokenId, snapshot, asset, registry, flags) {
|
|
1601
1689
|
const iconUrl = registry.getIconUrl(asset.coinId);
|
|
@@ -1682,6 +1770,13 @@ function priceOne(asset, quote) {
|
|
|
1682
1770
|
fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
|
|
1683
1771
|
};
|
|
1684
1772
|
}
|
|
1773
|
+
function transferringToken(tokenId, coinId, amount, registry, now) {
|
|
1774
|
+
const snapshot = { assets: [], createdAt: now, updatedAt: now };
|
|
1775
|
+
return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
|
|
1776
|
+
transferring: true,
|
|
1777
|
+
suspectedSpent: false
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1685
1780
|
|
|
1686
1781
|
// modules/payments-v2/machine/payload.ts
|
|
1687
1782
|
function buildPayload(recipientPubkey, request, spend, spentStates) {
|
|
@@ -2770,6 +2865,15 @@ var InventoryView = class {
|
|
|
2770
2865
|
}
|
|
2771
2866
|
return out;
|
|
2772
2867
|
}
|
|
2868
|
+
/**
|
|
2869
|
+
* Current mirrored state of an ACTIVE token, or undefined when it is unknown
|
|
2870
|
+
* or tombstoned. Exists so a cached blob can be state-scoped (F6): a blob is
|
|
2871
|
+
* only reusable while the token still sits at the state it was fetched at.
|
|
2872
|
+
*/
|
|
2873
|
+
stateHashOf(tokenId) {
|
|
2874
|
+
const entry = this.mirror.get(tokenId);
|
|
2875
|
+
return entry !== void 0 && entry.status === "active" ? entry.stateHash : void 0;
|
|
2876
|
+
}
|
|
2773
2877
|
tokens(registry, filter) {
|
|
2774
2878
|
const out = [];
|
|
2775
2879
|
for (const [tokenId, entry] of this.mirror) {
|
|
@@ -3781,6 +3885,31 @@ var SpendQueue = class {
|
|
|
3781
3885
|
}
|
|
3782
3886
|
return { kind: "queued", settled: this.enqueue(reservationId, request.coinId, amount) };
|
|
3783
3887
|
}
|
|
3888
|
+
/**
|
|
3889
|
+
* The sources `plan()` WOULD pick right now, reserving nothing and queueing
|
|
3890
|
+
* nothing — a read-only probe used to warm blobs while the user is still
|
|
3891
|
+
* looking at the confirm screen.
|
|
3892
|
+
*
|
|
3893
|
+
* Deliberately side-effect free: reserving here would pin the user's balance
|
|
3894
|
+
* across think-time and strand it if they never press send. The consequence is
|
|
3895
|
+
* that the real `plan()` may pick differently (inventory moves, another send
|
|
3896
|
+
* reserves first); callers must treat the result as a HINT and tolerate a miss,
|
|
3897
|
+
* never as a commitment that these sources will be spent.
|
|
3898
|
+
*/
|
|
3899
|
+
previewSelection(request) {
|
|
3900
|
+
if (this.destroyed) return [];
|
|
3901
|
+
let amount;
|
|
3902
|
+
try {
|
|
3903
|
+
amount = parseAmount(request.amount);
|
|
3904
|
+
} catch {
|
|
3905
|
+
return [];
|
|
3906
|
+
}
|
|
3907
|
+
const { freeView, freeTotal } = this.freeView(request.coinId);
|
|
3908
|
+
if (freeTotal < amount) return [];
|
|
3909
|
+
const plan = selectCoins(freeView, amount, { workBudget: this.deps.workBudget });
|
|
3910
|
+
if (plan === null) return [];
|
|
3911
|
+
return [...plan.direct, ...plan.split !== void 0 ? [plan.split.tokenId] : []];
|
|
3912
|
+
}
|
|
3784
3913
|
// Coverage math: free + Σ declared expected change of in-flight sends —
|
|
3785
3914
|
// never the full amount of in-flight sources (§5.3).
|
|
3786
3915
|
declareExpectedChange(transferId, coinId, amount) {
|
|
@@ -4130,6 +4259,7 @@ var PaymentsFacade = class {
|
|
|
4130
4259
|
now: () => this.nowMs()
|
|
4131
4260
|
});
|
|
4132
4261
|
}
|
|
4262
|
+
prewarmed = new PrewarmCache();
|
|
4133
4263
|
view;
|
|
4134
4264
|
ledger;
|
|
4135
4265
|
queue;
|
|
@@ -4223,6 +4353,15 @@ var PaymentsFacade = class {
|
|
|
4223
4353
|
return derivePendingTransfers(this.machineStores, this.machineDeps.decryptPayload);
|
|
4224
4354
|
}
|
|
4225
4355
|
// ── money movement ─────────────────────────────────────────────────────────
|
|
4356
|
+
async prewarmSend(request) {
|
|
4357
|
+
await warmSendSources(this.warmDeps(), request);
|
|
4358
|
+
}
|
|
4359
|
+
discardPrewarm() {
|
|
4360
|
+
this.prewarmed.clear();
|
|
4361
|
+
}
|
|
4362
|
+
warmDeps() {
|
|
4363
|
+
return { queue: this.queue, view: this.view, storagePort: this.deps.storagePort, cache: this.prewarmed };
|
|
4364
|
+
}
|
|
4226
4365
|
send(request) {
|
|
4227
4366
|
return this.track(this.sendOutcome(request));
|
|
4228
4367
|
}
|
|
@@ -4432,7 +4571,7 @@ var PaymentsFacade = class {
|
|
|
4432
4571
|
}
|
|
4433
4572
|
async materialize(transferId, recipientPubkey, request, spend, sourceIds) {
|
|
4434
4573
|
const engine = this.engine();
|
|
4435
|
-
const blobs = await this.
|
|
4574
|
+
const blobs = await takeSourceBlobs(this.warmDeps(), sourceIds);
|
|
4436
4575
|
const sources = /* @__PURE__ */ new Map();
|
|
4437
4576
|
const sourceTokens = [];
|
|
4438
4577
|
const spentStates = {};
|
|
@@ -4445,7 +4584,7 @@ var PaymentsFacade = class {
|
|
|
4445
4584
|
const keys = await engine.deliveryKeys(bytes);
|
|
4446
4585
|
spentStates[tokenId] = { local: keys.stateHash, protocol: keys.stateHash };
|
|
4447
4586
|
sources.set(tokenId, token);
|
|
4448
|
-
sourceTokens.push(
|
|
4587
|
+
sourceTokens.push(transferringToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId), this.deps.registry, this.nowMs()));
|
|
4449
4588
|
}
|
|
4450
4589
|
const payload = buildPayload(recipientPubkey, request, spend, spentStates);
|
|
4451
4590
|
const plan = {
|
|
@@ -4589,7 +4728,7 @@ var PaymentsFacade = class {
|
|
|
4589
4728
|
await this.machineStores.mintJournal.upsert(entry);
|
|
4590
4729
|
let token;
|
|
4591
4730
|
try {
|
|
4592
|
-
token = await engine.mint(this.
|
|
4731
|
+
token = await engine.mint(mintParams(this.ownPubkeyBytes, coinId, amount), { transferId: mintId });
|
|
4593
4732
|
} catch (err) {
|
|
4594
4733
|
this.heartbeat.arm();
|
|
4595
4734
|
return { success: false, error: messageOf(err) };
|
|
@@ -4648,7 +4787,7 @@ var PaymentsFacade = class {
|
|
|
4648
4787
|
});
|
|
4649
4788
|
return false;
|
|
4650
4789
|
}
|
|
4651
|
-
const token = await engine.mint(this.
|
|
4790
|
+
const token = await engine.mint(mintParams(this.ownPubkeyBytes, entry.coinId, BigInt(entry.amount)), {
|
|
4652
4791
|
transferId: entry.mintId
|
|
4653
4792
|
});
|
|
4654
4793
|
if (token.blob.tokenId !== entry.tokenId) {
|
|
@@ -4657,12 +4796,6 @@ var PaymentsFacade = class {
|
|
|
4657
4796
|
await this.finalizeMint(engine, entry.mintId, token, entry.coinId, entry.amount);
|
|
4658
4797
|
return true;
|
|
4659
4798
|
}
|
|
4660
|
-
mintParams(coinId, amount) {
|
|
4661
|
-
return {
|
|
4662
|
-
recipientPubkey: this.ownPubkeyBytes,
|
|
4663
|
-
value: { assets: [{ coinId, amount }] }
|
|
4664
|
-
};
|
|
4665
|
-
}
|
|
4666
4799
|
async tokenInServerInventory(tokenId) {
|
|
4667
4800
|
let page = await this.deps.storagePort.listInventory();
|
|
4668
4801
|
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
@@ -4691,12 +4824,6 @@ var PaymentsFacade = class {
|
|
|
4691
4824
|
page = await this.deps.storagePort.listInventory(page.cursor);
|
|
4692
4825
|
}
|
|
4693
4826
|
}
|
|
4694
|
-
toUiToken(tokenId, coinId, amount) {
|
|
4695
|
-
const now = this.nowMs();
|
|
4696
|
-
const snapshot = { assets: [], createdAt: now, updatedAt: now };
|
|
4697
|
-
const flags = { transferring: true, suspectedSpent: false };
|
|
4698
|
-
return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, this.deps.registry, flags);
|
|
4699
|
-
}
|
|
4700
4827
|
track(op) {
|
|
4701
4828
|
const settled = op.then(() => void 0, () => void 0);
|
|
4702
4829
|
this.pendingOps.add(settled);
|