@unicitylabs/sphere-sdk 0.14.10 → 0.14.11-dev.2
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 +157 -18
- 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 +157 -18
- 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/index.cjs +157 -18
- 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 +157 -18
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +157 -18
- 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 +157 -18
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -12417,6 +12417,90 @@ async function splitOpIndex(deps, envelope) {
|
|
|
12417
12417
|
return Array.isArray(payload.direct) ? payload.direct.length : 0;
|
|
12418
12418
|
}
|
|
12419
12419
|
|
|
12420
|
+
// modules/payments-v2/mint-params.ts
|
|
12421
|
+
function mintParams(recipientPubkey, coinId, amount) {
|
|
12422
|
+
return { recipientPubkey, value: { assets: [{ coinId, amount }] } };
|
|
12423
|
+
}
|
|
12424
|
+
|
|
12425
|
+
// modules/payments-v2/prewarm-cache.ts
|
|
12426
|
+
var PrewarmCache = class _PrewarmCache {
|
|
12427
|
+
entries = /* @__PURE__ */ new Map();
|
|
12428
|
+
generation = 0;
|
|
12429
|
+
static key(tokenId, stateHash) {
|
|
12430
|
+
return `${tokenId}:${stateHash}`;
|
|
12431
|
+
}
|
|
12432
|
+
/** Claim the cache for a warm about to start; supersedes any warm already running. */
|
|
12433
|
+
begin() {
|
|
12434
|
+
this.entries.clear();
|
|
12435
|
+
this.generation += 1;
|
|
12436
|
+
return this.generation;
|
|
12437
|
+
}
|
|
12438
|
+
/**
|
|
12439
|
+
* Publish a warm's result, unless it was cancelled or superseded while its
|
|
12440
|
+
* fetch was in flight — a cancel that only cleared what had landed so far
|
|
12441
|
+
* would be undone by the continuation of the very warm it cancelled.
|
|
12442
|
+
*/
|
|
12443
|
+
commit(generation, warmed) {
|
|
12444
|
+
if (generation !== this.generation) return;
|
|
12445
|
+
for (const [tokenId, stateHash, bytes] of warmed) {
|
|
12446
|
+
this.entries.set(_PrewarmCache.key(tokenId, stateHash), bytes);
|
|
12447
|
+
}
|
|
12448
|
+
}
|
|
12449
|
+
get(tokenId, stateHash) {
|
|
12450
|
+
if (stateHash === void 0) return void 0;
|
|
12451
|
+
return this.entries.get(_PrewarmCache.key(tokenId, stateHash));
|
|
12452
|
+
}
|
|
12453
|
+
clear() {
|
|
12454
|
+
this.entries.clear();
|
|
12455
|
+
this.generation += 1;
|
|
12456
|
+
}
|
|
12457
|
+
get size() {
|
|
12458
|
+
return this.entries.size;
|
|
12459
|
+
}
|
|
12460
|
+
};
|
|
12461
|
+
async function warmSendSources(deps, request) {
|
|
12462
|
+
const ids = deps.queue.previewSelection(request);
|
|
12463
|
+
if (ids.length === 0) return;
|
|
12464
|
+
const generation = deps.cache.begin();
|
|
12465
|
+
const at = new Map(ids.map((id) => [id, deps.view.stateHashOf(id)]));
|
|
12466
|
+
let blobs;
|
|
12467
|
+
try {
|
|
12468
|
+
blobs = await deps.storagePort.getBlobs([...ids]);
|
|
12469
|
+
} catch {
|
|
12470
|
+
return;
|
|
12471
|
+
}
|
|
12472
|
+
const warmed = [];
|
|
12473
|
+
for (const [tokenId, bytes] of blobs) {
|
|
12474
|
+
const stateHash = at.get(tokenId);
|
|
12475
|
+
if (stateHash !== void 0) warmed.push([tokenId, stateHash, bytes]);
|
|
12476
|
+
}
|
|
12477
|
+
deps.cache.commit(generation, warmed);
|
|
12478
|
+
}
|
|
12479
|
+
async function takeSourceBlobs(deps, sourceIds) {
|
|
12480
|
+
const blobs = /* @__PURE__ */ new Map();
|
|
12481
|
+
const missing = [];
|
|
12482
|
+
let trustCache = deps.cache.size > 0;
|
|
12483
|
+
if (trustCache) {
|
|
12484
|
+
try {
|
|
12485
|
+
await deps.view.delta();
|
|
12486
|
+
} catch {
|
|
12487
|
+
trustCache = false;
|
|
12488
|
+
}
|
|
12489
|
+
}
|
|
12490
|
+
for (const tokenId of sourceIds) {
|
|
12491
|
+
const warm = trustCache ? deps.cache.get(tokenId, deps.view.stateHashOf(tokenId)) : void 0;
|
|
12492
|
+
if (warm === void 0) missing.push(tokenId);
|
|
12493
|
+
else blobs.set(tokenId, warm);
|
|
12494
|
+
}
|
|
12495
|
+
deps.cache.clear();
|
|
12496
|
+
if (missing.length > 0) {
|
|
12497
|
+
for (const [tokenId, bytes] of await deps.storagePort.getBlobs(missing)) {
|
|
12498
|
+
blobs.set(tokenId, bytes);
|
|
12499
|
+
}
|
|
12500
|
+
}
|
|
12501
|
+
return blobs;
|
|
12502
|
+
}
|
|
12503
|
+
|
|
12420
12504
|
// modules/payments-v2/inventory/presentation.ts
|
|
12421
12505
|
function toToken(tokenId, snapshot, asset, registry, flags) {
|
|
12422
12506
|
const iconUrl = registry.getIconUrl(asset.coinId);
|
|
@@ -12503,6 +12587,13 @@ function priceOne(asset, quote) {
|
|
|
12503
12587
|
fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
|
|
12504
12588
|
};
|
|
12505
12589
|
}
|
|
12590
|
+
function transferringToken(tokenId, coinId, amount, registry, now) {
|
|
12591
|
+
const snapshot = { assets: [], createdAt: now, updatedAt: now };
|
|
12592
|
+
return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
|
|
12593
|
+
transferring: true,
|
|
12594
|
+
suspectedSpent: false
|
|
12595
|
+
});
|
|
12596
|
+
}
|
|
12506
12597
|
|
|
12507
12598
|
// modules/payments-v2/machine/payload.ts
|
|
12508
12599
|
function buildPayload(recipientPubkey, request, spend, spentStates) {
|
|
@@ -12741,6 +12832,15 @@ var InventoryView = class {
|
|
|
12741
12832
|
}
|
|
12742
12833
|
return out;
|
|
12743
12834
|
}
|
|
12835
|
+
/**
|
|
12836
|
+
* Current mirrored state of an ACTIVE token, or undefined when it is unknown
|
|
12837
|
+
* or tombstoned. Exists so a cached blob can be state-scoped (F6): a blob is
|
|
12838
|
+
* only reusable while the token still sits at the state it was fetched at.
|
|
12839
|
+
*/
|
|
12840
|
+
stateHashOf(tokenId) {
|
|
12841
|
+
const entry = this.mirror.get(tokenId);
|
|
12842
|
+
return entry !== void 0 && entry.status === "active" ? entry.stateHash : void 0;
|
|
12843
|
+
}
|
|
12744
12844
|
tokens(registry, filter) {
|
|
12745
12845
|
const out = [];
|
|
12746
12846
|
for (const [tokenId, entry] of this.mirror) {
|
|
@@ -12928,6 +13028,7 @@ var InventoryView = class {
|
|
|
12928
13028
|
|
|
12929
13029
|
// modules/payments-v2/receive/Receive.ts
|
|
12930
13030
|
var ACK_BATCH_SIZE = 200;
|
|
13031
|
+
var REFRESH_INTERVAL_MS = 750;
|
|
12931
13032
|
var POLL_INTERVAL_MS = 3e4;
|
|
12932
13033
|
var ATTENTION_CLAIM_CONFLICT = "claim:conflict";
|
|
12933
13034
|
function receivedDedupKey(tokenId, stateHash) {
|
|
@@ -12944,6 +13045,7 @@ var Receive = class {
|
|
|
12944
13045
|
drainFlight = new SingleFlight();
|
|
12945
13046
|
pollTimer = null;
|
|
12946
13047
|
unsubscribeWake = null;
|
|
13048
|
+
lastRefreshAt = 0;
|
|
12947
13049
|
drainOnce() {
|
|
12948
13050
|
return this.drainFlight.run(() => this.doDrain());
|
|
12949
13051
|
}
|
|
@@ -12961,6 +13063,14 @@ var Receive = class {
|
|
|
12961
13063
|
this.unsubscribeWake?.();
|
|
12962
13064
|
this.unsubscribeWake = null;
|
|
12963
13065
|
}
|
|
13066
|
+
/** Ask for a mirror refresh at most once per REFRESH_INTERVAL_MS. */
|
|
13067
|
+
maybeRefresh(deps) {
|
|
13068
|
+
if (deps.refreshView === void 0) return;
|
|
13069
|
+
const now = deps.now?.() ?? Date.now();
|
|
13070
|
+
if (now - this.lastRefreshAt < REFRESH_INTERVAL_MS) return;
|
|
13071
|
+
this.lastRefreshAt = now;
|
|
13072
|
+
deps.refreshView();
|
|
13073
|
+
}
|
|
12964
13074
|
async doDrain() {
|
|
12965
13075
|
const deps = this.deps;
|
|
12966
13076
|
const engine = deps.engine();
|
|
@@ -12974,6 +13084,7 @@ var Receive = class {
|
|
|
12974
13084
|
for await (const entry of deps.delivery.incoming(basis === null ? void 0 : String(basis.cursor))) {
|
|
12975
13085
|
await this.processEntry(deps, engine, entry, pending, stored);
|
|
12976
13086
|
if (pending.length >= ACK_BATCH_SIZE) await flushAcks(deps, pending, pageEpoch);
|
|
13087
|
+
if (stored.length > 0) this.maybeRefresh(deps);
|
|
12977
13088
|
}
|
|
12978
13089
|
await flushAcks(deps, pending, pageEpoch);
|
|
12979
13090
|
const served = deps.delivery.incomingEpoch();
|
|
@@ -13660,6 +13771,31 @@ var SpendQueue = class {
|
|
|
13660
13771
|
}
|
|
13661
13772
|
return { kind: "queued", settled: this.enqueue(reservationId, request.coinId, amount) };
|
|
13662
13773
|
}
|
|
13774
|
+
/**
|
|
13775
|
+
* The sources `plan()` WOULD pick right now, reserving nothing and queueing
|
|
13776
|
+
* nothing — a read-only probe used to warm blobs while the user is still
|
|
13777
|
+
* looking at the confirm screen.
|
|
13778
|
+
*
|
|
13779
|
+
* Deliberately side-effect free: reserving here would pin the user's balance
|
|
13780
|
+
* across think-time and strand it if they never press send. The consequence is
|
|
13781
|
+
* that the real `plan()` may pick differently (inventory moves, another send
|
|
13782
|
+
* reserves first); callers must treat the result as a HINT and tolerate a miss,
|
|
13783
|
+
* never as a commitment that these sources will be spent.
|
|
13784
|
+
*/
|
|
13785
|
+
previewSelection(request) {
|
|
13786
|
+
if (this.destroyed) return [];
|
|
13787
|
+
let amount;
|
|
13788
|
+
try {
|
|
13789
|
+
amount = parseAmount(request.amount);
|
|
13790
|
+
} catch {
|
|
13791
|
+
return [];
|
|
13792
|
+
}
|
|
13793
|
+
const { freeView, freeTotal } = this.freeView(request.coinId);
|
|
13794
|
+
if (freeTotal < amount) return [];
|
|
13795
|
+
const plan = selectCoins(freeView, amount, { workBudget: this.deps.workBudget });
|
|
13796
|
+
if (plan === null) return [];
|
|
13797
|
+
return [...plan.direct, ...plan.split !== void 0 ? [plan.split.tokenId] : []];
|
|
13798
|
+
}
|
|
13663
13799
|
// Coverage math: free + Σ declared expected change of in-flight sends —
|
|
13664
13800
|
// never the full amount of in-flight sources (§5.3).
|
|
13665
13801
|
declareExpectedChange(transferId, coinId, amount) {
|
|
@@ -13848,7 +13984,7 @@ function composeFacadeParts(deps, hooks) {
|
|
|
13848
13984
|
machineDeps,
|
|
13849
13985
|
machine: new TransferMachine(machineDeps),
|
|
13850
13986
|
heldStates,
|
|
13851
|
-
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates),
|
|
13987
|
+
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates, view),
|
|
13852
13988
|
requests: buildRequests(deps, hooks),
|
|
13853
13989
|
restoreDeps: buildRestoreDeps(deps, machineDeps, machineStores, view)
|
|
13854
13990
|
};
|
|
@@ -13915,7 +14051,7 @@ function buildMachineDeps(deps, hooks, historyStore) {
|
|
|
13915
14051
|
}
|
|
13916
14052
|
};
|
|
13917
14053
|
}
|
|
13918
|
-
function buildReceive(deps, hooks, historyStore, heldStates) {
|
|
14054
|
+
function buildReceive(deps, hooks, historyStore, heldStates, view) {
|
|
13919
14055
|
return new Receive({
|
|
13920
14056
|
delivery: deps.deliveryPort,
|
|
13921
14057
|
engine: () => hooks.engine(),
|
|
@@ -13929,6 +14065,11 @@ function buildReceive(deps, hooks, historyStore, heldStates) {
|
|
|
13929
14065
|
registry: deps.registry,
|
|
13930
14066
|
recordReceived: (record) => recordReceived(historyStore, record),
|
|
13931
14067
|
emit: (event, transfer) => deps.emit(event, transfer),
|
|
14068
|
+
// The mirror is the balance the wallet renders; refresh it as the drain runs
|
|
14069
|
+
// so a multi-token receive lands progressively instead of all at the end.
|
|
14070
|
+
refreshView: () => {
|
|
14071
|
+
void view.delta().catch(() => void 0);
|
|
14072
|
+
},
|
|
13932
14073
|
attention: (transferId, code, detail) => {
|
|
13933
14074
|
deps.emit(
|
|
13934
14075
|
"transfer:attention",
|
|
@@ -14009,6 +14150,7 @@ var PaymentsFacade = class {
|
|
|
14009
14150
|
now: () => this.nowMs()
|
|
14010
14151
|
});
|
|
14011
14152
|
}
|
|
14153
|
+
prewarmed = new PrewarmCache();
|
|
14012
14154
|
view;
|
|
14013
14155
|
ledger;
|
|
14014
14156
|
queue;
|
|
@@ -14102,6 +14244,15 @@ var PaymentsFacade = class {
|
|
|
14102
14244
|
return derivePendingTransfers(this.machineStores, this.machineDeps.decryptPayload);
|
|
14103
14245
|
}
|
|
14104
14246
|
// ── money movement ─────────────────────────────────────────────────────────
|
|
14247
|
+
async prewarmSend(request) {
|
|
14248
|
+
await warmSendSources(this.warmDeps(), request);
|
|
14249
|
+
}
|
|
14250
|
+
discardPrewarm() {
|
|
14251
|
+
this.prewarmed.clear();
|
|
14252
|
+
}
|
|
14253
|
+
warmDeps() {
|
|
14254
|
+
return { queue: this.queue, view: this.view, storagePort: this.deps.storagePort, cache: this.prewarmed };
|
|
14255
|
+
}
|
|
14105
14256
|
send(request) {
|
|
14106
14257
|
return this.track(this.sendOutcome(request));
|
|
14107
14258
|
}
|
|
@@ -14311,7 +14462,7 @@ var PaymentsFacade = class {
|
|
|
14311
14462
|
}
|
|
14312
14463
|
async materialize(transferId, recipientPubkey, request, spend, sourceIds) {
|
|
14313
14464
|
const engine = this.engine();
|
|
14314
|
-
const blobs = await this.
|
|
14465
|
+
const blobs = await takeSourceBlobs(this.warmDeps(), sourceIds);
|
|
14315
14466
|
const sources = /* @__PURE__ */ new Map();
|
|
14316
14467
|
const sourceTokens = [];
|
|
14317
14468
|
const spentStates = {};
|
|
@@ -14324,7 +14475,7 @@ var PaymentsFacade = class {
|
|
|
14324
14475
|
const keys = await engine.deliveryKeys(bytes);
|
|
14325
14476
|
spentStates[tokenId] = { local: keys.stateHash, protocol: keys.stateHash };
|
|
14326
14477
|
sources.set(tokenId, token);
|
|
14327
|
-
sourceTokens.push(
|
|
14478
|
+
sourceTokens.push(transferringToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId), this.deps.registry, this.nowMs()));
|
|
14328
14479
|
}
|
|
14329
14480
|
const payload = buildPayload(recipientPubkey, request, spend, spentStates);
|
|
14330
14481
|
const plan = {
|
|
@@ -14468,7 +14619,7 @@ var PaymentsFacade = class {
|
|
|
14468
14619
|
await this.machineStores.mintJournal.upsert(entry);
|
|
14469
14620
|
let token;
|
|
14470
14621
|
try {
|
|
14471
|
-
token = await engine.mint(this.
|
|
14622
|
+
token = await engine.mint(mintParams(this.ownPubkeyBytes, coinId, amount), { transferId: mintId });
|
|
14472
14623
|
} catch (err) {
|
|
14473
14624
|
this.heartbeat.arm();
|
|
14474
14625
|
return { success: false, error: messageOf(err) };
|
|
@@ -14527,7 +14678,7 @@ var PaymentsFacade = class {
|
|
|
14527
14678
|
});
|
|
14528
14679
|
return false;
|
|
14529
14680
|
}
|
|
14530
|
-
const token = await engine.mint(this.
|
|
14681
|
+
const token = await engine.mint(mintParams(this.ownPubkeyBytes, entry.coinId, BigInt(entry.amount)), {
|
|
14531
14682
|
transferId: entry.mintId
|
|
14532
14683
|
});
|
|
14533
14684
|
if (token.blob.tokenId !== entry.tokenId) {
|
|
@@ -14536,12 +14687,6 @@ var PaymentsFacade = class {
|
|
|
14536
14687
|
await this.finalizeMint(engine, entry.mintId, token, entry.coinId, entry.amount);
|
|
14537
14688
|
return true;
|
|
14538
14689
|
}
|
|
14539
|
-
mintParams(coinId, amount) {
|
|
14540
|
-
return {
|
|
14541
|
-
recipientPubkey: this.ownPubkeyBytes,
|
|
14542
|
-
value: { assets: [{ coinId, amount }] }
|
|
14543
|
-
};
|
|
14544
|
-
}
|
|
14545
14690
|
async tokenInServerInventory(tokenId) {
|
|
14546
14691
|
let page = await this.deps.storagePort.listInventory();
|
|
14547
14692
|
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
@@ -14570,12 +14715,6 @@ var PaymentsFacade = class {
|
|
|
14570
14715
|
page = await this.deps.storagePort.listInventory(page.cursor);
|
|
14571
14716
|
}
|
|
14572
14717
|
}
|
|
14573
|
-
toUiToken(tokenId, coinId, amount) {
|
|
14574
|
-
const now = this.nowMs();
|
|
14575
|
-
const snapshot = { assets: [], createdAt: now, updatedAt: now };
|
|
14576
|
-
const flags = { transferring: true, suspectedSpent: false };
|
|
14577
|
-
return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, this.deps.registry, flags);
|
|
14578
|
-
}
|
|
14579
14718
|
track(op) {
|
|
14580
14719
|
const settled = op.then(() => void 0, () => void 0);
|
|
14581
14720
|
this.pendingOps.add(settled);
|