@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
package/dist/index.js
CHANGED
|
@@ -10321,6 +10321,19 @@ async function readTokenNft(token) {
|
|
|
10321
10321
|
}
|
|
10322
10322
|
}
|
|
10323
10323
|
|
|
10324
|
+
// token-engine/plugin-ops.ts
|
|
10325
|
+
function contextWithMintVerifiers(base, verifiers) {
|
|
10326
|
+
const registry = new MintJustificationVerifierService();
|
|
10327
|
+
for (const verifier of verifiers) registry.register(verifier);
|
|
10328
|
+
return new VerificationContext(
|
|
10329
|
+
base.trustBase,
|
|
10330
|
+
base.predicateVerifier,
|
|
10331
|
+
base.unicityCertificateVerifier,
|
|
10332
|
+
registry,
|
|
10333
|
+
new TokenIssuanceVerifierService(false)
|
|
10334
|
+
);
|
|
10335
|
+
}
|
|
10336
|
+
|
|
10324
10337
|
// token-engine/split-checkpoint.ts
|
|
10325
10338
|
var CHECKPOINT_VERSION = 2;
|
|
10326
10339
|
var CHECKPOINT_SDK_VERSION = "@unicitylabs/state-transition-sdk@3.0.1";
|
|
@@ -10467,6 +10480,10 @@ var SphereTokenEngine = class {
|
|
|
10467
10480
|
const data = token.sdkToken.genesis.data;
|
|
10468
10481
|
return data ? new Uint8Array(data) : null;
|
|
10469
10482
|
}
|
|
10483
|
+
readTokenJustification(token) {
|
|
10484
|
+
const justification = token.sdkToken.genesis.justification;
|
|
10485
|
+
return justification ? new Uint8Array(justification) : null;
|
|
10486
|
+
}
|
|
10470
10487
|
readNft(token) {
|
|
10471
10488
|
return readTokenNft(token);
|
|
10472
10489
|
}
|
|
@@ -10498,7 +10515,7 @@ var SphereTokenEngine = class {
|
|
|
10498
10515
|
const recipient = SignaturePredicate.create(params.recipientPubkey);
|
|
10499
10516
|
const tokenType = params.tokenType ? new TokenType(params.tokenType) : TokenType.generate();
|
|
10500
10517
|
const salt = params.salt ? TokenSalt.fromBytes(params.salt) : TokenSalt.generate();
|
|
10501
|
-
const mintTx = await MintTransaction.create(this.deps.networkId, recipient, { data: params.data, tokenType, salt });
|
|
10518
|
+
const mintTx = await MintTransaction.create(this.deps.networkId, recipient, { data: params.data, tokenType, salt, justification: params.justification ?? null });
|
|
10502
10519
|
const certificationData = await CertificationData.fromMintTransaction(mintTx);
|
|
10503
10520
|
const proof = await this.submitAndAwaitProof(
|
|
10504
10521
|
certificationData,
|
|
@@ -10508,34 +10525,30 @@ var SphereTokenEngine = class {
|
|
|
10508
10525
|
options
|
|
10509
10526
|
);
|
|
10510
10527
|
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
|
|
10511
|
-
const
|
|
10512
|
-
return wrapToken(
|
|
10528
|
+
const verifiers = params.mintJustificationVerifiers;
|
|
10529
|
+
return wrapToken(await Token.mint(certified, verifiers ? contextWithMintVerifiers(this.deps, verifiers) : this.deps.verificationContext));
|
|
10513
10530
|
}
|
|
10514
10531
|
buildNftMint(params) {
|
|
10515
10532
|
return planNftMint(this.deps, params);
|
|
10516
10533
|
}
|
|
10517
10534
|
async transfer(params, options) {
|
|
10518
10535
|
this.assertOwned(params.token);
|
|
10519
|
-
|
|
10520
|
-
|
|
10536
|
+
return this.spend(params.token, SignaturePredicate.create(params.recipientPubkey), params.data ?? null, options, "Transfer certification failed");
|
|
10537
|
+
}
|
|
10538
|
+
async burn(params, options) {
|
|
10539
|
+
this.assertOwned(params.token);
|
|
10540
|
+
return this.spend(params.token, BurnPredicate.create(sha256(params.reasonBytes)), params.reasonBytes, options, "Burn certification failed");
|
|
10541
|
+
}
|
|
10542
|
+
async spend(token, recipient, data, options, failLabel) {
|
|
10521
10543
|
const stateMask = StateMask.fromBytes(
|
|
10522
|
-
deriveRealization(this.privateKeyHex,
|
|
10544
|
+
deriveRealization(this.privateKeyHex, this.resolveTransferId(options), this.resolveOpIndex(options), "stateMask")
|
|
10523
10545
|
);
|
|
10524
|
-
const transferTx = await TransferTransaction.create(
|
|
10525
|
-
data: params.data ?? null
|
|
10526
|
-
});
|
|
10546
|
+
const transferTx = await TransferTransaction.create(token.sdkToken, recipient, stateMask, { data });
|
|
10527
10547
|
const unlockScript = await SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);
|
|
10528
10548
|
const certificationData = await CertificationData.fromTransaction(transferTx, unlockScript);
|
|
10529
|
-
const proof = await this.submitAndAwaitProof(
|
|
10530
|
-
certificationData,
|
|
10531
|
-
transferTx,
|
|
10532
|
-
"Transfer certification failed",
|
|
10533
|
-
"TRANSFER_FAILED",
|
|
10534
|
-
options
|
|
10535
|
-
);
|
|
10549
|
+
const proof = await this.submitAndAwaitProof(certificationData, transferTx, failLabel, "TRANSFER_FAILED", options);
|
|
10536
10550
|
const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
|
|
10537
|
-
|
|
10538
|
-
return wrapToken(transferred);
|
|
10551
|
+
return wrapToken(await token.sdkToken.transfer(certified, this.deps.verificationContext));
|
|
10539
10552
|
}
|
|
10540
10553
|
async split(params, options) {
|
|
10541
10554
|
this.assertOwned(params.token);
|
|
@@ -10850,6 +10863,20 @@ var SphereTokenEngine = class {
|
|
|
10850
10863
|
|
|
10851
10864
|
// token-engine/factory.ts
|
|
10852
10865
|
var DEFAULT_VERIFICATION_POOL_SIZE = 4;
|
|
10866
|
+
function registerPluginVerifiers(registry, plugins) {
|
|
10867
|
+
for (const plugin of plugins ?? []) {
|
|
10868
|
+
for (const verifier of plugin.mintJustificationVerifiers ?? []) {
|
|
10869
|
+
try {
|
|
10870
|
+
registry.register(verifier);
|
|
10871
|
+
} catch (err) {
|
|
10872
|
+
throw new SphereError(
|
|
10873
|
+
`Token plugin '${plugin.id}' registers mint-reason tag ${verifier.tag} twice or over another plugin's: ` + (err instanceof Error ? err.message : String(err)),
|
|
10874
|
+
"INVALID_CONFIG"
|
|
10875
|
+
);
|
|
10876
|
+
}
|
|
10877
|
+
}
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
10853
10880
|
function disposedError() {
|
|
10854
10881
|
return new SphereError(
|
|
10855
10882
|
"Verification worker pool disposed \u2014 the in-flight verification was cancelled",
|
|
@@ -10931,6 +10958,7 @@ async function createSphereTokenEngine(config) {
|
|
|
10931
10958
|
mintJustificationVerifier.register(
|
|
10932
10959
|
new SplitMintJustificationVerifier(decodeSpherePaymentData)
|
|
10933
10960
|
);
|
|
10961
|
+
registerPluginVerifiers(mintJustificationVerifier, config.plugins);
|
|
10934
10962
|
const deps = {
|
|
10935
10963
|
client: new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
|
|
10936
10964
|
trustBase,
|
|
@@ -12048,6 +12076,8 @@ var STORE_KEYS = {
|
|
|
12048
12076
|
deliveryJournal: "delivery-journal",
|
|
12049
12077
|
mintJournal: "mint-journal",
|
|
12050
12078
|
nftMintJournal: "nft-mint-journal",
|
|
12079
|
+
customMintJournal: "custom-mint-journal",
|
|
12080
|
+
burnJournal: "burn-journal",
|
|
12051
12081
|
shortfalls: "shortfalls",
|
|
12052
12082
|
settlingLinks: "settling",
|
|
12053
12083
|
streamCursor: (s) => `cursor:${s}`,
|
|
@@ -12232,6 +12262,8 @@ function createMachineStores(kv) {
|
|
|
12232
12262
|
deliveryJournal: new DeliveryJournal(kv),
|
|
12233
12263
|
mintJournal: new ListStore(kv, STORE_KEYS.mintJournal, (e) => e.mintId),
|
|
12234
12264
|
nftMintJournal: new ListStore(kv, STORE_KEYS.nftMintJournal, (e) => e.mintId),
|
|
12265
|
+
customMintJournal: new ListStore(kv, STORE_KEYS.customMintJournal, (e) => e.mintId),
|
|
12266
|
+
burnJournal: new ListStore(kv, STORE_KEYS.burnJournal, (e) => e.burnId),
|
|
12235
12267
|
shortfalls: new ListStore(kv, STORE_KEYS.shortfalls, (e) => e.transferId)
|
|
12236
12268
|
};
|
|
12237
12269
|
}
|
|
@@ -12939,7 +12971,12 @@ var Converger = class {
|
|
|
12939
12971
|
} catch (err) {
|
|
12940
12972
|
retryAfterMs ??= retryAfterMsOf(err);
|
|
12941
12973
|
}
|
|
12942
|
-
for (const replay of [
|
|
12974
|
+
for (const replay of [
|
|
12975
|
+
() => this.deps.replayMints(),
|
|
12976
|
+
() => this.deps.replayNftMints(),
|
|
12977
|
+
() => this.deps.replayCustomMints(),
|
|
12978
|
+
() => this.deps.replayBurns()
|
|
12979
|
+
]) {
|
|
12943
12980
|
try {
|
|
12944
12981
|
progress = await replay() > 0 || progress;
|
|
12945
12982
|
} catch (err) {
|
|
@@ -12950,14 +12987,17 @@ var Converger = class {
|
|
|
12950
12987
|
}
|
|
12951
12988
|
/** §4 heartbeat predicate — read fresh from the §6 stores, never cached. */
|
|
12952
12989
|
async pendingWork() {
|
|
12953
|
-
const [backstop, journal, mints, nftMints] = await Promise.all([
|
|
12990
|
+
const [backstop, journal, mints, nftMints, customMints, burns] = await Promise.all([
|
|
12954
12991
|
this.deps.stores.backstop.list(),
|
|
12955
12992
|
this.deps.stores.deliveryJournal.list(),
|
|
12956
12993
|
this.deps.stores.mintJournal.list(),
|
|
12957
|
-
this.deps.stores.nftMintJournal.list()
|
|
12994
|
+
this.deps.stores.nftMintJournal.list(),
|
|
12995
|
+
this.deps.stores.customMintJournal.list(),
|
|
12996
|
+
this.deps.stores.burnJournal.list()
|
|
12958
12997
|
]);
|
|
12959
|
-
const mintsOpen = mints.length > 0 || nftMints.length > 0;
|
|
12960
|
-
|
|
12998
|
+
const mintsOpen = mints.length > 0 || nftMints.length > 0 || customMints.length > 0;
|
|
12999
|
+
const burnsOpen = burns.some((e) => !e.settled);
|
|
13000
|
+
return backstop.length > 0 || journal.some((e) => e.undeliverable !== true) || mintsOpen || burnsOpen;
|
|
12961
13001
|
}
|
|
12962
13002
|
async resumePass() {
|
|
12963
13003
|
const report = await resumeAll(this.filteredMachineDeps(), {
|
|
@@ -13189,6 +13229,12 @@ async function decodeView(deps, tokenId, bytes) {
|
|
|
13189
13229
|
|
|
13190
13230
|
// modules/payments-v2/inventory/token-data.ts
|
|
13191
13231
|
async function readTokenData(deps, tokenId) {
|
|
13232
|
+
return deps.engine.readTokenData(await heldToken(deps, tokenId));
|
|
13233
|
+
}
|
|
13234
|
+
async function readTokenJustification(deps, tokenId) {
|
|
13235
|
+
return deps.engine.readTokenJustification(await heldToken(deps, tokenId));
|
|
13236
|
+
}
|
|
13237
|
+
async function heldToken(deps, tokenId) {
|
|
13192
13238
|
if (deps.view.stateHashOf(tokenId) === void 0) {
|
|
13193
13239
|
throw new SphereError(`Token ${tokenId} is not in inventory`, "VALIDATION_ERROR");
|
|
13194
13240
|
}
|
|
@@ -13196,7 +13242,7 @@ async function readTokenData(deps, tokenId) {
|
|
|
13196
13242
|
if (bytes === void 0) {
|
|
13197
13243
|
throw new SphereError(`Token ${tokenId} has no blob in storage`, "STORAGE_ERROR");
|
|
13198
13244
|
}
|
|
13199
|
-
return deps.engine.
|
|
13245
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
13200
13246
|
}
|
|
13201
13247
|
|
|
13202
13248
|
// modules/payments-v2/history/History.ts
|
|
@@ -15032,6 +15078,7 @@ function mintParams(recipientPubkey, coinId, amount) {
|
|
|
15032
15078
|
|
|
15033
15079
|
// modules/payments-v2/mint.ts
|
|
15034
15080
|
var ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
15081
|
+
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
15035
15082
|
function runMintUnderJournal(deps, input) {
|
|
15036
15083
|
return mintUnderJournal(deps, input);
|
|
15037
15084
|
}
|
|
@@ -15084,6 +15131,15 @@ async function finalizeMint(deps, input) {
|
|
|
15084
15131
|
deps.noteHeldState(token.blob.tokenId, (await deps.engine.deliveryKeys(bytes)).stateHash);
|
|
15085
15132
|
deps.refreshView();
|
|
15086
15133
|
}
|
|
15134
|
+
async function tokenInServerInventory(storagePort, tokenId) {
|
|
15135
|
+
let page = await storagePort.listInventory();
|
|
15136
|
+
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
15137
|
+
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) return true;
|
|
15138
|
+
if (!page.more) return false;
|
|
15139
|
+
page = await storagePort.listInventory(page.cursor);
|
|
15140
|
+
}
|
|
15141
|
+
return false;
|
|
15142
|
+
}
|
|
15087
15143
|
async function replayCoinMints(deps) {
|
|
15088
15144
|
let resolved = 0;
|
|
15089
15145
|
for (const entry of await deps.mintJournal.list()) {
|
|
@@ -15187,6 +15243,199 @@ async function mintJournaled(deps, entry) {
|
|
|
15187
15243
|
await finalizeMint(deps, { mintId: entry.mintId, token, assets: [], journal: deps.nftMintJournal });
|
|
15188
15244
|
}
|
|
15189
15245
|
|
|
15246
|
+
// modules/payments-v2/mint-custom.ts
|
|
15247
|
+
var HEX32 = /^[0-9a-f]{64}$/;
|
|
15248
|
+
function assertRequest(request) {
|
|
15249
|
+
if (request.tokenType.length !== 32) {
|
|
15250
|
+
throw new SphereError("mintCustom: tokenType must be 32 bytes", "VALIDATION_ERROR");
|
|
15251
|
+
}
|
|
15252
|
+
if (request.salt.length < 16 || request.salt.length > 64) {
|
|
15253
|
+
throw new SphereError("mintCustom: salt must be 16 to 64 bytes", "VALIDATION_ERROR");
|
|
15254
|
+
}
|
|
15255
|
+
if (request.data.length === 0) {
|
|
15256
|
+
throw new SphereError("mintCustom: data must not be empty", "VALIDATION_ERROR");
|
|
15257
|
+
}
|
|
15258
|
+
for (const asset of request.assets) {
|
|
15259
|
+
if (!HEX32.test(asset.coinId) || asset.amount <= 0n) {
|
|
15260
|
+
throw new SphereError("mintCustom: assets need a 64-hex coinId and a positive amount", "VALIDATION_ERROR");
|
|
15261
|
+
}
|
|
15262
|
+
}
|
|
15263
|
+
}
|
|
15264
|
+
async function runCustomMintUnderJournal(deps, input) {
|
|
15265
|
+
try {
|
|
15266
|
+
assertRequest(input.request);
|
|
15267
|
+
} catch (err) {
|
|
15268
|
+
return { success: false, error: messageOf(err) };
|
|
15269
|
+
}
|
|
15270
|
+
const entry = journalEntryOf2(input.mintId, input.request, deps.now());
|
|
15271
|
+
await deps.customMintJournal.upsert(entry);
|
|
15272
|
+
let tokenId;
|
|
15273
|
+
try {
|
|
15274
|
+
tokenId = await mintJournaled2(deps, entry, input.request.mintJustificationVerifiers);
|
|
15275
|
+
} catch (err) {
|
|
15276
|
+
deps.armHeartbeat();
|
|
15277
|
+
return { success: false, error: messageOf(err) };
|
|
15278
|
+
}
|
|
15279
|
+
return { success: true, tokenId };
|
|
15280
|
+
}
|
|
15281
|
+
async function replayCustomMints(deps) {
|
|
15282
|
+
let resolved = 0;
|
|
15283
|
+
for (const entry of await deps.customMintJournal.list()) {
|
|
15284
|
+
if (deps.isActiveOp(entry.mintId)) continue;
|
|
15285
|
+
try {
|
|
15286
|
+
if (entry.tokenId !== "" && await deps.tokenInServerInventory(entry.tokenId)) {
|
|
15287
|
+
await deps.customMintJournal.removeByKey(entry.mintId);
|
|
15288
|
+
} else {
|
|
15289
|
+
await mintJournaled2(deps, entry, void 0);
|
|
15290
|
+
}
|
|
15291
|
+
resolved += 1;
|
|
15292
|
+
} catch {
|
|
15293
|
+
continue;
|
|
15294
|
+
}
|
|
15295
|
+
}
|
|
15296
|
+
return resolved;
|
|
15297
|
+
}
|
|
15298
|
+
function journalEntryOf2(mintId, request, createdAt) {
|
|
15299
|
+
return {
|
|
15300
|
+
mintId,
|
|
15301
|
+
tokenId: "",
|
|
15302
|
+
dataHex: bytesToHex3(request.data),
|
|
15303
|
+
saltHex: bytesToHex3(request.salt),
|
|
15304
|
+
tokenTypeHex: bytesToHex3(request.tokenType),
|
|
15305
|
+
justificationHex: request.justification ? bytesToHex3(request.justification) : null,
|
|
15306
|
+
assets: request.assets.map((a) => ({ coinId: a.coinId, amount: a.amount.toString() })),
|
|
15307
|
+
createdAt
|
|
15308
|
+
};
|
|
15309
|
+
}
|
|
15310
|
+
async function mintJournaled2(deps, entry, mintJustificationVerifiers) {
|
|
15311
|
+
const token = await deps.engine.mintDataToken(
|
|
15312
|
+
{
|
|
15313
|
+
recipientPubkey: deps.ownPubkeyBytes,
|
|
15314
|
+
data: hexToBytes4(entry.dataHex),
|
|
15315
|
+
tokenType: hexToBytes4(entry.tokenTypeHex),
|
|
15316
|
+
salt: hexToBytes4(entry.saltHex),
|
|
15317
|
+
...entry.justificationHex !== null ? { justification: hexToBytes4(entry.justificationHex) } : {},
|
|
15318
|
+
...mintJustificationVerifiers ? { mintJustificationVerifiers } : {}
|
|
15319
|
+
},
|
|
15320
|
+
{ transferId: entry.mintId }
|
|
15321
|
+
);
|
|
15322
|
+
if (entry.tokenId !== "" && token.blob.tokenId !== entry.tokenId) {
|
|
15323
|
+
throw new SphereError(`minted token ${token.blob.tokenId}, but the journal names ${entry.tokenId}`, "VALIDATION_ERROR");
|
|
15324
|
+
}
|
|
15325
|
+
if (entry.tokenId === "") {
|
|
15326
|
+
await deps.customMintJournal.upsert({ ...entry, tokenId: token.blob.tokenId });
|
|
15327
|
+
}
|
|
15328
|
+
await finalizeMint(deps, {
|
|
15329
|
+
mintId: entry.mintId,
|
|
15330
|
+
token,
|
|
15331
|
+
assets: entry.assets,
|
|
15332
|
+
journal: deps.customMintJournal
|
|
15333
|
+
});
|
|
15334
|
+
return token.blob.tokenId;
|
|
15335
|
+
}
|
|
15336
|
+
|
|
15337
|
+
// modules/payments-v2/burn.ts
|
|
15338
|
+
var HEX322 = /^[0-9a-f]{64}$/;
|
|
15339
|
+
async function heldToken2(deps, tokenId) {
|
|
15340
|
+
const bytes = (await deps.storagePort.getBlobs([tokenId])).get(tokenId);
|
|
15341
|
+
if (bytes === void 0) {
|
|
15342
|
+
throw new SphereError(`Token ${tokenId} has no blob in storage; it is not held here`, "STORAGE_ERROR");
|
|
15343
|
+
}
|
|
15344
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
15345
|
+
}
|
|
15346
|
+
function assetsOf(token) {
|
|
15347
|
+
return (token.value?.assets ?? []).map((a) => ({ coinId: a.coinId, amount: a.amount.toString() }));
|
|
15348
|
+
}
|
|
15349
|
+
async function runBurnUnderJournal(deps, input) {
|
|
15350
|
+
const { burnId } = input;
|
|
15351
|
+
const { tokenId, reasonBytes } = input.request;
|
|
15352
|
+
if (!HEX322.test(tokenId) || reasonBytes.length === 0) {
|
|
15353
|
+
return { success: false, burnId, tokenId, error: "burn: tokenId must be 64 hex and reasonBytes non-empty" };
|
|
15354
|
+
}
|
|
15355
|
+
let token;
|
|
15356
|
+
try {
|
|
15357
|
+
token = await heldToken2(deps, tokenId);
|
|
15358
|
+
if (!deps.engine.isOwnedBy(token, deps.engine.getIdentity().chainPubkey)) {
|
|
15359
|
+
throw new SphereError(`Token ${tokenId} is not owned by this wallet`, "VALIDATION_ERROR");
|
|
15360
|
+
}
|
|
15361
|
+
} catch (err) {
|
|
15362
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
15363
|
+
}
|
|
15364
|
+
const entry = {
|
|
15365
|
+
burnId,
|
|
15366
|
+
tokenId,
|
|
15367
|
+
reasonHex: bytesToHex3(reasonBytes),
|
|
15368
|
+
burnedTokenHex: null,
|
|
15369
|
+
settled: false,
|
|
15370
|
+
assets: assetsOf(token),
|
|
15371
|
+
createdAt: deps.now()
|
|
15372
|
+
};
|
|
15373
|
+
await deps.burnJournal.upsert(entry);
|
|
15374
|
+
try {
|
|
15375
|
+
const settled = await burnJournaled(deps, entry, token, { applied: false });
|
|
15376
|
+
return { success: true, burnId, tokenId, burnedToken: hexToBytes4(settled.burnedTokenHex ?? "") };
|
|
15377
|
+
} catch (err) {
|
|
15378
|
+
deps.armHeartbeat();
|
|
15379
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
15380
|
+
}
|
|
15381
|
+
}
|
|
15382
|
+
async function replayBurns(deps) {
|
|
15383
|
+
let progressed = 0;
|
|
15384
|
+
for (const entry of await deps.burnJournal.list()) {
|
|
15385
|
+
if (entry.settled || deps.isActiveOp(entry.burnId)) continue;
|
|
15386
|
+
try {
|
|
15387
|
+
const token = entry.burnedTokenHex === null ? await heldToken2(deps, entry.tokenId) : null;
|
|
15388
|
+
const applied = entry.burnedTokenHex !== null && !await deps.tokenInServerInventory(entry.tokenId);
|
|
15389
|
+
await burnJournaled(deps, entry, token, { applied });
|
|
15390
|
+
progressed += 1;
|
|
15391
|
+
} catch {
|
|
15392
|
+
continue;
|
|
15393
|
+
}
|
|
15394
|
+
}
|
|
15395
|
+
return progressed;
|
|
15396
|
+
}
|
|
15397
|
+
async function pendingBurns(deps) {
|
|
15398
|
+
return (await deps.burnJournal.list()).map((entry) => ({
|
|
15399
|
+
burnId: entry.burnId,
|
|
15400
|
+
tokenId: entry.tokenId,
|
|
15401
|
+
reasonBytes: hexToBytes4(entry.reasonHex),
|
|
15402
|
+
burnedToken: entry.burnedTokenHex === null ? null : hexToBytes4(entry.burnedTokenHex),
|
|
15403
|
+
settled: entry.settled,
|
|
15404
|
+
createdAt: entry.createdAt
|
|
15405
|
+
}));
|
|
15406
|
+
}
|
|
15407
|
+
async function acknowledgeBurn(deps, burnId) {
|
|
15408
|
+
const entry = await deps.burnJournal.getByKey(burnId);
|
|
15409
|
+
if (entry === void 0) return;
|
|
15410
|
+
if (!entry.settled) {
|
|
15411
|
+
throw new SphereError(`Burn ${burnId} is not settled yet; it cannot be acknowledged`, "VALIDATION_ERROR");
|
|
15412
|
+
}
|
|
15413
|
+
await deps.burnJournal.removeByKey(burnId);
|
|
15414
|
+
}
|
|
15415
|
+
async function burnJournaled(deps, entry, token, state) {
|
|
15416
|
+
let current = entry;
|
|
15417
|
+
if (current.burnedTokenHex === null) {
|
|
15418
|
+
if (token === null) throw new SphereError("burn replay needs the held token to certify", "VALIDATION_ERROR");
|
|
15419
|
+
const burned = await deps.engine.burn(
|
|
15420
|
+
{ token, reasonBytes: hexToBytes4(current.reasonHex) },
|
|
15421
|
+
{ transferId: current.burnId }
|
|
15422
|
+
);
|
|
15423
|
+
if (burned.blob.tokenId !== current.tokenId) {
|
|
15424
|
+
throw new SphereError(`burned token ${burned.blob.tokenId}, but the journal names ${current.tokenId}`, "VALIDATION_ERROR");
|
|
15425
|
+
}
|
|
15426
|
+
current = { ...current, burnedTokenHex: bytesToHex3(burned.blob.token) };
|
|
15427
|
+
await deps.burnJournal.upsert(current);
|
|
15428
|
+
}
|
|
15429
|
+
if (!state.applied) {
|
|
15430
|
+
await deps.storagePort.applyDelta({ transferId: current.burnId, spent: [current.tokenId], added: [] });
|
|
15431
|
+
}
|
|
15432
|
+
await deps.recordSent({ transferId: current.burnId, assets: current.assets, tokenId: current.tokenId });
|
|
15433
|
+
current = { ...current, settled: true };
|
|
15434
|
+
await deps.burnJournal.upsert(current);
|
|
15435
|
+
deps.refreshView();
|
|
15436
|
+
return current;
|
|
15437
|
+
}
|
|
15438
|
+
|
|
15190
15439
|
// modules/payments-v2/send-whole.ts
|
|
15191
15440
|
async function materializeWholeSpend(deps, input) {
|
|
15192
15441
|
const { engine } = deps;
|
|
@@ -15372,7 +15621,6 @@ async function takeSourceBlobs(deps, sourceIds) {
|
|
|
15372
15621
|
|
|
15373
15622
|
// modules/payments-v2/PaymentsFacade.ts
|
|
15374
15623
|
var MAX_RESELECT = 8;
|
|
15375
|
-
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
15376
15624
|
var PaymentsFacade = class {
|
|
15377
15625
|
constructor(deps) {
|
|
15378
15626
|
this.deps = deps;
|
|
@@ -15409,6 +15657,8 @@ var PaymentsFacade = class {
|
|
|
15409
15657
|
refreshView: () => this.view.delta(),
|
|
15410
15658
|
replayMints: () => replayCoinMints(this.mintDeps()),
|
|
15411
15659
|
replayNftMints: () => replayNftMints(this.mintDeps()),
|
|
15660
|
+
replayCustomMints: () => replayCustomMints(this.mintDeps()),
|
|
15661
|
+
replayBurns: () => replayBurns(this.mintDeps()),
|
|
15412
15662
|
isActiveOp: (id) => this.activeMoneyOps.has(id),
|
|
15413
15663
|
emit: deps.emit,
|
|
15414
15664
|
now: () => this.nowMs()
|
|
@@ -15504,6 +15754,9 @@ var PaymentsFacade = class {
|
|
|
15504
15754
|
tokenData(tokenId) {
|
|
15505
15755
|
return readTokenData(this.readDeps(), tokenId);
|
|
15506
15756
|
}
|
|
15757
|
+
tokenJustification(tokenId) {
|
|
15758
|
+
return readTokenJustification(this.readDeps(), tokenId);
|
|
15759
|
+
}
|
|
15507
15760
|
nft(tokenId) {
|
|
15508
15761
|
return readNft(this.readDeps(), tokenId);
|
|
15509
15762
|
}
|
|
@@ -15552,8 +15805,12 @@ var PaymentsFacade = class {
|
|
|
15552
15805
|
return this.track(this.mintInner(coinId, amount));
|
|
15553
15806
|
}
|
|
15554
15807
|
mintNft(request) {
|
|
15555
|
-
return this.track(this.
|
|
15808
|
+
return this.track(this.ownedOp((mintId) => runNftMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
15556
15809
|
}
|
|
15810
|
+
mintCustom = (request) => this.track(this.ownedOp((mintId) => runCustomMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
15811
|
+
burn = (request) => this.track(this.ownedOp((burnId) => runBurnUnderJournal(this.mintDeps(), { burnId, request })));
|
|
15812
|
+
pendingBurns = () => this.track(pendingBurns(this.mintDeps()));
|
|
15813
|
+
acknowledgeBurn = (burnId) => this.track(acknowledgeBurn(this.mintDeps(), burnId));
|
|
15557
15814
|
// §4 retry verb: coalesces onto the running pass — NEVER re-issue send() (#631/#676).
|
|
15558
15815
|
async resumeNow() {
|
|
15559
15816
|
if (!this.started) return;
|
|
@@ -15577,12 +15834,15 @@ var PaymentsFacade = class {
|
|
|
15577
15834
|
nowMs() {
|
|
15578
15835
|
return (this.deps.now ?? Date.now)();
|
|
15579
15836
|
}
|
|
15580
|
-
/** One snapshot serves the coin and the
|
|
15837
|
+
/** One snapshot serves the coin, NFT and custom mints and the burn, live and replayed. */
|
|
15581
15838
|
mintDeps() {
|
|
15582
15839
|
return {
|
|
15583
15840
|
engine: this.engine(),
|
|
15584
15841
|
mintJournal: this.machineStores.mintJournal,
|
|
15585
15842
|
nftMintJournal: this.machineStores.nftMintJournal,
|
|
15843
|
+
customMintJournal: this.machineStores.customMintJournal,
|
|
15844
|
+
burnJournal: this.machineStores.burnJournal,
|
|
15845
|
+
recordSent: (input) => this.historyStore.recordSent(input),
|
|
15586
15846
|
storagePort: this.deps.storagePort,
|
|
15587
15847
|
recordMint: (input) => this.historyStore.recordMint(input),
|
|
15588
15848
|
armHeartbeat: () => this.heartbeat.arm(),
|
|
@@ -15591,7 +15851,7 @@ var PaymentsFacade = class {
|
|
|
15591
15851
|
ownPubkeyBytes: this.ownPubkeyBytes,
|
|
15592
15852
|
nftTokenType: this.deps.nftTokenType,
|
|
15593
15853
|
isActiveOp: (mintId) => this.activeMoneyOps.has(mintId),
|
|
15594
|
-
tokenInServerInventory: (tokenId) => this.
|
|
15854
|
+
tokenInServerInventory: (tokenId) => tokenInServerInventory(this.deps.storagePort, tokenId),
|
|
15595
15855
|
emit: this.deps.emit,
|
|
15596
15856
|
now: () => this.nowMs()
|
|
15597
15857
|
};
|
|
@@ -15912,28 +16172,17 @@ var PaymentsFacade = class {
|
|
|
15912
16172
|
if (!/^(?:[0-9a-f]{2})+$/.test(coinId) || amount <= 0n) {
|
|
15913
16173
|
return { success: false, error: "coinId must be even-length lowercase hex and amount positive" };
|
|
15914
16174
|
}
|
|
15915
|
-
return this.
|
|
16175
|
+
return this.ownedOp((mintId) => runMintUnderJournal(this.mintDeps(), { mintId, coinId, amount }));
|
|
15916
16176
|
}
|
|
15917
|
-
/** A live
|
|
15918
|
-
async
|
|
15919
|
-
const
|
|
15920
|
-
this.activeMoneyOps.add(
|
|
16177
|
+
/** A live money op under a fresh id — its replay stays hands-off while this attempt runs (§7). */
|
|
16178
|
+
async ownedOp(run) {
|
|
16179
|
+
const id = this.newId();
|
|
16180
|
+
this.activeMoneyOps.add(id);
|
|
15921
16181
|
try {
|
|
15922
|
-
return await run(
|
|
16182
|
+
return await run(id);
|
|
15923
16183
|
} finally {
|
|
15924
|
-
this.activeMoneyOps.delete(
|
|
15925
|
-
}
|
|
15926
|
-
}
|
|
15927
|
-
async tokenInServerInventory(tokenId) {
|
|
15928
|
-
let page = await this.deps.storagePort.listInventory();
|
|
15929
|
-
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
15930
|
-
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) {
|
|
15931
|
-
return true;
|
|
15932
|
-
}
|
|
15933
|
-
if (!page.more) return false;
|
|
15934
|
-
page = await this.deps.storagePort.listInventory(page.cursor);
|
|
16184
|
+
this.activeMoneyOps.delete(id);
|
|
15935
16185
|
}
|
|
15936
|
-
return false;
|
|
15937
16186
|
}
|
|
15938
16187
|
// ── wiring internals ───────────────────────────────────────────────────────
|
|
15939
16188
|
engine() {
|
|
@@ -17325,6 +17574,8 @@ var Sphere = class _Sphere {
|
|
|
17325
17574
|
* the rebuild after an api-key change share one pool configuration.
|
|
17326
17575
|
*/
|
|
17327
17576
|
_verification;
|
|
17577
|
+
/** Token plugins (SphereInitOptions.plugins), applied to every engine like _verification. */
|
|
17578
|
+
_plugins;
|
|
17328
17579
|
/** This Sphere's OWN token registry. Disposed by destroy(); never the process global. */
|
|
17329
17580
|
_registry = null;
|
|
17330
17581
|
// Events
|
|
@@ -17438,6 +17689,7 @@ var Sphere = class _Sphere {
|
|
|
17438
17689
|
communications: options.communications,
|
|
17439
17690
|
password: options.password,
|
|
17440
17691
|
verification: options.verification,
|
|
17692
|
+
plugins: options.plugins,
|
|
17441
17693
|
discoverAddresses: options.discoverAddresses,
|
|
17442
17694
|
onProgress: options.onProgress
|
|
17443
17695
|
});
|
|
@@ -17475,6 +17727,7 @@ var Sphere = class _Sphere {
|
|
|
17475
17727
|
communications: options.communications,
|
|
17476
17728
|
password: options.password,
|
|
17477
17729
|
verification: options.verification,
|
|
17730
|
+
plugins: options.plugins,
|
|
17478
17731
|
discoverAddresses: options.discoverAddresses,
|
|
17479
17732
|
onProgress: options.onProgress
|
|
17480
17733
|
});
|
|
@@ -17653,6 +17906,7 @@ var Sphere = class _Sphere {
|
|
|
17653
17906
|
options.communications
|
|
17654
17907
|
);
|
|
17655
17908
|
sphere._verification = options.verification;
|
|
17909
|
+
sphere._plugins = options.plugins;
|
|
17656
17910
|
sphere._paymentsV2Composition = composition;
|
|
17657
17911
|
sphere._password = options.password ?? null;
|
|
17658
17912
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -17717,6 +17971,7 @@ var Sphere = class _Sphere {
|
|
|
17717
17971
|
options.communications
|
|
17718
17972
|
);
|
|
17719
17973
|
sphere._verification = options.verification;
|
|
17974
|
+
sphere._plugins = options.plugins;
|
|
17720
17975
|
sphere._paymentsV2Composition = composition;
|
|
17721
17976
|
sphere._password = options.password ?? null;
|
|
17722
17977
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -17803,6 +18058,7 @@ var Sphere = class _Sphere {
|
|
|
17803
18058
|
options.communications
|
|
17804
18059
|
);
|
|
17805
18060
|
sphere._verification = options.verification;
|
|
18061
|
+
sphere._plugins = options.plugins;
|
|
17806
18062
|
sphere._paymentsV2Composition = composition;
|
|
17807
18063
|
sphere._password = options.password ?? null;
|
|
17808
18064
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -20113,7 +20369,8 @@ var Sphere = class _Sphere {
|
|
|
20113
20369
|
apiKey: oracle.getApiKey?.(),
|
|
20114
20370
|
privateKey: hexToBytes4(privateKey),
|
|
20115
20371
|
trustBaseJson,
|
|
20116
|
-
...this._verification ? { verification: this._verification } : {}
|
|
20372
|
+
...this._verification ? { verification: this._verification } : {},
|
|
20373
|
+
...this._plugins ? { plugins: this._plugins } : {}
|
|
20117
20374
|
});
|
|
20118
20375
|
} catch (err) {
|
|
20119
20376
|
logger.warn(
|