@unicitylabs/sphere-sdk 0.17.0 → 0.17.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.
@@ -11888,6 +11888,9 @@ function createMachineStores(kv) {
11888
11888
  }
11889
11889
 
11890
11890
  // modules/payments-v2/machine/payload-view.ts
11891
+ function isWholeIntent(p) {
11892
+ return p.kind === "whole" || p.kind === "coinless";
11893
+ }
11891
11894
  function splitOf(p) {
11892
11895
  return p.kind === "coin" ? p.split : void 0;
11893
11896
  }
@@ -12256,7 +12259,14 @@ var TransferMachine = class {
12256
12259
  try {
12257
12260
  const coinId = coinIdOf(payload);
12258
12261
  const committedAmount = coinId === void 0 ? "0" : await this.settledAmount(engine, coinId, committed);
12259
- await this.deps.recordHistory?.({ transferId, payload, phase, committedAmount });
12262
+ const committedAssets = coinId === void 0 ? await this.settledAssets(engine, committed) : void 0;
12263
+ await this.deps.recordHistory?.({
12264
+ transferId,
12265
+ payload,
12266
+ phase,
12267
+ committedAmount,
12268
+ ...committedAssets !== void 0 ? { committedAssets } : {}
12269
+ });
12260
12270
  } catch {
12261
12271
  }
12262
12272
  try {
@@ -12270,6 +12280,18 @@ var TransferMachine = class {
12270
12280
  * a journal-rehydrated resume leg has no source token, but every committed
12271
12281
  * leg carries its recipient blob, so the outcome-derived rule is uniform).
12272
12282
  */
12283
+ /** Every asset across the certified recipient blobs — the whole-spend twin of settledAmount. */
12284
+ async settledAssets(engine, committed) {
12285
+ const total = /* @__PURE__ */ new Map();
12286
+ for (const outcome of committed) {
12287
+ if (outcome.recipientBlob === void 0) continue;
12288
+ const token = await engine.decodeToken({ tokenId: "", token: outcome.recipientBlob });
12289
+ for (const asset of engine.readValue(token)?.assets ?? []) {
12290
+ total.set(asset.coinId, (total.get(asset.coinId) ?? 0n) + asset.amount);
12291
+ }
12292
+ }
12293
+ return [...total].map(([coinId, amount]) => ({ coinId, amount: amount.toString() }));
12294
+ }
12273
12295
  async settledAmount(engine, coinId, committed) {
12274
12296
  let total = 0n;
12275
12297
  for (const outcome of committed) {
@@ -12409,7 +12431,7 @@ async function runOne(ctx, job, report) {
12409
12431
  report.failed.push(job.transferId);
12410
12432
  return;
12411
12433
  }
12412
- if (job.payload.kind === "coinless" && !isCoinlessEnvelope(token.valueEnvelope)) {
12434
+ if (isWholeIntent(job.payload) && token.valueEnvelope === "bare_collection") {
12413
12435
  logger.warn(
12414
12436
  "PaymentsV2",
12415
12437
  `resume: token intent ${job.transferId} names a source carrying coin value \u2014 refusing`
@@ -12457,7 +12479,7 @@ function validateCoinPayload(c) {
12457
12479
  }
12458
12480
  return { ...c, kind: "coin" };
12459
12481
  }
12460
- function validateCoinlessPayload(p) {
12482
+ function validateWholePayload(p) {
12461
12483
  if (p.direct?.length !== 1 || typeof p.direct[0] !== "string" || p.direct[0] === "") {
12462
12484
  throw new SphereError(
12463
12485
  "token intent payload must name exactly one source token",
@@ -12467,7 +12489,7 @@ function validateCoinlessPayload(p) {
12467
12489
  if (p.split !== void 0) {
12468
12490
  throw new SphereError("token intent payload cannot carry a split", "VALIDATION_ERROR");
12469
12491
  }
12470
- return { ...p, kind: "coinless", direct: [p.direct[0]] };
12492
+ return { ...p, kind: "whole", direct: [p.direct[0]] };
12471
12493
  }
12472
12494
  function validatePayload(raw) {
12473
12495
  const p = raw !== null && typeof raw === "object" ? raw : null;
@@ -12481,13 +12503,13 @@ function validatePayload(raw) {
12481
12503
  throw new SphereError("intent payload is missing recipient", "VALIDATION_ERROR");
12482
12504
  }
12483
12505
  const kind = p.kind ?? "coin";
12484
- if (kind !== "coin" && kind !== "coinless") {
12506
+ if (kind !== "coin" && kind !== "whole" && kind !== "coinless") {
12485
12507
  throw new SphereError(
12486
12508
  `unsupported intent kind '${String(kind)}' \u2014 not resumable by this client`,
12487
12509
  "VALIDATION_ERROR"
12488
12510
  );
12489
12511
  }
12490
- return kind === "coinless" ? validateCoinlessPayload(p) : validateCoinPayload(p);
12512
+ return kind === "coin" ? validateCoinPayload(p) : validateWholePayload(p);
12491
12513
  }
12492
12514
 
12493
12515
  // modules/payments-v2/convergence.ts
@@ -12700,8 +12722,9 @@ async function openRow(entry, journal, decryptPayload) {
12700
12722
  };
12701
12723
  }
12702
12724
  function subject(payload) {
12703
- if (payload?.kind === "coinless") {
12704
- const tokenId = payload.direct?.[0];
12725
+ const rawKind = payload?.kind;
12726
+ if (rawKind === "whole" || rawKind === "coinless") {
12727
+ const tokenId = payload?.direct?.[0];
12705
12728
  return { coinId: "", amount: "", ...typeof tokenId === "string" ? { tokenId } : {} };
12706
12729
  }
12707
12730
  const coin = payload;
@@ -12774,10 +12797,10 @@ function buildPayload(recipientPubkey, request, spend, spentStates) {
12774
12797
  function messageOf(err) {
12775
12798
  return err instanceof Error ? err.message : String(err);
12776
12799
  }
12777
- function buildCoinlessPayload(recipientPubkey, request, spentStates) {
12800
+ function buildWholePayload(recipientPubkey, request, spentStates) {
12778
12801
  return {
12779
12802
  v: 2,
12780
- kind: "coinless",
12803
+ kind: "whole",
12781
12804
  recipient: recipientPubkey,
12782
12805
  ...request.memo !== void 0 ? { memo: request.memo } : {},
12783
12806
  direct: [request.tokenId],
@@ -12846,8 +12869,102 @@ async function finalizeMint(deps, mintId, token, coinId, amount) {
12846
12869
  deps.refreshView();
12847
12870
  }
12848
12871
 
12849
- // modules/payments-v2/send-coinless.ts
12850
- async function materializeCoinlessSpend(deps, input) {
12872
+ // modules/payments-v2/inventory/presentation.ts
12873
+ function toToken(tokenId, snapshot, asset, registry, flags) {
12874
+ const iconUrl = registry.getIconUrl(asset.coinId);
12875
+ return {
12876
+ id: tokenId,
12877
+ coinId: asset.coinId,
12878
+ symbol: registry.getSymbol(asset.coinId),
12879
+ name: registry.getName(asset.coinId),
12880
+ decimals: registry.getDecimals(asset.coinId),
12881
+ ...iconUrl !== null ? { iconUrl } : {},
12882
+ amount: asset.amount,
12883
+ status: flags.transferring ? "transferring" : "confirmed",
12884
+ createdAt: snapshot.createdAt,
12885
+ updatedAt: snapshot.updatedAt,
12886
+ lazy: true,
12887
+ ...flags.suspectedSpent ? { suspectedSpent: true } : {}
12888
+ };
12889
+ }
12890
+ function aggregateAssets(entries, isTransferring, registry) {
12891
+ const groups = /* @__PURE__ */ new Map();
12892
+ for (const [tokenId, entry] of entries) {
12893
+ const moving = isTransferring(tokenId);
12894
+ for (const asset of entry.assets) {
12895
+ let group = groups.get(asset.coinId);
12896
+ if (!group) {
12897
+ group = { total: 0n, count: 0, transferring: 0n, transferringCount: 0 };
12898
+ groups.set(asset.coinId, group);
12899
+ }
12900
+ if (moving) {
12901
+ group.transferring += BigInt(asset.amount);
12902
+ group.transferringCount += 1;
12903
+ } else {
12904
+ group.total += BigInt(asset.amount);
12905
+ group.count += 1;
12906
+ }
12907
+ }
12908
+ }
12909
+ return [...groups].map(([coinId, totals]) => toAsset(coinId, totals, registry));
12910
+ }
12911
+ function toAsset(coinId, totals, registry) {
12912
+ const iconUrl = registry.getIconUrl(coinId);
12913
+ return {
12914
+ coinId,
12915
+ symbol: registry.getSymbol(coinId),
12916
+ name: registry.getName(coinId),
12917
+ decimals: registry.getDecimals(coinId),
12918
+ ...iconUrl !== null ? { iconUrl } : {},
12919
+ totalAmount: totals.total.toString(),
12920
+ tokenCount: totals.count,
12921
+ confirmedAmount: totals.total.toString(),
12922
+ unconfirmedAmount: "0",
12923
+ confirmedTokenCount: totals.count,
12924
+ unconfirmedTokenCount: 0,
12925
+ transferringTokenCount: totals.transferringCount,
12926
+ transferringAmount: totals.transferring.toString(),
12927
+ priceUsd: null,
12928
+ priceEur: null,
12929
+ change24h: null,
12930
+ fiatValueUsd: null,
12931
+ fiatValueEur: null
12932
+ };
12933
+ }
12934
+ function priceIdOf(registry, coinId) {
12935
+ return registry.getName(coinId).toLowerCase();
12936
+ }
12937
+ async function withPrices(raw, registry, price) {
12938
+ try {
12939
+ const ids = [...new Set(raw.map((a) => priceIdOf(registry, a.coinId)))];
12940
+ const quotes = await price.getPrices(ids);
12941
+ return raw.map((a) => priceOne(a, quotes.get(priceIdOf(registry, a.coinId))));
12942
+ } catch {
12943
+ return raw;
12944
+ }
12945
+ }
12946
+ function priceOne(asset, quote) {
12947
+ if (!quote) return asset;
12948
+ const human = Number(asset.totalAmount) / Math.pow(10, asset.decimals);
12949
+ return {
12950
+ ...asset,
12951
+ priceUsd: quote.priceUsd,
12952
+ priceEur: quote.priceEur ?? null,
12953
+ change24h: quote.change24h ?? null,
12954
+ fiatValueUsd: human * quote.priceUsd,
12955
+ fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
12956
+ };
12957
+ }
12958
+ function transferringToken(tokenId, coinId, amount, registry, now) {
12959
+ const snapshot = { assets: [], createdAt: now, updatedAt: now };
12960
+ return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
12961
+ transferring: true,
12962
+ suspectedSpent: false
12963
+ });
12964
+ }
12965
+
12966
+ // modules/payments-v2/send-whole.ts
12967
+ async function materializeWholeSpend(deps, input) {
12851
12968
  const { engine } = deps;
12852
12969
  const tokenId = input.request.tokenId;
12853
12970
  const bytes = (await deps.storagePort.getBlobs([tokenId])).get(tokenId);
@@ -12855,14 +12972,20 @@ async function materializeCoinlessSpend(deps, input) {
12855
12972
  throw new SphereError(`Selected source ${tokenId} has no blob in storage`, "STORAGE_ERROR");
12856
12973
  }
12857
12974
  const token = await engine.decodeToken({ tokenId, token: bytes });
12858
- if (!isCoinlessEnvelope(token.valueEnvelope)) {
12975
+ if (token.valueEnvelope === "bare_collection") {
12976
+ throw new SphereError(
12977
+ `Token ${tokenId} carries a value envelope this SDK cannot read, so its coins cannot be accounted for; it cannot be sent`,
12978
+ "VALIDATION_ERROR"
12979
+ );
12980
+ }
12981
+ if (input.requireCoinless && token.value !== null) {
12859
12982
  throw new SphereError(
12860
- `Token ${tokenId} carries coin value and cannot be sent with sendCoinless \u2014 use send()`,
12983
+ `Token ${tokenId} carries coin value and cannot be sent with sendCoinless \u2014 use sendWholeToken, which requires coin-transfer authority`,
12861
12984
  "VALIDATION_ERROR"
12862
12985
  );
12863
12986
  }
12864
12987
  const keys = await engine.deliveryKeys(bytes);
12865
- const payload = buildCoinlessPayload(input.recipientPubkey, input.request, {
12988
+ const payload = buildWholePayload(input.recipientPubkey, input.request, {
12866
12989
  [tokenId]: { local: keys.stateHash, protocol: keys.stateHash }
12867
12990
  });
12868
12991
  const plan = {
@@ -12873,7 +12996,10 @@ async function materializeCoinlessSpend(deps, input) {
12873
12996
  sources: /* @__PURE__ */ new Map([[tokenId, token]]),
12874
12997
  ...input.request.memo !== void 0 ? { memo: input.request.memo } : {}
12875
12998
  };
12876
- return { transferId: input.transferId, coinId: "", plan, sourceIds: input.sourceIds, sourceTokens: [] };
12999
+ const sourceTokens = (token.value?.assets ?? []).map(
13000
+ (asset) => transferringToken(tokenId, asset.coinId, asset.amount, deps.registry, deps.now)
13001
+ );
13002
+ return { transferId: input.transferId, coinId: "", plan, sourceIds: input.sourceIds, sourceTokens };
12877
13003
  }
12878
13004
 
12879
13005
  // modules/payments-v2/send-errors.ts
@@ -13020,100 +13146,6 @@ async function takeSourceBlobs(deps, sourceIds) {
13020
13146
  return blobs;
13021
13147
  }
13022
13148
 
13023
- // modules/payments-v2/inventory/presentation.ts
13024
- function toToken(tokenId, snapshot, asset, registry, flags) {
13025
- const iconUrl = registry.getIconUrl(asset.coinId);
13026
- return {
13027
- id: tokenId,
13028
- coinId: asset.coinId,
13029
- symbol: registry.getSymbol(asset.coinId),
13030
- name: registry.getName(asset.coinId),
13031
- decimals: registry.getDecimals(asset.coinId),
13032
- ...iconUrl !== null ? { iconUrl } : {},
13033
- amount: asset.amount,
13034
- status: flags.transferring ? "transferring" : "confirmed",
13035
- createdAt: snapshot.createdAt,
13036
- updatedAt: snapshot.updatedAt,
13037
- lazy: true,
13038
- ...flags.suspectedSpent ? { suspectedSpent: true } : {}
13039
- };
13040
- }
13041
- function aggregateAssets(entries, isTransferring, registry) {
13042
- const groups = /* @__PURE__ */ new Map();
13043
- for (const [tokenId, entry] of entries) {
13044
- const moving = isTransferring(tokenId);
13045
- for (const asset of entry.assets) {
13046
- let group = groups.get(asset.coinId);
13047
- if (!group) {
13048
- group = { total: 0n, count: 0, transferring: 0n, transferringCount: 0 };
13049
- groups.set(asset.coinId, group);
13050
- }
13051
- if (moving) {
13052
- group.transferring += BigInt(asset.amount);
13053
- group.transferringCount += 1;
13054
- } else {
13055
- group.total += BigInt(asset.amount);
13056
- group.count += 1;
13057
- }
13058
- }
13059
- }
13060
- return [...groups].map(([coinId, totals]) => toAsset(coinId, totals, registry));
13061
- }
13062
- function toAsset(coinId, totals, registry) {
13063
- const iconUrl = registry.getIconUrl(coinId);
13064
- return {
13065
- coinId,
13066
- symbol: registry.getSymbol(coinId),
13067
- name: registry.getName(coinId),
13068
- decimals: registry.getDecimals(coinId),
13069
- ...iconUrl !== null ? { iconUrl } : {},
13070
- totalAmount: totals.total.toString(),
13071
- tokenCount: totals.count,
13072
- confirmedAmount: totals.total.toString(),
13073
- unconfirmedAmount: "0",
13074
- confirmedTokenCount: totals.count,
13075
- unconfirmedTokenCount: 0,
13076
- transferringTokenCount: totals.transferringCount,
13077
- transferringAmount: totals.transferring.toString(),
13078
- priceUsd: null,
13079
- priceEur: null,
13080
- change24h: null,
13081
- fiatValueUsd: null,
13082
- fiatValueEur: null
13083
- };
13084
- }
13085
- function priceIdOf(registry, coinId) {
13086
- return registry.getName(coinId).toLowerCase();
13087
- }
13088
- async function withPrices(raw, registry, price) {
13089
- try {
13090
- const ids = [...new Set(raw.map((a) => priceIdOf(registry, a.coinId)))];
13091
- const quotes = await price.getPrices(ids);
13092
- return raw.map((a) => priceOne(a, quotes.get(priceIdOf(registry, a.coinId))));
13093
- } catch {
13094
- return raw;
13095
- }
13096
- }
13097
- function priceOne(asset, quote) {
13098
- if (!quote) return asset;
13099
- const human = Number(asset.totalAmount) / Math.pow(10, asset.decimals);
13100
- return {
13101
- ...asset,
13102
- priceUsd: quote.priceUsd,
13103
- priceEur: quote.priceEur ?? null,
13104
- change24h: quote.change24h ?? null,
13105
- fiatValueUsd: human * quote.priceUsd,
13106
- fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
13107
- };
13108
- }
13109
- function transferringToken(tokenId, coinId, amount, registry, now) {
13110
- const snapshot = { assets: [], createdAt: now, updatedAt: now };
13111
- return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
13112
- transferring: true,
13113
- suspectedSpent: false
13114
- });
13115
- }
13116
-
13117
13149
  // modules/payments-v2/history/History.ts
13118
13150
  function wireAssets(assets) {
13119
13151
  return assets.map((a) => ({ coinId: a.coinId, amount: a.amount }));
@@ -13369,10 +13401,10 @@ var InventoryView = class {
13369
13401
  if (this.inFlight.has(tokenId) && !this.pinned(tokenId)) return false;
13370
13402
  return !this.suspected.has(stateKey(tokenId, entry.stateHash));
13371
13403
  }
13372
- /** #777: is this NAMED token a spendable COINLESS holding? Never a coin source. */
13373
- spendableCoinless(tokenId) {
13404
+ /** Is this NAMED token spendable whole — coinless or valued (#777)? */
13405
+ spendableToken(tokenId) {
13374
13406
  const entry = this.mirror.get(tokenId);
13375
- return entry !== void 0 && entry.coinless && this.isSpendable(tokenId, entry);
13407
+ return entry !== void 0 && this.isSpendable(tokenId, entry);
13376
13408
  }
13377
13409
  /**
13378
13410
  * Current mirrored state of an ACTIVE token, or undefined when it is unknown
@@ -13745,7 +13777,7 @@ async function screen(deps, engine, entry) {
13745
13777
  tokenId: keys.tokenId,
13746
13778
  stateHash: keys.stateHash,
13747
13779
  assets: toAssetAmounts(token),
13748
- ...isCoinlessEnvelope2(token.valueEnvelope) ? { tokenType: token.tokenType } : {}
13780
+ ...isCoinlessEnvelope(token.valueEnvelope) ? { tokenType: token.tokenType } : {}
13749
13781
  }
13750
13782
  };
13751
13783
  }
@@ -13882,9 +13914,6 @@ function claimAck(entry) {
13882
13914
  function rejectAck(entry, reason) {
13883
13915
  return { deliveryId: entry.deliveryId, disposition: "rejected", reason, cursor: entry.cursor };
13884
13916
  }
13885
- function isCoinlessEnvelope2(envelope) {
13886
- return envelope.startsWith("none_");
13887
- }
13888
13917
  function toAssetAmounts(token) {
13889
13918
  return (token.value?.assets ?? []).map((asset) => ({
13890
13919
  coinId: asset.coinId,
@@ -14459,7 +14488,7 @@ var SpendQueue = class {
14459
14488
  * can release, so queueing would block until a timeout on a spend that cannot
14460
14489
  * become possible.
14461
14490
  */
14462
- planCoinless(reservationId, tokenId) {
14491
+ planWhole(reservationId, tokenId) {
14463
14492
  if (this.destroyed) {
14464
14493
  throw new SphereError("Module has been destroyed", "MODULE_DESTROYED");
14465
14494
  }
@@ -14467,11 +14496,8 @@ var SpendQueue = class {
14467
14496
  if (unproven !== null) {
14468
14497
  throw new SphereError(`Cannot spend yet: ${unproven}`, "SEND_SYNC_PENDING");
14469
14498
  }
14470
- if (this.deps.spendableCoinless?.(tokenId) !== true) {
14471
- throw new SphereError(
14472
- `Token ${tokenId} is not a spendable coinless holding`,
14473
- "VALIDATION_ERROR"
14474
- );
14499
+ if (this.deps.spendableToken?.(tokenId) !== true) {
14500
+ throw new SphereError(`Token ${tokenId} is not a spendable holding`, "VALIDATION_ERROR");
14475
14501
  }
14476
14502
  const holder = this.deps.ledger.holderOf(tokenId);
14477
14503
  if (holder !== void 0) {
@@ -14672,7 +14698,7 @@ function composeFacadeParts(deps, hooks) {
14672
14698
  const queue = new SpendQueue({
14673
14699
  ledger,
14674
14700
  getPool: (coinId) => view.pool(coinId),
14675
- spendableCoinless: (tokenId) => view.spendableCoinless(tokenId),
14701
+ spendableToken: (tokenId) => view.spendableToken(tokenId),
14676
14702
  ...deps.workBudget !== void 0 ? { workBudget: deps.workBudget } : {}
14677
14703
  });
14678
14704
  const historyStore = new History({
@@ -14753,12 +14779,12 @@ function buildMachineDeps(deps, hooks, historyStore) {
14753
14779
  ...deps.ownNametag !== void 0 ? { ownNametag: deps.ownNametag } : {},
14754
14780
  emit: deps.emit,
14755
14781
  now: deps.now ?? Date.now,
14756
- recordHistory: async ({ transferId, payload, committedAmount }) => {
14782
+ recordHistory: async ({ transferId, payload, committedAmount, committedAssets }) => {
14757
14783
  await historyStore.recordSent({
14758
14784
  transferId,
14759
14785
  // §5.9: the SETTLED amount, never payload.amount — the plan. A token
14760
14786
  // spend moved no coin: `assets: []` + tokenId (wallet-api#151 / §10).
14761
- ...payload.kind === "coinless" ? { assets: [], tokenId: payload.direct[0] } : { assets: [{ coinId: payload.coinId, amount: committedAmount }] },
14787
+ ...isWholeIntent(payload) ? { assets: committedAssets ?? [], tokenId: payload.direct[0] } : { assets: [{ coinId: payload.coinId, amount: committedAmount }] },
14762
14788
  recipientPubkey: payload.recipient,
14763
14789
  ...payload.memo !== void 0 ? { memo: payload.memo } : {}
14764
14790
  });
@@ -14973,10 +14999,14 @@ var PaymentsFacade = class {
14973
14999
  return { queue: this.queue, view: this.view, storagePort: this.deps.storagePort, cache: this.prewarmed };
14974
15000
  }
14975
15001
  send(request) {
14976
- return this.track(this.sendOutcome(request));
15002
+ return this.track(this.runJob({ kind: "coin", request }, request.amount));
15003
+ }
15004
+ sendWholeToken(request) {
15005
+ return this.track(this.runJob({ kind: "whole", request, requireCoinless: false }, "0"));
14977
15006
  }
15007
+ /** NFT-scoped twin: refuses a valued source. */
14978
15008
  sendCoinless(request) {
14979
- return this.track(this.sendCoinlessOutcome(request));
15009
+ return this.track(this.runJob({ kind: "whole", request, requireCoinless: true }, "0"));
14980
15010
  }
14981
15011
  async receive() {
14982
15012
  const transfers = await this.track(this.receiveLoop.drainOnce());
@@ -15024,13 +15054,6 @@ var PaymentsFacade = class {
15024
15054
  // ── send policy (§5.5 + old-loop parity; the machine stays policy-free) ────
15025
15055
  /** The ONE place a send() outcome is shaped: success emits in finishSend, a
15026
15056
  * CLEAN rejection emits `transfer:updated{status:'failed'}` here (§4). */
15027
- sendOutcome(request) {
15028
- return this.runJob({ kind: "coin", request }, request.amount);
15029
- }
15030
- /** A token spend has no amount; '0' keeps the shortfall arithmetic total-free. */
15031
- sendCoinlessOutcome(request) {
15032
- return this.runJob({ kind: "coinless", request }, "0");
15033
- }
15034
15057
  async runJob(job, amount) {
15035
15058
  const run = {
15036
15059
  amount,
@@ -15075,7 +15098,7 @@ var PaymentsFacade = class {
15075
15098
  const disposition = await this.runAttempt(ctx);
15076
15099
  if (disposition.kind === "rethrow") throw partialize(disposition.error, run);
15077
15100
  if (disposition.kind === "retry-full") {
15078
- if (job.kind === "coinless" || attempt >= MAX_RESELECT) throw partialize(disposition.error, run);
15101
+ if (job.kind === "whole" || attempt >= MAX_RESELECT) throw partialize(disposition.error, run);
15079
15102
  continue;
15080
15103
  }
15081
15104
  if (disposition.kind === "success") {
@@ -15179,13 +15202,19 @@ var PaymentsFacade = class {
15179
15202
  async planAndMaterialize(recipientPubkey, job, run) {
15180
15203
  const transferId = this.newId();
15181
15204
  run.lastTransferId = transferId;
15182
- if (job.kind === "coinless") {
15183
- const spend2 = this.queue.planCoinless(transferId, job.request.tokenId);
15205
+ if (job.kind === "whole") {
15206
+ const spend2 = this.queue.planWhole(transferId, job.request.tokenId);
15184
15207
  const sourceIds2 = this.markPlanned(transferId, "", spend2);
15185
15208
  try {
15186
- return await materializeCoinlessSpend(
15187
- { engine: this.engine(), storagePort: this.deps.storagePort },
15188
- { transferId, recipientPubkey, request: job.request, sourceIds: sourceIds2 }
15209
+ return await materializeWholeSpend(
15210
+ { engine: this.engine(), storagePort: this.deps.storagePort, registry: this.deps.registry, now: this.nowMs() },
15211
+ {
15212
+ transferId,
15213
+ recipientPubkey,
15214
+ request: job.request,
15215
+ sourceIds: sourceIds2,
15216
+ requireCoinless: job.requireCoinless
15217
+ }
15189
15218
  );
15190
15219
  } catch (err) {
15191
15220
  this.settleFailure(transferId, "", sourceIds2);