@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.
package/dist/index.cjs CHANGED
@@ -11948,6 +11948,9 @@ function createMachineStores(kv) {
11948
11948
  }
11949
11949
 
11950
11950
  // modules/payments-v2/machine/payload-view.ts
11951
+ function isWholeIntent(p) {
11952
+ return p.kind === "whole" || p.kind === "coinless";
11953
+ }
11951
11954
  function splitOf(p) {
11952
11955
  return p.kind === "coin" ? p.split : void 0;
11953
11956
  }
@@ -12316,7 +12319,14 @@ var TransferMachine = class {
12316
12319
  try {
12317
12320
  const coinId = coinIdOf(payload);
12318
12321
  const committedAmount = coinId === void 0 ? "0" : await this.settledAmount(engine, coinId, committed);
12319
- await this.deps.recordHistory?.({ transferId, payload, phase, committedAmount });
12322
+ const committedAssets = coinId === void 0 ? await this.settledAssets(engine, committed) : void 0;
12323
+ await this.deps.recordHistory?.({
12324
+ transferId,
12325
+ payload,
12326
+ phase,
12327
+ committedAmount,
12328
+ ...committedAssets !== void 0 ? { committedAssets } : {}
12329
+ });
12320
12330
  } catch {
12321
12331
  }
12322
12332
  try {
@@ -12330,6 +12340,18 @@ var TransferMachine = class {
12330
12340
  * a journal-rehydrated resume leg has no source token, but every committed
12331
12341
  * leg carries its recipient blob, so the outcome-derived rule is uniform).
12332
12342
  */
12343
+ /** Every asset across the certified recipient blobs — the whole-spend twin of settledAmount. */
12344
+ async settledAssets(engine, committed) {
12345
+ const total = /* @__PURE__ */ new Map();
12346
+ for (const outcome of committed) {
12347
+ if (outcome.recipientBlob === void 0) continue;
12348
+ const token = await engine.decodeToken({ tokenId: "", token: outcome.recipientBlob });
12349
+ for (const asset of engine.readValue(token)?.assets ?? []) {
12350
+ total.set(asset.coinId, (total.get(asset.coinId) ?? 0n) + asset.amount);
12351
+ }
12352
+ }
12353
+ return [...total].map(([coinId, amount]) => ({ coinId, amount: amount.toString() }));
12354
+ }
12333
12355
  async settledAmount(engine, coinId, committed) {
12334
12356
  let total = 0n;
12335
12357
  for (const outcome of committed) {
@@ -12469,7 +12491,7 @@ async function runOne(ctx, job, report) {
12469
12491
  report.failed.push(job.transferId);
12470
12492
  return;
12471
12493
  }
12472
- if (job.payload.kind === "coinless" && !isCoinlessEnvelope(token.valueEnvelope)) {
12494
+ if (isWholeIntent(job.payload) && token.valueEnvelope === "bare_collection") {
12473
12495
  logger.warn(
12474
12496
  "PaymentsV2",
12475
12497
  `resume: token intent ${job.transferId} names a source carrying coin value \u2014 refusing`
@@ -12517,7 +12539,7 @@ function validateCoinPayload(c) {
12517
12539
  }
12518
12540
  return { ...c, kind: "coin" };
12519
12541
  }
12520
- function validateCoinlessPayload(p) {
12542
+ function validateWholePayload(p) {
12521
12543
  if (p.direct?.length !== 1 || typeof p.direct[0] !== "string" || p.direct[0] === "") {
12522
12544
  throw new SphereError(
12523
12545
  "token intent payload must name exactly one source token",
@@ -12527,7 +12549,7 @@ function validateCoinlessPayload(p) {
12527
12549
  if (p.split !== void 0) {
12528
12550
  throw new SphereError("token intent payload cannot carry a split", "VALIDATION_ERROR");
12529
12551
  }
12530
- return { ...p, kind: "coinless", direct: [p.direct[0]] };
12552
+ return { ...p, kind: "whole", direct: [p.direct[0]] };
12531
12553
  }
12532
12554
  function validatePayload(raw) {
12533
12555
  const p = raw !== null && typeof raw === "object" ? raw : null;
@@ -12541,13 +12563,13 @@ function validatePayload(raw) {
12541
12563
  throw new SphereError("intent payload is missing recipient", "VALIDATION_ERROR");
12542
12564
  }
12543
12565
  const kind = p.kind ?? "coin";
12544
- if (kind !== "coin" && kind !== "coinless") {
12566
+ if (kind !== "coin" && kind !== "whole" && kind !== "coinless") {
12545
12567
  throw new SphereError(
12546
12568
  `unsupported intent kind '${String(kind)}' \u2014 not resumable by this client`,
12547
12569
  "VALIDATION_ERROR"
12548
12570
  );
12549
12571
  }
12550
- return kind === "coinless" ? validateCoinlessPayload(p) : validateCoinPayload(p);
12572
+ return kind === "coin" ? validateCoinPayload(p) : validateWholePayload(p);
12551
12573
  }
12552
12574
 
12553
12575
  // modules/payments-v2/convergence.ts
@@ -12760,8 +12782,9 @@ async function openRow(entry, journal, decryptPayload) {
12760
12782
  };
12761
12783
  }
12762
12784
  function subject(payload) {
12763
- if (payload?.kind === "coinless") {
12764
- const tokenId = payload.direct?.[0];
12785
+ const rawKind = payload?.kind;
12786
+ if (rawKind === "whole" || rawKind === "coinless") {
12787
+ const tokenId = payload?.direct?.[0];
12765
12788
  return { coinId: "", amount: "", ...typeof tokenId === "string" ? { tokenId } : {} };
12766
12789
  }
12767
12790
  const coin = payload;
@@ -12834,10 +12857,10 @@ function buildPayload(recipientPubkey, request, spend, spentStates) {
12834
12857
  function messageOf(err) {
12835
12858
  return err instanceof Error ? err.message : String(err);
12836
12859
  }
12837
- function buildCoinlessPayload(recipientPubkey, request, spentStates) {
12860
+ function buildWholePayload(recipientPubkey, request, spentStates) {
12838
12861
  return {
12839
12862
  v: 2,
12840
- kind: "coinless",
12863
+ kind: "whole",
12841
12864
  recipient: recipientPubkey,
12842
12865
  ...request.memo !== void 0 ? { memo: request.memo } : {},
12843
12866
  direct: [request.tokenId],
@@ -12906,8 +12929,102 @@ async function finalizeMint(deps, mintId, token, coinId, amount) {
12906
12929
  deps.refreshView();
12907
12930
  }
12908
12931
 
12909
- // modules/payments-v2/send-coinless.ts
12910
- async function materializeCoinlessSpend(deps, input) {
12932
+ // modules/payments-v2/inventory/presentation.ts
12933
+ function toToken(tokenId, snapshot, asset, registry, flags) {
12934
+ const iconUrl = registry.getIconUrl(asset.coinId);
12935
+ return {
12936
+ id: tokenId,
12937
+ coinId: asset.coinId,
12938
+ symbol: registry.getSymbol(asset.coinId),
12939
+ name: registry.getName(asset.coinId),
12940
+ decimals: registry.getDecimals(asset.coinId),
12941
+ ...iconUrl !== null ? { iconUrl } : {},
12942
+ amount: asset.amount,
12943
+ status: flags.transferring ? "transferring" : "confirmed",
12944
+ createdAt: snapshot.createdAt,
12945
+ updatedAt: snapshot.updatedAt,
12946
+ lazy: true,
12947
+ ...flags.suspectedSpent ? { suspectedSpent: true } : {}
12948
+ };
12949
+ }
12950
+ function aggregateAssets(entries, isTransferring, registry) {
12951
+ const groups = /* @__PURE__ */ new Map();
12952
+ for (const [tokenId, entry] of entries) {
12953
+ const moving = isTransferring(tokenId);
12954
+ for (const asset of entry.assets) {
12955
+ let group = groups.get(asset.coinId);
12956
+ if (!group) {
12957
+ group = { total: 0n, count: 0, transferring: 0n, transferringCount: 0 };
12958
+ groups.set(asset.coinId, group);
12959
+ }
12960
+ if (moving) {
12961
+ group.transferring += BigInt(asset.amount);
12962
+ group.transferringCount += 1;
12963
+ } else {
12964
+ group.total += BigInt(asset.amount);
12965
+ group.count += 1;
12966
+ }
12967
+ }
12968
+ }
12969
+ return [...groups].map(([coinId, totals]) => toAsset(coinId, totals, registry));
12970
+ }
12971
+ function toAsset(coinId, totals, registry) {
12972
+ const iconUrl = registry.getIconUrl(coinId);
12973
+ return {
12974
+ coinId,
12975
+ symbol: registry.getSymbol(coinId),
12976
+ name: registry.getName(coinId),
12977
+ decimals: registry.getDecimals(coinId),
12978
+ ...iconUrl !== null ? { iconUrl } : {},
12979
+ totalAmount: totals.total.toString(),
12980
+ tokenCount: totals.count,
12981
+ confirmedAmount: totals.total.toString(),
12982
+ unconfirmedAmount: "0",
12983
+ confirmedTokenCount: totals.count,
12984
+ unconfirmedTokenCount: 0,
12985
+ transferringTokenCount: totals.transferringCount,
12986
+ transferringAmount: totals.transferring.toString(),
12987
+ priceUsd: null,
12988
+ priceEur: null,
12989
+ change24h: null,
12990
+ fiatValueUsd: null,
12991
+ fiatValueEur: null
12992
+ };
12993
+ }
12994
+ function priceIdOf(registry, coinId) {
12995
+ return registry.getName(coinId).toLowerCase();
12996
+ }
12997
+ async function withPrices(raw, registry, price) {
12998
+ try {
12999
+ const ids = [...new Set(raw.map((a) => priceIdOf(registry, a.coinId)))];
13000
+ const quotes = await price.getPrices(ids);
13001
+ return raw.map((a) => priceOne(a, quotes.get(priceIdOf(registry, a.coinId))));
13002
+ } catch {
13003
+ return raw;
13004
+ }
13005
+ }
13006
+ function priceOne(asset, quote) {
13007
+ if (!quote) return asset;
13008
+ const human = Number(asset.totalAmount) / Math.pow(10, asset.decimals);
13009
+ return {
13010
+ ...asset,
13011
+ priceUsd: quote.priceUsd,
13012
+ priceEur: quote.priceEur ?? null,
13013
+ change24h: quote.change24h ?? null,
13014
+ fiatValueUsd: human * quote.priceUsd,
13015
+ fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
13016
+ };
13017
+ }
13018
+ function transferringToken(tokenId, coinId, amount, registry, now) {
13019
+ const snapshot = { assets: [], createdAt: now, updatedAt: now };
13020
+ return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
13021
+ transferring: true,
13022
+ suspectedSpent: false
13023
+ });
13024
+ }
13025
+
13026
+ // modules/payments-v2/send-whole.ts
13027
+ async function materializeWholeSpend(deps, input) {
12911
13028
  const { engine } = deps;
12912
13029
  const tokenId = input.request.tokenId;
12913
13030
  const bytes = (await deps.storagePort.getBlobs([tokenId])).get(tokenId);
@@ -12915,14 +13032,20 @@ async function materializeCoinlessSpend(deps, input) {
12915
13032
  throw new SphereError(`Selected source ${tokenId} has no blob in storage`, "STORAGE_ERROR");
12916
13033
  }
12917
13034
  const token = await engine.decodeToken({ tokenId, token: bytes });
12918
- if (!isCoinlessEnvelope(token.valueEnvelope)) {
13035
+ if (token.valueEnvelope === "bare_collection") {
13036
+ throw new SphereError(
13037
+ `Token ${tokenId} carries a value envelope this SDK cannot read, so its coins cannot be accounted for; it cannot be sent`,
13038
+ "VALIDATION_ERROR"
13039
+ );
13040
+ }
13041
+ if (input.requireCoinless && token.value !== null) {
12919
13042
  throw new SphereError(
12920
- `Token ${tokenId} carries coin value and cannot be sent with sendCoinless \u2014 use send()`,
13043
+ `Token ${tokenId} carries coin value and cannot be sent with sendCoinless \u2014 use sendWholeToken, which requires coin-transfer authority`,
12921
13044
  "VALIDATION_ERROR"
12922
13045
  );
12923
13046
  }
12924
13047
  const keys = await engine.deliveryKeys(bytes);
12925
- const payload = buildCoinlessPayload(input.recipientPubkey, input.request, {
13048
+ const payload = buildWholePayload(input.recipientPubkey, input.request, {
12926
13049
  [tokenId]: { local: keys.stateHash, protocol: keys.stateHash }
12927
13050
  });
12928
13051
  const plan = {
@@ -12933,7 +13056,10 @@ async function materializeCoinlessSpend(deps, input) {
12933
13056
  sources: /* @__PURE__ */ new Map([[tokenId, token]]),
12934
13057
  ...input.request.memo !== void 0 ? { memo: input.request.memo } : {}
12935
13058
  };
12936
- return { transferId: input.transferId, coinId: "", plan, sourceIds: input.sourceIds, sourceTokens: [] };
13059
+ const sourceTokens = (token.value?.assets ?? []).map(
13060
+ (asset) => transferringToken(tokenId, asset.coinId, asset.amount, deps.registry, deps.now)
13061
+ );
13062
+ return { transferId: input.transferId, coinId: "", plan, sourceIds: input.sourceIds, sourceTokens };
12937
13063
  }
12938
13064
 
12939
13065
  // modules/payments-v2/send-errors.ts
@@ -13080,100 +13206,6 @@ async function takeSourceBlobs(deps, sourceIds) {
13080
13206
  return blobs;
13081
13207
  }
13082
13208
 
13083
- // modules/payments-v2/inventory/presentation.ts
13084
- function toToken(tokenId, snapshot, asset, registry, flags) {
13085
- const iconUrl = registry.getIconUrl(asset.coinId);
13086
- return {
13087
- id: tokenId,
13088
- coinId: asset.coinId,
13089
- symbol: registry.getSymbol(asset.coinId),
13090
- name: registry.getName(asset.coinId),
13091
- decimals: registry.getDecimals(asset.coinId),
13092
- ...iconUrl !== null ? { iconUrl } : {},
13093
- amount: asset.amount,
13094
- status: flags.transferring ? "transferring" : "confirmed",
13095
- createdAt: snapshot.createdAt,
13096
- updatedAt: snapshot.updatedAt,
13097
- lazy: true,
13098
- ...flags.suspectedSpent ? { suspectedSpent: true } : {}
13099
- };
13100
- }
13101
- function aggregateAssets(entries, isTransferring, registry) {
13102
- const groups = /* @__PURE__ */ new Map();
13103
- for (const [tokenId, entry] of entries) {
13104
- const moving = isTransferring(tokenId);
13105
- for (const asset of entry.assets) {
13106
- let group = groups.get(asset.coinId);
13107
- if (!group) {
13108
- group = { total: 0n, count: 0, transferring: 0n, transferringCount: 0 };
13109
- groups.set(asset.coinId, group);
13110
- }
13111
- if (moving) {
13112
- group.transferring += BigInt(asset.amount);
13113
- group.transferringCount += 1;
13114
- } else {
13115
- group.total += BigInt(asset.amount);
13116
- group.count += 1;
13117
- }
13118
- }
13119
- }
13120
- return [...groups].map(([coinId, totals]) => toAsset(coinId, totals, registry));
13121
- }
13122
- function toAsset(coinId, totals, registry) {
13123
- const iconUrl = registry.getIconUrl(coinId);
13124
- return {
13125
- coinId,
13126
- symbol: registry.getSymbol(coinId),
13127
- name: registry.getName(coinId),
13128
- decimals: registry.getDecimals(coinId),
13129
- ...iconUrl !== null ? { iconUrl } : {},
13130
- totalAmount: totals.total.toString(),
13131
- tokenCount: totals.count,
13132
- confirmedAmount: totals.total.toString(),
13133
- unconfirmedAmount: "0",
13134
- confirmedTokenCount: totals.count,
13135
- unconfirmedTokenCount: 0,
13136
- transferringTokenCount: totals.transferringCount,
13137
- transferringAmount: totals.transferring.toString(),
13138
- priceUsd: null,
13139
- priceEur: null,
13140
- change24h: null,
13141
- fiatValueUsd: null,
13142
- fiatValueEur: null
13143
- };
13144
- }
13145
- function priceIdOf(registry, coinId) {
13146
- return registry.getName(coinId).toLowerCase();
13147
- }
13148
- async function withPrices(raw, registry, price) {
13149
- try {
13150
- const ids = [...new Set(raw.map((a) => priceIdOf(registry, a.coinId)))];
13151
- const quotes = await price.getPrices(ids);
13152
- return raw.map((a) => priceOne(a, quotes.get(priceIdOf(registry, a.coinId))));
13153
- } catch {
13154
- return raw;
13155
- }
13156
- }
13157
- function priceOne(asset, quote) {
13158
- if (!quote) return asset;
13159
- const human = Number(asset.totalAmount) / Math.pow(10, asset.decimals);
13160
- return {
13161
- ...asset,
13162
- priceUsd: quote.priceUsd,
13163
- priceEur: quote.priceEur ?? null,
13164
- change24h: quote.change24h ?? null,
13165
- fiatValueUsd: human * quote.priceUsd,
13166
- fiatValueEur: quote.priceEur != null ? human * quote.priceEur : null
13167
- };
13168
- }
13169
- function transferringToken(tokenId, coinId, amount, registry, now) {
13170
- const snapshot = { assets: [], createdAt: now, updatedAt: now };
13171
- return toToken(tokenId, snapshot, { coinId, amount: amount.toString() }, registry, {
13172
- transferring: true,
13173
- suspectedSpent: false
13174
- });
13175
- }
13176
-
13177
13209
  // modules/payments-v2/history/History.ts
13178
13210
  function wireAssets(assets) {
13179
13211
  return assets.map((a) => ({ coinId: a.coinId, amount: a.amount }));
@@ -13429,10 +13461,10 @@ var InventoryView = class {
13429
13461
  if (this.inFlight.has(tokenId) && !this.pinned(tokenId)) return false;
13430
13462
  return !this.suspected.has(stateKey(tokenId, entry.stateHash));
13431
13463
  }
13432
- /** #777: is this NAMED token a spendable COINLESS holding? Never a coin source. */
13433
- spendableCoinless(tokenId) {
13464
+ /** Is this NAMED token spendable whole — coinless or valued (#777)? */
13465
+ spendableToken(tokenId) {
13434
13466
  const entry = this.mirror.get(tokenId);
13435
- return entry !== void 0 && entry.coinless && this.isSpendable(tokenId, entry);
13467
+ return entry !== void 0 && this.isSpendable(tokenId, entry);
13436
13468
  }
13437
13469
  /**
13438
13470
  * Current mirrored state of an ACTIVE token, or undefined when it is unknown
@@ -13805,7 +13837,7 @@ async function screen(deps, engine, entry) {
13805
13837
  tokenId: keys.tokenId,
13806
13838
  stateHash: keys.stateHash,
13807
13839
  assets: toAssetAmounts(token),
13808
- ...isCoinlessEnvelope2(token.valueEnvelope) ? { tokenType: token.tokenType } : {}
13840
+ ...isCoinlessEnvelope(token.valueEnvelope) ? { tokenType: token.tokenType } : {}
13809
13841
  }
13810
13842
  };
13811
13843
  }
@@ -13942,9 +13974,6 @@ function claimAck(entry) {
13942
13974
  function rejectAck(entry, reason) {
13943
13975
  return { deliveryId: entry.deliveryId, disposition: "rejected", reason, cursor: entry.cursor };
13944
13976
  }
13945
- function isCoinlessEnvelope2(envelope) {
13946
- return envelope.startsWith("none_");
13947
- }
13948
13977
  function toAssetAmounts(token) {
13949
13978
  return (token.value?.assets ?? []).map((asset) => ({
13950
13979
  coinId: asset.coinId,
@@ -14519,7 +14548,7 @@ var SpendQueue = class {
14519
14548
  * can release, so queueing would block until a timeout on a spend that cannot
14520
14549
  * become possible.
14521
14550
  */
14522
- planCoinless(reservationId, tokenId) {
14551
+ planWhole(reservationId, tokenId) {
14523
14552
  if (this.destroyed) {
14524
14553
  throw new SphereError("Module has been destroyed", "MODULE_DESTROYED");
14525
14554
  }
@@ -14527,11 +14556,8 @@ var SpendQueue = class {
14527
14556
  if (unproven !== null) {
14528
14557
  throw new SphereError(`Cannot spend yet: ${unproven}`, "SEND_SYNC_PENDING");
14529
14558
  }
14530
- if (this.deps.spendableCoinless?.(tokenId) !== true) {
14531
- throw new SphereError(
14532
- `Token ${tokenId} is not a spendable coinless holding`,
14533
- "VALIDATION_ERROR"
14534
- );
14559
+ if (this.deps.spendableToken?.(tokenId) !== true) {
14560
+ throw new SphereError(`Token ${tokenId} is not a spendable holding`, "VALIDATION_ERROR");
14535
14561
  }
14536
14562
  const holder = this.deps.ledger.holderOf(tokenId);
14537
14563
  if (holder !== void 0) {
@@ -14732,7 +14758,7 @@ function composeFacadeParts(deps, hooks) {
14732
14758
  const queue = new SpendQueue({
14733
14759
  ledger,
14734
14760
  getPool: (coinId) => view.pool(coinId),
14735
- spendableCoinless: (tokenId) => view.spendableCoinless(tokenId),
14761
+ spendableToken: (tokenId) => view.spendableToken(tokenId),
14736
14762
  ...deps.workBudget !== void 0 ? { workBudget: deps.workBudget } : {}
14737
14763
  });
14738
14764
  const historyStore = new History({
@@ -14813,12 +14839,12 @@ function buildMachineDeps(deps, hooks, historyStore) {
14813
14839
  ...deps.ownNametag !== void 0 ? { ownNametag: deps.ownNametag } : {},
14814
14840
  emit: deps.emit,
14815
14841
  now: deps.now ?? Date.now,
14816
- recordHistory: async ({ transferId, payload, committedAmount }) => {
14842
+ recordHistory: async ({ transferId, payload, committedAmount, committedAssets }) => {
14817
14843
  await historyStore.recordSent({
14818
14844
  transferId,
14819
14845
  // §5.9: the SETTLED amount, never payload.amount — the plan. A token
14820
14846
  // spend moved no coin: `assets: []` + tokenId (wallet-api#151 / §10).
14821
- ...payload.kind === "coinless" ? { assets: [], tokenId: payload.direct[0] } : { assets: [{ coinId: payload.coinId, amount: committedAmount }] },
14847
+ ...isWholeIntent(payload) ? { assets: committedAssets ?? [], tokenId: payload.direct[0] } : { assets: [{ coinId: payload.coinId, amount: committedAmount }] },
14822
14848
  recipientPubkey: payload.recipient,
14823
14849
  ...payload.memo !== void 0 ? { memo: payload.memo } : {}
14824
14850
  });
@@ -15033,10 +15059,14 @@ var PaymentsFacade = class {
15033
15059
  return { queue: this.queue, view: this.view, storagePort: this.deps.storagePort, cache: this.prewarmed };
15034
15060
  }
15035
15061
  send(request) {
15036
- return this.track(this.sendOutcome(request));
15062
+ return this.track(this.runJob({ kind: "coin", request }, request.amount));
15063
+ }
15064
+ sendWholeToken(request) {
15065
+ return this.track(this.runJob({ kind: "whole", request, requireCoinless: false }, "0"));
15037
15066
  }
15067
+ /** NFT-scoped twin: refuses a valued source. */
15038
15068
  sendCoinless(request) {
15039
- return this.track(this.sendCoinlessOutcome(request));
15069
+ return this.track(this.runJob({ kind: "whole", request, requireCoinless: true }, "0"));
15040
15070
  }
15041
15071
  async receive() {
15042
15072
  const transfers = await this.track(this.receiveLoop.drainOnce());
@@ -15084,13 +15114,6 @@ var PaymentsFacade = class {
15084
15114
  // ── send policy (§5.5 + old-loop parity; the machine stays policy-free) ────
15085
15115
  /** The ONE place a send() outcome is shaped: success emits in finishSend, a
15086
15116
  * CLEAN rejection emits `transfer:updated{status:'failed'}` here (§4). */
15087
- sendOutcome(request) {
15088
- return this.runJob({ kind: "coin", request }, request.amount);
15089
- }
15090
- /** A token spend has no amount; '0' keeps the shortfall arithmetic total-free. */
15091
- sendCoinlessOutcome(request) {
15092
- return this.runJob({ kind: "coinless", request }, "0");
15093
- }
15094
15117
  async runJob(job, amount) {
15095
15118
  const run = {
15096
15119
  amount,
@@ -15135,7 +15158,7 @@ var PaymentsFacade = class {
15135
15158
  const disposition = await this.runAttempt(ctx);
15136
15159
  if (disposition.kind === "rethrow") throw partialize(disposition.error, run);
15137
15160
  if (disposition.kind === "retry-full") {
15138
- if (job.kind === "coinless" || attempt >= MAX_RESELECT) throw partialize(disposition.error, run);
15161
+ if (job.kind === "whole" || attempt >= MAX_RESELECT) throw partialize(disposition.error, run);
15139
15162
  continue;
15140
15163
  }
15141
15164
  if (disposition.kind === "success") {
@@ -15239,13 +15262,19 @@ var PaymentsFacade = class {
15239
15262
  async planAndMaterialize(recipientPubkey, job, run) {
15240
15263
  const transferId = this.newId();
15241
15264
  run.lastTransferId = transferId;
15242
- if (job.kind === "coinless") {
15243
- const spend2 = this.queue.planCoinless(transferId, job.request.tokenId);
15265
+ if (job.kind === "whole") {
15266
+ const spend2 = this.queue.planWhole(transferId, job.request.tokenId);
15244
15267
  const sourceIds2 = this.markPlanned(transferId, "", spend2);
15245
15268
  try {
15246
- return await materializeCoinlessSpend(
15247
- { engine: this.engine(), storagePort: this.deps.storagePort },
15248
- { transferId, recipientPubkey, request: job.request, sourceIds: sourceIds2 }
15269
+ return await materializeWholeSpend(
15270
+ { engine: this.engine(), storagePort: this.deps.storagePort, registry: this.deps.registry, now: this.nowMs() },
15271
+ {
15272
+ transferId,
15273
+ recipientPubkey,
15274
+ request: job.request,
15275
+ sourceIds: sourceIds2,
15276
+ requireCoinless: job.requireCoinless
15277
+ }
15249
15278
  );
15250
15279
  } catch (err) {
15251
15280
  this.settleFailure(transferId, "", sourceIds2);