@unicitylabs/sphere-sdk 0.17.6 → 0.18.0-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/chunks/{chunk-T4DZCXWS.js → chunk-FGQ6CKKK.js} +2 -2
- package/dist/connect/chunks/{chunk-T4DZCXWS.js.map → chunk-FGQ6CKKK.js.map} +1 -1
- package/dist/connect/chunks/{chunk-BRYRG2TL.js → chunk-JYBI36GK.js} +2 -2
- package/dist/connect/chunks/{chunk-MA32MTCD.js → chunk-OAAY2JEA.js} +2 -2
- package/dist/connect/index.cjs +1 -1
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +3 -3
- package/dist/connect/internal/host.js +2 -2
- package/dist/core/index.cjs +304 -47
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +392 -320
- package/dist/core/index.d.ts +392 -320
- package/dist/core/index.js +304 -47
- 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 +2 -2
- package/dist/impl/wallet-api-v2/index.cjs +2 -0
- package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
- package/dist/impl/wallet-api-v2/index.d.cts +30 -30
- package/dist/impl/wallet-api-v2/index.d.ts +30 -30
- package/dist/impl/wallet-api-v2/index.js +2 -0
- package/dist/impl/wallet-api-v2/index.js.map +1 -1
- package/dist/index.cjs +304 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +279 -154
- package/dist/index.d.ts +279 -154
- package/dist/index.js +304 -47
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +249 -28
- package/dist/modules/payments-v2/index.cjs.map +1 -1
- package/dist/modules/payments-v2/index.d.cts +252 -166
- package/dist/modules/payments-v2/index.d.ts +252 -166
- package/dist/modules/payments-v2/index.js +249 -28
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/dist/token-engine/index.cjs +455 -427
- package/dist/token-engine/index.cjs.map +1 -1
- package/dist/token-engine/index.d.cts +26 -1
- package/dist/token-engine/index.d.ts +26 -1
- package/dist/token-engine/index.js +455 -427
- package/dist/token-engine/index.js.map +1 -1
- package/package.json +1 -1
- /package/dist/connect/chunks/{chunk-BRYRG2TL.js.map → chunk-JYBI36GK.js.map} +0 -0
- /package/dist/connect/chunks/{chunk-MA32MTCD.js.map → chunk-OAAY2JEA.js.map} +0 -0
|
@@ -2036,6 +2036,8 @@ var STORE_KEYS = {
|
|
|
2036
2036
|
deliveryJournal: "delivery-journal",
|
|
2037
2037
|
mintJournal: "mint-journal",
|
|
2038
2038
|
nftMintJournal: "nft-mint-journal",
|
|
2039
|
+
customMintJournal: "custom-mint-journal",
|
|
2040
|
+
burnJournal: "burn-journal",
|
|
2039
2041
|
shortfalls: "shortfalls",
|
|
2040
2042
|
settlingLinks: "settling",
|
|
2041
2043
|
streamCursor: (s) => `cursor:${s}`,
|
|
@@ -3301,6 +3303,8 @@ function createMachineStores(kv) {
|
|
|
3301
3303
|
deliveryJournal: new DeliveryJournal(kv),
|
|
3302
3304
|
mintJournal: new ListStore(kv, STORE_KEYS.mintJournal, (e) => e.mintId),
|
|
3303
3305
|
nftMintJournal: new ListStore(kv, STORE_KEYS.nftMintJournal, (e) => e.mintId),
|
|
3306
|
+
customMintJournal: new ListStore(kv, STORE_KEYS.customMintJournal, (e) => e.mintId),
|
|
3307
|
+
burnJournal: new ListStore(kv, STORE_KEYS.burnJournal, (e) => e.burnId),
|
|
3304
3308
|
shortfalls: new ListStore(kv, STORE_KEYS.shortfalls, (e) => e.transferId)
|
|
3305
3309
|
};
|
|
3306
3310
|
}
|
|
@@ -4036,7 +4040,12 @@ var Converger = class {
|
|
|
4036
4040
|
} catch (err) {
|
|
4037
4041
|
retryAfterMs ??= retryAfterMsOf(err);
|
|
4038
4042
|
}
|
|
4039
|
-
for (const replay of [
|
|
4043
|
+
for (const replay of [
|
|
4044
|
+
() => this.deps.replayMints(),
|
|
4045
|
+
() => this.deps.replayNftMints(),
|
|
4046
|
+
() => this.deps.replayCustomMints(),
|
|
4047
|
+
() => this.deps.replayBurns()
|
|
4048
|
+
]) {
|
|
4040
4049
|
try {
|
|
4041
4050
|
progress = await replay() > 0 || progress;
|
|
4042
4051
|
} catch (err) {
|
|
@@ -4047,14 +4056,17 @@ var Converger = class {
|
|
|
4047
4056
|
}
|
|
4048
4057
|
/** §4 heartbeat predicate — read fresh from the §6 stores, never cached. */
|
|
4049
4058
|
async pendingWork() {
|
|
4050
|
-
const [backstop, journal, mints, nftMints] = await Promise.all([
|
|
4059
|
+
const [backstop, journal, mints, nftMints, customMints, burns] = await Promise.all([
|
|
4051
4060
|
this.deps.stores.backstop.list(),
|
|
4052
4061
|
this.deps.stores.deliveryJournal.list(),
|
|
4053
4062
|
this.deps.stores.mintJournal.list(),
|
|
4054
|
-
this.deps.stores.nftMintJournal.list()
|
|
4063
|
+
this.deps.stores.nftMintJournal.list(),
|
|
4064
|
+
this.deps.stores.customMintJournal.list(),
|
|
4065
|
+
this.deps.stores.burnJournal.list()
|
|
4055
4066
|
]);
|
|
4056
|
-
const mintsOpen = mints.length > 0 || nftMints.length > 0;
|
|
4057
|
-
|
|
4067
|
+
const mintsOpen = mints.length > 0 || nftMints.length > 0 || customMints.length > 0;
|
|
4068
|
+
const burnsOpen = burns.some((e) => !e.settled);
|
|
4069
|
+
return backstop.length > 0 || journal.some((e) => e.undeliverable !== true) || mintsOpen || burnsOpen;
|
|
4058
4070
|
}
|
|
4059
4071
|
async resumePass() {
|
|
4060
4072
|
const report = await resumeAll(this.filteredMachineDeps(), {
|
|
@@ -4802,6 +4814,7 @@ function mintParams(recipientPubkey, coinId, amount) {
|
|
|
4802
4814
|
|
|
4803
4815
|
// modules/payments-v2/mint.ts
|
|
4804
4816
|
var ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
4817
|
+
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
4805
4818
|
function runMintUnderJournal(deps, input) {
|
|
4806
4819
|
return mintUnderJournal(deps, input);
|
|
4807
4820
|
}
|
|
@@ -4854,6 +4867,15 @@ async function finalizeMint(deps, input) {
|
|
|
4854
4867
|
deps.noteHeldState(token.blob.tokenId, (await deps.engine.deliveryKeys(bytes)).stateHash);
|
|
4855
4868
|
deps.refreshView();
|
|
4856
4869
|
}
|
|
4870
|
+
async function tokenInServerInventory(storagePort, tokenId) {
|
|
4871
|
+
let page = await storagePort.listInventory();
|
|
4872
|
+
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
4873
|
+
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) return true;
|
|
4874
|
+
if (!page.more) return false;
|
|
4875
|
+
page = await storagePort.listInventory(page.cursor);
|
|
4876
|
+
}
|
|
4877
|
+
return false;
|
|
4878
|
+
}
|
|
4857
4879
|
async function replayCoinMints(deps) {
|
|
4858
4880
|
let resolved = 0;
|
|
4859
4881
|
for (const entry of await deps.mintJournal.list()) {
|
|
@@ -5031,6 +5053,12 @@ async function decodeView(deps, tokenId, bytes) {
|
|
|
5031
5053
|
|
|
5032
5054
|
// modules/payments-v2/inventory/token-data.ts
|
|
5033
5055
|
async function readTokenData(deps, tokenId) {
|
|
5056
|
+
return deps.engine.readTokenData(await heldToken(deps, tokenId));
|
|
5057
|
+
}
|
|
5058
|
+
async function readTokenJustification(deps, tokenId) {
|
|
5059
|
+
return deps.engine.readTokenJustification(await heldToken(deps, tokenId));
|
|
5060
|
+
}
|
|
5061
|
+
async function heldToken(deps, tokenId) {
|
|
5034
5062
|
if (deps.view.stateHashOf(tokenId) === void 0) {
|
|
5035
5063
|
throw new SphereError(`Token ${tokenId} is not in inventory`, "VALIDATION_ERROR");
|
|
5036
5064
|
}
|
|
@@ -5038,7 +5066,200 @@ async function readTokenData(deps, tokenId) {
|
|
|
5038
5066
|
if (bytes === void 0) {
|
|
5039
5067
|
throw new SphereError(`Token ${tokenId} has no blob in storage`, "STORAGE_ERROR");
|
|
5040
5068
|
}
|
|
5041
|
-
return deps.engine.
|
|
5069
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
5070
|
+
}
|
|
5071
|
+
|
|
5072
|
+
// modules/payments-v2/mint-custom.ts
|
|
5073
|
+
var HEX32 = /^[0-9a-f]{64}$/;
|
|
5074
|
+
function assertRequest(request) {
|
|
5075
|
+
if (request.tokenType.length !== 32) {
|
|
5076
|
+
throw new SphereError("mintCustom: tokenType must be 32 bytes", "VALIDATION_ERROR");
|
|
5077
|
+
}
|
|
5078
|
+
if (request.salt.length < 16 || request.salt.length > 64) {
|
|
5079
|
+
throw new SphereError("mintCustom: salt must be 16 to 64 bytes", "VALIDATION_ERROR");
|
|
5080
|
+
}
|
|
5081
|
+
if (request.data.length === 0) {
|
|
5082
|
+
throw new SphereError("mintCustom: data must not be empty", "VALIDATION_ERROR");
|
|
5083
|
+
}
|
|
5084
|
+
for (const asset of request.assets) {
|
|
5085
|
+
if (!HEX32.test(asset.coinId) || asset.amount <= 0n) {
|
|
5086
|
+
throw new SphereError("mintCustom: assets need a 64-hex coinId and a positive amount", "VALIDATION_ERROR");
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
}
|
|
5090
|
+
async function runCustomMintUnderJournal(deps, input) {
|
|
5091
|
+
try {
|
|
5092
|
+
assertRequest(input.request);
|
|
5093
|
+
} catch (err) {
|
|
5094
|
+
return { success: false, error: messageOf(err) };
|
|
5095
|
+
}
|
|
5096
|
+
const entry = journalEntryOf2(input.mintId, input.request, deps.now());
|
|
5097
|
+
await deps.customMintJournal.upsert(entry);
|
|
5098
|
+
let tokenId;
|
|
5099
|
+
try {
|
|
5100
|
+
tokenId = await mintJournaled2(deps, entry, input.request.mintJustificationVerifiers);
|
|
5101
|
+
} catch (err) {
|
|
5102
|
+
deps.armHeartbeat();
|
|
5103
|
+
return { success: false, error: messageOf(err) };
|
|
5104
|
+
}
|
|
5105
|
+
return { success: true, tokenId };
|
|
5106
|
+
}
|
|
5107
|
+
async function replayCustomMints(deps) {
|
|
5108
|
+
let resolved = 0;
|
|
5109
|
+
for (const entry of await deps.customMintJournal.list()) {
|
|
5110
|
+
if (deps.isActiveOp(entry.mintId)) continue;
|
|
5111
|
+
try {
|
|
5112
|
+
if (entry.tokenId !== "" && await deps.tokenInServerInventory(entry.tokenId)) {
|
|
5113
|
+
await deps.customMintJournal.removeByKey(entry.mintId);
|
|
5114
|
+
} else {
|
|
5115
|
+
await mintJournaled2(deps, entry, void 0);
|
|
5116
|
+
}
|
|
5117
|
+
resolved += 1;
|
|
5118
|
+
} catch {
|
|
5119
|
+
continue;
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
return resolved;
|
|
5123
|
+
}
|
|
5124
|
+
function journalEntryOf2(mintId, request, createdAt) {
|
|
5125
|
+
return {
|
|
5126
|
+
mintId,
|
|
5127
|
+
tokenId: "",
|
|
5128
|
+
dataHex: bytesToHex(request.data),
|
|
5129
|
+
saltHex: bytesToHex(request.salt),
|
|
5130
|
+
tokenTypeHex: bytesToHex(request.tokenType),
|
|
5131
|
+
justificationHex: request.justification ? bytesToHex(request.justification) : null,
|
|
5132
|
+
assets: request.assets.map((a) => ({ coinId: a.coinId, amount: a.amount.toString() })),
|
|
5133
|
+
createdAt
|
|
5134
|
+
};
|
|
5135
|
+
}
|
|
5136
|
+
async function mintJournaled2(deps, entry, mintJustificationVerifiers) {
|
|
5137
|
+
const token = await deps.engine.mintDataToken(
|
|
5138
|
+
{
|
|
5139
|
+
recipientPubkey: deps.ownPubkeyBytes,
|
|
5140
|
+
data: hexToBytes(entry.dataHex),
|
|
5141
|
+
tokenType: hexToBytes(entry.tokenTypeHex),
|
|
5142
|
+
salt: hexToBytes(entry.saltHex),
|
|
5143
|
+
...entry.justificationHex !== null ? { justification: hexToBytes(entry.justificationHex) } : {},
|
|
5144
|
+
...mintJustificationVerifiers ? { mintJustificationVerifiers } : {}
|
|
5145
|
+
},
|
|
5146
|
+
{ transferId: entry.mintId }
|
|
5147
|
+
);
|
|
5148
|
+
if (entry.tokenId !== "" && token.blob.tokenId !== entry.tokenId) {
|
|
5149
|
+
throw new SphereError(`minted token ${token.blob.tokenId}, but the journal names ${entry.tokenId}`, "VALIDATION_ERROR");
|
|
5150
|
+
}
|
|
5151
|
+
if (entry.tokenId === "") {
|
|
5152
|
+
await deps.customMintJournal.upsert({ ...entry, tokenId: token.blob.tokenId });
|
|
5153
|
+
}
|
|
5154
|
+
await finalizeMint(deps, {
|
|
5155
|
+
mintId: entry.mintId,
|
|
5156
|
+
token,
|
|
5157
|
+
assets: entry.assets,
|
|
5158
|
+
journal: deps.customMintJournal
|
|
5159
|
+
});
|
|
5160
|
+
return token.blob.tokenId;
|
|
5161
|
+
}
|
|
5162
|
+
|
|
5163
|
+
// modules/payments-v2/burn.ts
|
|
5164
|
+
var HEX322 = /^[0-9a-f]{64}$/;
|
|
5165
|
+
async function heldToken2(deps, tokenId) {
|
|
5166
|
+
const bytes = (await deps.storagePort.getBlobs([tokenId])).get(tokenId);
|
|
5167
|
+
if (bytes === void 0) {
|
|
5168
|
+
throw new SphereError(`Token ${tokenId} has no blob in storage; it is not held here`, "STORAGE_ERROR");
|
|
5169
|
+
}
|
|
5170
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
5171
|
+
}
|
|
5172
|
+
function assetsOf(token) {
|
|
5173
|
+
return (token.value?.assets ?? []).map((a) => ({ coinId: a.coinId, amount: a.amount.toString() }));
|
|
5174
|
+
}
|
|
5175
|
+
async function runBurnUnderJournal(deps, input) {
|
|
5176
|
+
const { burnId } = input;
|
|
5177
|
+
const { tokenId, reasonBytes } = input.request;
|
|
5178
|
+
if (!HEX322.test(tokenId) || reasonBytes.length === 0) {
|
|
5179
|
+
return { success: false, burnId, tokenId, error: "burn: tokenId must be 64 hex and reasonBytes non-empty" };
|
|
5180
|
+
}
|
|
5181
|
+
let token;
|
|
5182
|
+
try {
|
|
5183
|
+
token = await heldToken2(deps, tokenId);
|
|
5184
|
+
if (!deps.engine.isOwnedBy(token, deps.engine.getIdentity().chainPubkey)) {
|
|
5185
|
+
throw new SphereError(`Token ${tokenId} is not owned by this wallet`, "VALIDATION_ERROR");
|
|
5186
|
+
}
|
|
5187
|
+
} catch (err) {
|
|
5188
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
5189
|
+
}
|
|
5190
|
+
const entry = {
|
|
5191
|
+
burnId,
|
|
5192
|
+
tokenId,
|
|
5193
|
+
reasonHex: bytesToHex(reasonBytes),
|
|
5194
|
+
burnedTokenHex: null,
|
|
5195
|
+
settled: false,
|
|
5196
|
+
assets: assetsOf(token),
|
|
5197
|
+
createdAt: deps.now()
|
|
5198
|
+
};
|
|
5199
|
+
await deps.burnJournal.upsert(entry);
|
|
5200
|
+
try {
|
|
5201
|
+
const settled = await burnJournaled(deps, entry, token, { applied: false });
|
|
5202
|
+
return { success: true, burnId, tokenId, burnedToken: hexToBytes(settled.burnedTokenHex ?? "") };
|
|
5203
|
+
} catch (err) {
|
|
5204
|
+
deps.armHeartbeat();
|
|
5205
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
async function replayBurns(deps) {
|
|
5209
|
+
let progressed = 0;
|
|
5210
|
+
for (const entry of await deps.burnJournal.list()) {
|
|
5211
|
+
if (entry.settled || deps.isActiveOp(entry.burnId)) continue;
|
|
5212
|
+
try {
|
|
5213
|
+
const token = entry.burnedTokenHex === null ? await heldToken2(deps, entry.tokenId) : null;
|
|
5214
|
+
const applied = entry.burnedTokenHex !== null && !await deps.tokenInServerInventory(entry.tokenId);
|
|
5215
|
+
await burnJournaled(deps, entry, token, { applied });
|
|
5216
|
+
progressed += 1;
|
|
5217
|
+
} catch {
|
|
5218
|
+
continue;
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
return progressed;
|
|
5222
|
+
}
|
|
5223
|
+
async function pendingBurns(deps) {
|
|
5224
|
+
return (await deps.burnJournal.list()).map((entry) => ({
|
|
5225
|
+
burnId: entry.burnId,
|
|
5226
|
+
tokenId: entry.tokenId,
|
|
5227
|
+
reasonBytes: hexToBytes(entry.reasonHex),
|
|
5228
|
+
burnedToken: entry.burnedTokenHex === null ? null : hexToBytes(entry.burnedTokenHex),
|
|
5229
|
+
settled: entry.settled,
|
|
5230
|
+
createdAt: entry.createdAt
|
|
5231
|
+
}));
|
|
5232
|
+
}
|
|
5233
|
+
async function acknowledgeBurn(deps, burnId) {
|
|
5234
|
+
const entry = await deps.burnJournal.getByKey(burnId);
|
|
5235
|
+
if (entry === void 0) return;
|
|
5236
|
+
if (!entry.settled) {
|
|
5237
|
+
throw new SphereError(`Burn ${burnId} is not settled yet; it cannot be acknowledged`, "VALIDATION_ERROR");
|
|
5238
|
+
}
|
|
5239
|
+
await deps.burnJournal.removeByKey(burnId);
|
|
5240
|
+
}
|
|
5241
|
+
async function burnJournaled(deps, entry, token, state) {
|
|
5242
|
+
let current = entry;
|
|
5243
|
+
if (current.burnedTokenHex === null) {
|
|
5244
|
+
if (token === null) throw new SphereError("burn replay needs the held token to certify", "VALIDATION_ERROR");
|
|
5245
|
+
const burned = await deps.engine.burn(
|
|
5246
|
+
{ token, reasonBytes: hexToBytes(current.reasonHex) },
|
|
5247
|
+
{ transferId: current.burnId }
|
|
5248
|
+
);
|
|
5249
|
+
if (burned.blob.tokenId !== current.tokenId) {
|
|
5250
|
+
throw new SphereError(`burned token ${burned.blob.tokenId}, but the journal names ${current.tokenId}`, "VALIDATION_ERROR");
|
|
5251
|
+
}
|
|
5252
|
+
current = { ...current, burnedTokenHex: bytesToHex(burned.blob.token) };
|
|
5253
|
+
await deps.burnJournal.upsert(current);
|
|
5254
|
+
}
|
|
5255
|
+
if (!state.applied) {
|
|
5256
|
+
await deps.storagePort.applyDelta({ transferId: current.burnId, spent: [current.tokenId], added: [] });
|
|
5257
|
+
}
|
|
5258
|
+
await deps.recordSent({ transferId: current.burnId, assets: current.assets, tokenId: current.tokenId });
|
|
5259
|
+
current = { ...current, settled: true };
|
|
5260
|
+
await deps.burnJournal.upsert(current);
|
|
5261
|
+
deps.refreshView();
|
|
5262
|
+
return current;
|
|
5042
5263
|
}
|
|
5043
5264
|
|
|
5044
5265
|
// modules/payments-v2/send-whole.ts
|
|
@@ -5226,7 +5447,6 @@ async function takeSourceBlobs(deps, sourceIds) {
|
|
|
5226
5447
|
|
|
5227
5448
|
// modules/payments-v2/PaymentsFacade.ts
|
|
5228
5449
|
var MAX_RESELECT = 8;
|
|
5229
|
-
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
5230
5450
|
var PaymentsFacade = class {
|
|
5231
5451
|
constructor(deps) {
|
|
5232
5452
|
this.deps = deps;
|
|
@@ -5263,6 +5483,8 @@ var PaymentsFacade = class {
|
|
|
5263
5483
|
refreshView: () => this.view.delta(),
|
|
5264
5484
|
replayMints: () => replayCoinMints(this.mintDeps()),
|
|
5265
5485
|
replayNftMints: () => replayNftMints(this.mintDeps()),
|
|
5486
|
+
replayCustomMints: () => replayCustomMints(this.mintDeps()),
|
|
5487
|
+
replayBurns: () => replayBurns(this.mintDeps()),
|
|
5266
5488
|
isActiveOp: (id) => this.activeMoneyOps.has(id),
|
|
5267
5489
|
emit: deps.emit,
|
|
5268
5490
|
now: () => this.nowMs()
|
|
@@ -5358,6 +5580,9 @@ var PaymentsFacade = class {
|
|
|
5358
5580
|
tokenData(tokenId) {
|
|
5359
5581
|
return readTokenData(this.readDeps(), tokenId);
|
|
5360
5582
|
}
|
|
5583
|
+
tokenJustification(tokenId) {
|
|
5584
|
+
return readTokenJustification(this.readDeps(), tokenId);
|
|
5585
|
+
}
|
|
5361
5586
|
nft(tokenId) {
|
|
5362
5587
|
return readNft(this.readDeps(), tokenId);
|
|
5363
5588
|
}
|
|
@@ -5406,8 +5631,12 @@ var PaymentsFacade = class {
|
|
|
5406
5631
|
return this.track(this.mintInner(coinId, amount));
|
|
5407
5632
|
}
|
|
5408
5633
|
mintNft(request) {
|
|
5409
|
-
return this.track(this.
|
|
5634
|
+
return this.track(this.ownedOp((mintId) => runNftMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
5410
5635
|
}
|
|
5636
|
+
mintCustom = (request) => this.track(this.ownedOp((mintId) => runCustomMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
5637
|
+
burn = (request) => this.track(this.ownedOp((burnId) => runBurnUnderJournal(this.mintDeps(), { burnId, request })));
|
|
5638
|
+
pendingBurns = () => this.track(pendingBurns(this.mintDeps()));
|
|
5639
|
+
acknowledgeBurn = (burnId) => this.track(acknowledgeBurn(this.mintDeps(), burnId));
|
|
5411
5640
|
// §4 retry verb: coalesces onto the running pass — NEVER re-issue send() (#631/#676).
|
|
5412
5641
|
async resumeNow() {
|
|
5413
5642
|
if (!this.started) return;
|
|
@@ -5431,12 +5660,15 @@ var PaymentsFacade = class {
|
|
|
5431
5660
|
nowMs() {
|
|
5432
5661
|
return (this.deps.now ?? Date.now)();
|
|
5433
5662
|
}
|
|
5434
|
-
/** One snapshot serves the coin and the
|
|
5663
|
+
/** One snapshot serves the coin, NFT and custom mints and the burn, live and replayed. */
|
|
5435
5664
|
mintDeps() {
|
|
5436
5665
|
return {
|
|
5437
5666
|
engine: this.engine(),
|
|
5438
5667
|
mintJournal: this.machineStores.mintJournal,
|
|
5439
5668
|
nftMintJournal: this.machineStores.nftMintJournal,
|
|
5669
|
+
customMintJournal: this.machineStores.customMintJournal,
|
|
5670
|
+
burnJournal: this.machineStores.burnJournal,
|
|
5671
|
+
recordSent: (input) => this.historyStore.recordSent(input),
|
|
5440
5672
|
storagePort: this.deps.storagePort,
|
|
5441
5673
|
recordMint: (input) => this.historyStore.recordMint(input),
|
|
5442
5674
|
armHeartbeat: () => this.heartbeat.arm(),
|
|
@@ -5445,7 +5677,7 @@ var PaymentsFacade = class {
|
|
|
5445
5677
|
ownPubkeyBytes: this.ownPubkeyBytes,
|
|
5446
5678
|
nftTokenType: this.deps.nftTokenType,
|
|
5447
5679
|
isActiveOp: (mintId) => this.activeMoneyOps.has(mintId),
|
|
5448
|
-
tokenInServerInventory: (tokenId) => this.
|
|
5680
|
+
tokenInServerInventory: (tokenId) => tokenInServerInventory(this.deps.storagePort, tokenId),
|
|
5449
5681
|
emit: this.deps.emit,
|
|
5450
5682
|
now: () => this.nowMs()
|
|
5451
5683
|
};
|
|
@@ -5766,29 +5998,18 @@ var PaymentsFacade = class {
|
|
|
5766
5998
|
if (!/^(?:[0-9a-f]{2})+$/.test(coinId) || amount <= 0n) {
|
|
5767
5999
|
return { success: false, error: "coinId must be even-length lowercase hex and amount positive" };
|
|
5768
6000
|
}
|
|
5769
|
-
return this.
|
|
6001
|
+
return this.ownedOp((mintId) => runMintUnderJournal(this.mintDeps(), { mintId, coinId, amount }));
|
|
5770
6002
|
}
|
|
5771
|
-
/** A live
|
|
5772
|
-
async
|
|
5773
|
-
const
|
|
5774
|
-
this.activeMoneyOps.add(
|
|
6003
|
+
/** A live money op under a fresh id — its replay stays hands-off while this attempt runs (§7). */
|
|
6004
|
+
async ownedOp(run) {
|
|
6005
|
+
const id = this.newId();
|
|
6006
|
+
this.activeMoneyOps.add(id);
|
|
5775
6007
|
try {
|
|
5776
|
-
return await run(
|
|
6008
|
+
return await run(id);
|
|
5777
6009
|
} finally {
|
|
5778
|
-
this.activeMoneyOps.delete(
|
|
6010
|
+
this.activeMoneyOps.delete(id);
|
|
5779
6011
|
}
|
|
5780
6012
|
}
|
|
5781
|
-
async tokenInServerInventory(tokenId) {
|
|
5782
|
-
let page = await this.deps.storagePort.listInventory();
|
|
5783
|
-
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
5784
|
-
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) {
|
|
5785
|
-
return true;
|
|
5786
|
-
}
|
|
5787
|
-
if (!page.more) return false;
|
|
5788
|
-
page = await this.deps.storagePort.listInventory(page.cursor);
|
|
5789
|
-
}
|
|
5790
|
-
return false;
|
|
5791
|
-
}
|
|
5792
6013
|
// ── wiring internals ───────────────────────────────────────────────────────
|
|
5793
6014
|
engine() {
|
|
5794
6015
|
return this.currentEngine ?? this.deps.engineRef();
|