@unicitylabs/sphere-sdk 0.14.10 → 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 +139 -16
- 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 +139 -16
- 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 +139 -16
- 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 +139 -16
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +139 -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 +139 -16
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/package.json +1 -1
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
|
@@ -12289,6 +12289,90 @@ async function splitOpIndex(deps, envelope) {
|
|
|
12289
12289
|
return Array.isArray(payload.direct) ? payload.direct.length : 0;
|
|
12290
12290
|
}
|
|
12291
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
|
+
|
|
12292
12376
|
// modules/payments-v2/inventory/presentation.ts
|
|
12293
12377
|
function toToken(tokenId, snapshot, asset, registry, flags) {
|
|
12294
12378
|
const iconUrl = registry.getIconUrl(asset.coinId);
|
|
@@ -12375,6 +12459,13 @@ function priceOne(asset, quote) {
|
|
|
12375
12459
|
fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
|
|
12376
12460
|
};
|
|
12377
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
|
+
}
|
|
12378
12469
|
|
|
12379
12470
|
// modules/payments-v2/machine/payload.ts
|
|
12380
12471
|
function buildPayload(recipientPubkey, request, spend, spentStates) {
|
|
@@ -12613,6 +12704,15 @@ var InventoryView = class {
|
|
|
12613
12704
|
}
|
|
12614
12705
|
return out;
|
|
12615
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
|
+
}
|
|
12616
12716
|
tokens(registry, filter) {
|
|
12617
12717
|
const out = [];
|
|
12618
12718
|
for (const [tokenId, entry] of this.mirror) {
|
|
@@ -13532,6 +13632,31 @@ var SpendQueue = class {
|
|
|
13532
13632
|
}
|
|
13533
13633
|
return { kind: "queued", settled: this.enqueue(reservationId, request.coinId, amount) };
|
|
13534
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
|
+
}
|
|
13535
13660
|
// Coverage math: free + Σ declared expected change of in-flight sends —
|
|
13536
13661
|
// never the full amount of in-flight sources (§5.3).
|
|
13537
13662
|
declareExpectedChange(transferId, coinId, amount) {
|
|
@@ -13881,6 +14006,7 @@ var PaymentsFacade = class {
|
|
|
13881
14006
|
now: () => this.nowMs()
|
|
13882
14007
|
});
|
|
13883
14008
|
}
|
|
14009
|
+
prewarmed = new PrewarmCache();
|
|
13884
14010
|
view;
|
|
13885
14011
|
ledger;
|
|
13886
14012
|
queue;
|
|
@@ -13974,6 +14100,15 @@ var PaymentsFacade = class {
|
|
|
13974
14100
|
return derivePendingTransfers(this.machineStores, this.machineDeps.decryptPayload);
|
|
13975
14101
|
}
|
|
13976
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
|
+
}
|
|
13977
14112
|
send(request) {
|
|
13978
14113
|
return this.track(this.sendOutcome(request));
|
|
13979
14114
|
}
|
|
@@ -14183,7 +14318,7 @@ var PaymentsFacade = class {
|
|
|
14183
14318
|
}
|
|
14184
14319
|
async materialize(transferId, recipientPubkey, request, spend, sourceIds) {
|
|
14185
14320
|
const engine = this.engine();
|
|
14186
|
-
const blobs = await this.
|
|
14321
|
+
const blobs = await takeSourceBlobs(this.warmDeps(), sourceIds);
|
|
14187
14322
|
const sources = /* @__PURE__ */ new Map();
|
|
14188
14323
|
const sourceTokens = [];
|
|
14189
14324
|
const spentStates = {};
|
|
@@ -14196,7 +14331,7 @@ var PaymentsFacade = class {
|
|
|
14196
14331
|
const keys = await engine.deliveryKeys(bytes);
|
|
14197
14332
|
spentStates[tokenId] = { local: keys.stateHash, protocol: keys.stateHash };
|
|
14198
14333
|
sources.set(tokenId, token);
|
|
14199
|
-
sourceTokens.push(
|
|
14334
|
+
sourceTokens.push(transferringToken(tokenId, request.coinId, engine.balanceOf(token, request.coinId), this.deps.registry, this.nowMs()));
|
|
14200
14335
|
}
|
|
14201
14336
|
const payload = buildPayload(recipientPubkey, request, spend, spentStates);
|
|
14202
14337
|
const plan = {
|
|
@@ -14340,7 +14475,7 @@ var PaymentsFacade = class {
|
|
|
14340
14475
|
await this.machineStores.mintJournal.upsert(entry);
|
|
14341
14476
|
let token;
|
|
14342
14477
|
try {
|
|
14343
|
-
token = await engine.mint(this.
|
|
14478
|
+
token = await engine.mint(mintParams(this.ownPubkeyBytes, coinId, amount), { transferId: mintId });
|
|
14344
14479
|
} catch (err) {
|
|
14345
14480
|
this.heartbeat.arm();
|
|
14346
14481
|
return { success: false, error: messageOf(err) };
|
|
@@ -14399,7 +14534,7 @@ var PaymentsFacade = class {
|
|
|
14399
14534
|
});
|
|
14400
14535
|
return false;
|
|
14401
14536
|
}
|
|
14402
|
-
const token = await engine.mint(this.
|
|
14537
|
+
const token = await engine.mint(mintParams(this.ownPubkeyBytes, entry.coinId, BigInt(entry.amount)), {
|
|
14403
14538
|
transferId: entry.mintId
|
|
14404
14539
|
});
|
|
14405
14540
|
if (token.blob.tokenId !== entry.tokenId) {
|
|
@@ -14408,12 +14543,6 @@ var PaymentsFacade = class {
|
|
|
14408
14543
|
await this.finalizeMint(engine, entry.mintId, token, entry.coinId, entry.amount);
|
|
14409
14544
|
return true;
|
|
14410
14545
|
}
|
|
14411
|
-
mintParams(coinId, amount) {
|
|
14412
|
-
return {
|
|
14413
|
-
recipientPubkey: this.ownPubkeyBytes,
|
|
14414
|
-
value: { assets: [{ coinId, amount }] }
|
|
14415
|
-
};
|
|
14416
|
-
}
|
|
14417
14546
|
async tokenInServerInventory(tokenId) {
|
|
14418
14547
|
let page = await this.deps.storagePort.listInventory();
|
|
14419
14548
|
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
@@ -14442,12 +14571,6 @@ var PaymentsFacade = class {
|
|
|
14442
14571
|
page = await this.deps.storagePort.listInventory(page.cursor);
|
|
14443
14572
|
}
|
|
14444
14573
|
}
|
|
14445
|
-
toUiToken(tokenId, coinId, amount) {
|
|
14446
|
-
const now = this.nowMs();
|
|
14447
|
-
const snapshot = { assets: [], createdAt: now, updatedAt: now };
|
|
14448
|
-
const flags = { transferring: true, suspectedSpent: false };
|
|
14449
|
-
return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, this.deps.registry, flags);
|
|
14450
|
-
}
|
|
14451
14574
|
track(op) {
|
|
14452
14575
|
const settled = op.then(() => void 0, () => void 0);
|
|
14453
14576
|
this.pendingOps.add(settled);
|