@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
|
@@ -1978,6 +1978,8 @@ var STORE_KEYS = {
|
|
|
1978
1978
|
deliveryJournal: "delivery-journal",
|
|
1979
1979
|
mintJournal: "mint-journal",
|
|
1980
1980
|
nftMintJournal: "nft-mint-journal",
|
|
1981
|
+
customMintJournal: "custom-mint-journal",
|
|
1982
|
+
burnJournal: "burn-journal",
|
|
1981
1983
|
shortfalls: "shortfalls",
|
|
1982
1984
|
settlingLinks: "settling",
|
|
1983
1985
|
streamCursor: (s) => `cursor:${s}`,
|
|
@@ -3243,6 +3245,8 @@ function createMachineStores(kv) {
|
|
|
3243
3245
|
deliveryJournal: new DeliveryJournal(kv),
|
|
3244
3246
|
mintJournal: new ListStore(kv, STORE_KEYS.mintJournal, (e) => e.mintId),
|
|
3245
3247
|
nftMintJournal: new ListStore(kv, STORE_KEYS.nftMintJournal, (e) => e.mintId),
|
|
3248
|
+
customMintJournal: new ListStore(kv, STORE_KEYS.customMintJournal, (e) => e.mintId),
|
|
3249
|
+
burnJournal: new ListStore(kv, STORE_KEYS.burnJournal, (e) => e.burnId),
|
|
3246
3250
|
shortfalls: new ListStore(kv, STORE_KEYS.shortfalls, (e) => e.transferId)
|
|
3247
3251
|
};
|
|
3248
3252
|
}
|
|
@@ -3978,7 +3982,12 @@ var Converger = class {
|
|
|
3978
3982
|
} catch (err) {
|
|
3979
3983
|
retryAfterMs ??= retryAfterMsOf(err);
|
|
3980
3984
|
}
|
|
3981
|
-
for (const replay of [
|
|
3985
|
+
for (const replay of [
|
|
3986
|
+
() => this.deps.replayMints(),
|
|
3987
|
+
() => this.deps.replayNftMints(),
|
|
3988
|
+
() => this.deps.replayCustomMints(),
|
|
3989
|
+
() => this.deps.replayBurns()
|
|
3990
|
+
]) {
|
|
3982
3991
|
try {
|
|
3983
3992
|
progress = await replay() > 0 || progress;
|
|
3984
3993
|
} catch (err) {
|
|
@@ -3989,14 +3998,17 @@ var Converger = class {
|
|
|
3989
3998
|
}
|
|
3990
3999
|
/** §4 heartbeat predicate — read fresh from the §6 stores, never cached. */
|
|
3991
4000
|
async pendingWork() {
|
|
3992
|
-
const [backstop, journal, mints, nftMints] = await Promise.all([
|
|
4001
|
+
const [backstop, journal, mints, nftMints, customMints, burns] = await Promise.all([
|
|
3993
4002
|
this.deps.stores.backstop.list(),
|
|
3994
4003
|
this.deps.stores.deliveryJournal.list(),
|
|
3995
4004
|
this.deps.stores.mintJournal.list(),
|
|
3996
|
-
this.deps.stores.nftMintJournal.list()
|
|
4005
|
+
this.deps.stores.nftMintJournal.list(),
|
|
4006
|
+
this.deps.stores.customMintJournal.list(),
|
|
4007
|
+
this.deps.stores.burnJournal.list()
|
|
3997
4008
|
]);
|
|
3998
|
-
const mintsOpen = mints.length > 0 || nftMints.length > 0;
|
|
3999
|
-
|
|
4009
|
+
const mintsOpen = mints.length > 0 || nftMints.length > 0 || customMints.length > 0;
|
|
4010
|
+
const burnsOpen = burns.some((e) => !e.settled);
|
|
4011
|
+
return backstop.length > 0 || journal.some((e) => e.undeliverable !== true) || mintsOpen || burnsOpen;
|
|
4000
4012
|
}
|
|
4001
4013
|
async resumePass() {
|
|
4002
4014
|
const report = await resumeAll(this.filteredMachineDeps(), {
|
|
@@ -4744,6 +4756,7 @@ function mintParams(recipientPubkey, coinId, amount) {
|
|
|
4744
4756
|
|
|
4745
4757
|
// modules/payments-v2/mint.ts
|
|
4746
4758
|
var ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
4759
|
+
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
4747
4760
|
function runMintUnderJournal(deps, input) {
|
|
4748
4761
|
return mintUnderJournal(deps, input);
|
|
4749
4762
|
}
|
|
@@ -4796,6 +4809,15 @@ async function finalizeMint(deps, input) {
|
|
|
4796
4809
|
deps.noteHeldState(token.blob.tokenId, (await deps.engine.deliveryKeys(bytes)).stateHash);
|
|
4797
4810
|
deps.refreshView();
|
|
4798
4811
|
}
|
|
4812
|
+
async function tokenInServerInventory(storagePort, tokenId) {
|
|
4813
|
+
let page = await storagePort.listInventory();
|
|
4814
|
+
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
4815
|
+
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) return true;
|
|
4816
|
+
if (!page.more) return false;
|
|
4817
|
+
page = await storagePort.listInventory(page.cursor);
|
|
4818
|
+
}
|
|
4819
|
+
return false;
|
|
4820
|
+
}
|
|
4799
4821
|
async function replayCoinMints(deps) {
|
|
4800
4822
|
let resolved = 0;
|
|
4801
4823
|
for (const entry of await deps.mintJournal.list()) {
|
|
@@ -4973,6 +4995,12 @@ async function decodeView(deps, tokenId, bytes) {
|
|
|
4973
4995
|
|
|
4974
4996
|
// modules/payments-v2/inventory/token-data.ts
|
|
4975
4997
|
async function readTokenData(deps, tokenId) {
|
|
4998
|
+
return deps.engine.readTokenData(await heldToken(deps, tokenId));
|
|
4999
|
+
}
|
|
5000
|
+
async function readTokenJustification(deps, tokenId) {
|
|
5001
|
+
return deps.engine.readTokenJustification(await heldToken(deps, tokenId));
|
|
5002
|
+
}
|
|
5003
|
+
async function heldToken(deps, tokenId) {
|
|
4976
5004
|
if (deps.view.stateHashOf(tokenId) === void 0) {
|
|
4977
5005
|
throw new SphereError(`Token ${tokenId} is not in inventory`, "VALIDATION_ERROR");
|
|
4978
5006
|
}
|
|
@@ -4980,7 +5008,200 @@ async function readTokenData(deps, tokenId) {
|
|
|
4980
5008
|
if (bytes === void 0) {
|
|
4981
5009
|
throw new SphereError(`Token ${tokenId} has no blob in storage`, "STORAGE_ERROR");
|
|
4982
5010
|
}
|
|
4983
|
-
return deps.engine.
|
|
5011
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
5012
|
+
}
|
|
5013
|
+
|
|
5014
|
+
// modules/payments-v2/mint-custom.ts
|
|
5015
|
+
var HEX32 = /^[0-9a-f]{64}$/;
|
|
5016
|
+
function assertRequest(request) {
|
|
5017
|
+
if (request.tokenType.length !== 32) {
|
|
5018
|
+
throw new SphereError("mintCustom: tokenType must be 32 bytes", "VALIDATION_ERROR");
|
|
5019
|
+
}
|
|
5020
|
+
if (request.salt.length < 16 || request.salt.length > 64) {
|
|
5021
|
+
throw new SphereError("mintCustom: salt must be 16 to 64 bytes", "VALIDATION_ERROR");
|
|
5022
|
+
}
|
|
5023
|
+
if (request.data.length === 0) {
|
|
5024
|
+
throw new SphereError("mintCustom: data must not be empty", "VALIDATION_ERROR");
|
|
5025
|
+
}
|
|
5026
|
+
for (const asset of request.assets) {
|
|
5027
|
+
if (!HEX32.test(asset.coinId) || asset.amount <= 0n) {
|
|
5028
|
+
throw new SphereError("mintCustom: assets need a 64-hex coinId and a positive amount", "VALIDATION_ERROR");
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
}
|
|
5032
|
+
async function runCustomMintUnderJournal(deps, input) {
|
|
5033
|
+
try {
|
|
5034
|
+
assertRequest(input.request);
|
|
5035
|
+
} catch (err) {
|
|
5036
|
+
return { success: false, error: messageOf(err) };
|
|
5037
|
+
}
|
|
5038
|
+
const entry = journalEntryOf2(input.mintId, input.request, deps.now());
|
|
5039
|
+
await deps.customMintJournal.upsert(entry);
|
|
5040
|
+
let tokenId;
|
|
5041
|
+
try {
|
|
5042
|
+
tokenId = await mintJournaled2(deps, entry, input.request.mintJustificationVerifiers);
|
|
5043
|
+
} catch (err) {
|
|
5044
|
+
deps.armHeartbeat();
|
|
5045
|
+
return { success: false, error: messageOf(err) };
|
|
5046
|
+
}
|
|
5047
|
+
return { success: true, tokenId };
|
|
5048
|
+
}
|
|
5049
|
+
async function replayCustomMints(deps) {
|
|
5050
|
+
let resolved = 0;
|
|
5051
|
+
for (const entry of await deps.customMintJournal.list()) {
|
|
5052
|
+
if (deps.isActiveOp(entry.mintId)) continue;
|
|
5053
|
+
try {
|
|
5054
|
+
if (entry.tokenId !== "" && await deps.tokenInServerInventory(entry.tokenId)) {
|
|
5055
|
+
await deps.customMintJournal.removeByKey(entry.mintId);
|
|
5056
|
+
} else {
|
|
5057
|
+
await mintJournaled2(deps, entry, void 0);
|
|
5058
|
+
}
|
|
5059
|
+
resolved += 1;
|
|
5060
|
+
} catch {
|
|
5061
|
+
continue;
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
return resolved;
|
|
5065
|
+
}
|
|
5066
|
+
function journalEntryOf2(mintId, request, createdAt) {
|
|
5067
|
+
return {
|
|
5068
|
+
mintId,
|
|
5069
|
+
tokenId: "",
|
|
5070
|
+
dataHex: bytesToHex(request.data),
|
|
5071
|
+
saltHex: bytesToHex(request.salt),
|
|
5072
|
+
tokenTypeHex: bytesToHex(request.tokenType),
|
|
5073
|
+
justificationHex: request.justification ? bytesToHex(request.justification) : null,
|
|
5074
|
+
assets: request.assets.map((a) => ({ coinId: a.coinId, amount: a.amount.toString() })),
|
|
5075
|
+
createdAt
|
|
5076
|
+
};
|
|
5077
|
+
}
|
|
5078
|
+
async function mintJournaled2(deps, entry, mintJustificationVerifiers) {
|
|
5079
|
+
const token = await deps.engine.mintDataToken(
|
|
5080
|
+
{
|
|
5081
|
+
recipientPubkey: deps.ownPubkeyBytes,
|
|
5082
|
+
data: hexToBytes(entry.dataHex),
|
|
5083
|
+
tokenType: hexToBytes(entry.tokenTypeHex),
|
|
5084
|
+
salt: hexToBytes(entry.saltHex),
|
|
5085
|
+
...entry.justificationHex !== null ? { justification: hexToBytes(entry.justificationHex) } : {},
|
|
5086
|
+
...mintJustificationVerifiers ? { mintJustificationVerifiers } : {}
|
|
5087
|
+
},
|
|
5088
|
+
{ transferId: entry.mintId }
|
|
5089
|
+
);
|
|
5090
|
+
if (entry.tokenId !== "" && token.blob.tokenId !== entry.tokenId) {
|
|
5091
|
+
throw new SphereError(`minted token ${token.blob.tokenId}, but the journal names ${entry.tokenId}`, "VALIDATION_ERROR");
|
|
5092
|
+
}
|
|
5093
|
+
if (entry.tokenId === "") {
|
|
5094
|
+
await deps.customMintJournal.upsert({ ...entry, tokenId: token.blob.tokenId });
|
|
5095
|
+
}
|
|
5096
|
+
await finalizeMint(deps, {
|
|
5097
|
+
mintId: entry.mintId,
|
|
5098
|
+
token,
|
|
5099
|
+
assets: entry.assets,
|
|
5100
|
+
journal: deps.customMintJournal
|
|
5101
|
+
});
|
|
5102
|
+
return token.blob.tokenId;
|
|
5103
|
+
}
|
|
5104
|
+
|
|
5105
|
+
// modules/payments-v2/burn.ts
|
|
5106
|
+
var HEX322 = /^[0-9a-f]{64}$/;
|
|
5107
|
+
async function heldToken2(deps, tokenId) {
|
|
5108
|
+
const bytes = (await deps.storagePort.getBlobs([tokenId])).get(tokenId);
|
|
5109
|
+
if (bytes === void 0) {
|
|
5110
|
+
throw new SphereError(`Token ${tokenId} has no blob in storage; it is not held here`, "STORAGE_ERROR");
|
|
5111
|
+
}
|
|
5112
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
5113
|
+
}
|
|
5114
|
+
function assetsOf(token) {
|
|
5115
|
+
return (token.value?.assets ?? []).map((a) => ({ coinId: a.coinId, amount: a.amount.toString() }));
|
|
5116
|
+
}
|
|
5117
|
+
async function runBurnUnderJournal(deps, input) {
|
|
5118
|
+
const { burnId } = input;
|
|
5119
|
+
const { tokenId, reasonBytes } = input.request;
|
|
5120
|
+
if (!HEX322.test(tokenId) || reasonBytes.length === 0) {
|
|
5121
|
+
return { success: false, burnId, tokenId, error: "burn: tokenId must be 64 hex and reasonBytes non-empty" };
|
|
5122
|
+
}
|
|
5123
|
+
let token;
|
|
5124
|
+
try {
|
|
5125
|
+
token = await heldToken2(deps, tokenId);
|
|
5126
|
+
if (!deps.engine.isOwnedBy(token, deps.engine.getIdentity().chainPubkey)) {
|
|
5127
|
+
throw new SphereError(`Token ${tokenId} is not owned by this wallet`, "VALIDATION_ERROR");
|
|
5128
|
+
}
|
|
5129
|
+
} catch (err) {
|
|
5130
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
5131
|
+
}
|
|
5132
|
+
const entry = {
|
|
5133
|
+
burnId,
|
|
5134
|
+
tokenId,
|
|
5135
|
+
reasonHex: bytesToHex(reasonBytes),
|
|
5136
|
+
burnedTokenHex: null,
|
|
5137
|
+
settled: false,
|
|
5138
|
+
assets: assetsOf(token),
|
|
5139
|
+
createdAt: deps.now()
|
|
5140
|
+
};
|
|
5141
|
+
await deps.burnJournal.upsert(entry);
|
|
5142
|
+
try {
|
|
5143
|
+
const settled = await burnJournaled(deps, entry, token, { applied: false });
|
|
5144
|
+
return { success: true, burnId, tokenId, burnedToken: hexToBytes(settled.burnedTokenHex ?? "") };
|
|
5145
|
+
} catch (err) {
|
|
5146
|
+
deps.armHeartbeat();
|
|
5147
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
async function replayBurns(deps) {
|
|
5151
|
+
let progressed = 0;
|
|
5152
|
+
for (const entry of await deps.burnJournal.list()) {
|
|
5153
|
+
if (entry.settled || deps.isActiveOp(entry.burnId)) continue;
|
|
5154
|
+
try {
|
|
5155
|
+
const token = entry.burnedTokenHex === null ? await heldToken2(deps, entry.tokenId) : null;
|
|
5156
|
+
const applied = entry.burnedTokenHex !== null && !await deps.tokenInServerInventory(entry.tokenId);
|
|
5157
|
+
await burnJournaled(deps, entry, token, { applied });
|
|
5158
|
+
progressed += 1;
|
|
5159
|
+
} catch {
|
|
5160
|
+
continue;
|
|
5161
|
+
}
|
|
5162
|
+
}
|
|
5163
|
+
return progressed;
|
|
5164
|
+
}
|
|
5165
|
+
async function pendingBurns(deps) {
|
|
5166
|
+
return (await deps.burnJournal.list()).map((entry) => ({
|
|
5167
|
+
burnId: entry.burnId,
|
|
5168
|
+
tokenId: entry.tokenId,
|
|
5169
|
+
reasonBytes: hexToBytes(entry.reasonHex),
|
|
5170
|
+
burnedToken: entry.burnedTokenHex === null ? null : hexToBytes(entry.burnedTokenHex),
|
|
5171
|
+
settled: entry.settled,
|
|
5172
|
+
createdAt: entry.createdAt
|
|
5173
|
+
}));
|
|
5174
|
+
}
|
|
5175
|
+
async function acknowledgeBurn(deps, burnId) {
|
|
5176
|
+
const entry = await deps.burnJournal.getByKey(burnId);
|
|
5177
|
+
if (entry === void 0) return;
|
|
5178
|
+
if (!entry.settled) {
|
|
5179
|
+
throw new SphereError(`Burn ${burnId} is not settled yet; it cannot be acknowledged`, "VALIDATION_ERROR");
|
|
5180
|
+
}
|
|
5181
|
+
await deps.burnJournal.removeByKey(burnId);
|
|
5182
|
+
}
|
|
5183
|
+
async function burnJournaled(deps, entry, token, state) {
|
|
5184
|
+
let current = entry;
|
|
5185
|
+
if (current.burnedTokenHex === null) {
|
|
5186
|
+
if (token === null) throw new SphereError("burn replay needs the held token to certify", "VALIDATION_ERROR");
|
|
5187
|
+
const burned = await deps.engine.burn(
|
|
5188
|
+
{ token, reasonBytes: hexToBytes(current.reasonHex) },
|
|
5189
|
+
{ transferId: current.burnId }
|
|
5190
|
+
);
|
|
5191
|
+
if (burned.blob.tokenId !== current.tokenId) {
|
|
5192
|
+
throw new SphereError(`burned token ${burned.blob.tokenId}, but the journal names ${current.tokenId}`, "VALIDATION_ERROR");
|
|
5193
|
+
}
|
|
5194
|
+
current = { ...current, burnedTokenHex: bytesToHex(burned.blob.token) };
|
|
5195
|
+
await deps.burnJournal.upsert(current);
|
|
5196
|
+
}
|
|
5197
|
+
if (!state.applied) {
|
|
5198
|
+
await deps.storagePort.applyDelta({ transferId: current.burnId, spent: [current.tokenId], added: [] });
|
|
5199
|
+
}
|
|
5200
|
+
await deps.recordSent({ transferId: current.burnId, assets: current.assets, tokenId: current.tokenId });
|
|
5201
|
+
current = { ...current, settled: true };
|
|
5202
|
+
await deps.burnJournal.upsert(current);
|
|
5203
|
+
deps.refreshView();
|
|
5204
|
+
return current;
|
|
4984
5205
|
}
|
|
4985
5206
|
|
|
4986
5207
|
// modules/payments-v2/send-whole.ts
|
|
@@ -5168,7 +5389,6 @@ async function takeSourceBlobs(deps, sourceIds) {
|
|
|
5168
5389
|
|
|
5169
5390
|
// modules/payments-v2/PaymentsFacade.ts
|
|
5170
5391
|
var MAX_RESELECT = 8;
|
|
5171
|
-
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
5172
5392
|
var PaymentsFacade = class {
|
|
5173
5393
|
constructor(deps) {
|
|
5174
5394
|
this.deps = deps;
|
|
@@ -5205,6 +5425,8 @@ var PaymentsFacade = class {
|
|
|
5205
5425
|
refreshView: () => this.view.delta(),
|
|
5206
5426
|
replayMints: () => replayCoinMints(this.mintDeps()),
|
|
5207
5427
|
replayNftMints: () => replayNftMints(this.mintDeps()),
|
|
5428
|
+
replayCustomMints: () => replayCustomMints(this.mintDeps()),
|
|
5429
|
+
replayBurns: () => replayBurns(this.mintDeps()),
|
|
5208
5430
|
isActiveOp: (id) => this.activeMoneyOps.has(id),
|
|
5209
5431
|
emit: deps.emit,
|
|
5210
5432
|
now: () => this.nowMs()
|
|
@@ -5300,6 +5522,9 @@ var PaymentsFacade = class {
|
|
|
5300
5522
|
tokenData(tokenId) {
|
|
5301
5523
|
return readTokenData(this.readDeps(), tokenId);
|
|
5302
5524
|
}
|
|
5525
|
+
tokenJustification(tokenId) {
|
|
5526
|
+
return readTokenJustification(this.readDeps(), tokenId);
|
|
5527
|
+
}
|
|
5303
5528
|
nft(tokenId) {
|
|
5304
5529
|
return readNft(this.readDeps(), tokenId);
|
|
5305
5530
|
}
|
|
@@ -5348,8 +5573,12 @@ var PaymentsFacade = class {
|
|
|
5348
5573
|
return this.track(this.mintInner(coinId, amount));
|
|
5349
5574
|
}
|
|
5350
5575
|
mintNft(request) {
|
|
5351
|
-
return this.track(this.
|
|
5576
|
+
return this.track(this.ownedOp((mintId) => runNftMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
5352
5577
|
}
|
|
5578
|
+
mintCustom = (request) => this.track(this.ownedOp((mintId) => runCustomMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
5579
|
+
burn = (request) => this.track(this.ownedOp((burnId) => runBurnUnderJournal(this.mintDeps(), { burnId, request })));
|
|
5580
|
+
pendingBurns = () => this.track(pendingBurns(this.mintDeps()));
|
|
5581
|
+
acknowledgeBurn = (burnId) => this.track(acknowledgeBurn(this.mintDeps(), burnId));
|
|
5353
5582
|
// §4 retry verb: coalesces onto the running pass — NEVER re-issue send() (#631/#676).
|
|
5354
5583
|
async resumeNow() {
|
|
5355
5584
|
if (!this.started) return;
|
|
@@ -5373,12 +5602,15 @@ var PaymentsFacade = class {
|
|
|
5373
5602
|
nowMs() {
|
|
5374
5603
|
return (this.deps.now ?? Date.now)();
|
|
5375
5604
|
}
|
|
5376
|
-
/** One snapshot serves the coin and the
|
|
5605
|
+
/** One snapshot serves the coin, NFT and custom mints and the burn, live and replayed. */
|
|
5377
5606
|
mintDeps() {
|
|
5378
5607
|
return {
|
|
5379
5608
|
engine: this.engine(),
|
|
5380
5609
|
mintJournal: this.machineStores.mintJournal,
|
|
5381
5610
|
nftMintJournal: this.machineStores.nftMintJournal,
|
|
5611
|
+
customMintJournal: this.machineStores.customMintJournal,
|
|
5612
|
+
burnJournal: this.machineStores.burnJournal,
|
|
5613
|
+
recordSent: (input) => this.historyStore.recordSent(input),
|
|
5382
5614
|
storagePort: this.deps.storagePort,
|
|
5383
5615
|
recordMint: (input) => this.historyStore.recordMint(input),
|
|
5384
5616
|
armHeartbeat: () => this.heartbeat.arm(),
|
|
@@ -5387,7 +5619,7 @@ var PaymentsFacade = class {
|
|
|
5387
5619
|
ownPubkeyBytes: this.ownPubkeyBytes,
|
|
5388
5620
|
nftTokenType: this.deps.nftTokenType,
|
|
5389
5621
|
isActiveOp: (mintId) => this.activeMoneyOps.has(mintId),
|
|
5390
|
-
tokenInServerInventory: (tokenId) => this.
|
|
5622
|
+
tokenInServerInventory: (tokenId) => tokenInServerInventory(this.deps.storagePort, tokenId),
|
|
5391
5623
|
emit: this.deps.emit,
|
|
5392
5624
|
now: () => this.nowMs()
|
|
5393
5625
|
};
|
|
@@ -5708,29 +5940,18 @@ var PaymentsFacade = class {
|
|
|
5708
5940
|
if (!/^(?:[0-9a-f]{2})+$/.test(coinId) || amount <= 0n) {
|
|
5709
5941
|
return { success: false, error: "coinId must be even-length lowercase hex and amount positive" };
|
|
5710
5942
|
}
|
|
5711
|
-
return this.
|
|
5943
|
+
return this.ownedOp((mintId) => runMintUnderJournal(this.mintDeps(), { mintId, coinId, amount }));
|
|
5712
5944
|
}
|
|
5713
|
-
/** A live
|
|
5714
|
-
async
|
|
5715
|
-
const
|
|
5716
|
-
this.activeMoneyOps.add(
|
|
5945
|
+
/** A live money op under a fresh id — its replay stays hands-off while this attempt runs (§7). */
|
|
5946
|
+
async ownedOp(run) {
|
|
5947
|
+
const id = this.newId();
|
|
5948
|
+
this.activeMoneyOps.add(id);
|
|
5717
5949
|
try {
|
|
5718
|
-
return await run(
|
|
5950
|
+
return await run(id);
|
|
5719
5951
|
} finally {
|
|
5720
|
-
this.activeMoneyOps.delete(
|
|
5952
|
+
this.activeMoneyOps.delete(id);
|
|
5721
5953
|
}
|
|
5722
5954
|
}
|
|
5723
|
-
async tokenInServerInventory(tokenId) {
|
|
5724
|
-
let page = await this.deps.storagePort.listInventory();
|
|
5725
|
-
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
5726
|
-
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) {
|
|
5727
|
-
return true;
|
|
5728
|
-
}
|
|
5729
|
-
if (!page.more) return false;
|
|
5730
|
-
page = await this.deps.storagePort.listInventory(page.cursor);
|
|
5731
|
-
}
|
|
5732
|
-
return false;
|
|
5733
|
-
}
|
|
5734
5955
|
// ── wiring internals ───────────────────────────────────────────────────────
|
|
5735
5956
|
engine() {
|
|
5736
5957
|
return this.currentEngine ?? this.deps.engineRef();
|