@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.cjs
CHANGED
|
@@ -10460,6 +10460,19 @@ async function readTokenNft(token) {
|
|
|
10460
10460
|
}
|
|
10461
10461
|
}
|
|
10462
10462
|
|
|
10463
|
+
// token-engine/plugin-ops.ts
|
|
10464
|
+
function contextWithMintVerifiers(base, verifiers) {
|
|
10465
|
+
const registry = new import_MintJustificationVerifierService.MintJustificationVerifierService();
|
|
10466
|
+
for (const verifier of verifiers) registry.register(verifier);
|
|
10467
|
+
return new import_VerificationContext.VerificationContext(
|
|
10468
|
+
base.trustBase,
|
|
10469
|
+
base.predicateVerifier,
|
|
10470
|
+
base.unicityCertificateVerifier,
|
|
10471
|
+
registry,
|
|
10472
|
+
new import_TokenIssuanceVerifierService.TokenIssuanceVerifierService(false)
|
|
10473
|
+
);
|
|
10474
|
+
}
|
|
10475
|
+
|
|
10463
10476
|
// token-engine/split-checkpoint.ts
|
|
10464
10477
|
var CHECKPOINT_VERSION = 2;
|
|
10465
10478
|
var CHECKPOINT_SDK_VERSION = "@unicitylabs/state-transition-sdk@3.0.1";
|
|
@@ -10606,6 +10619,10 @@ var SphereTokenEngine = class {
|
|
|
10606
10619
|
const data = token.sdkToken.genesis.data;
|
|
10607
10620
|
return data ? new Uint8Array(data) : null;
|
|
10608
10621
|
}
|
|
10622
|
+
readTokenJustification(token) {
|
|
10623
|
+
const justification = token.sdkToken.genesis.justification;
|
|
10624
|
+
return justification ? new Uint8Array(justification) : null;
|
|
10625
|
+
}
|
|
10609
10626
|
readNft(token) {
|
|
10610
10627
|
return readTokenNft(token);
|
|
10611
10628
|
}
|
|
@@ -10637,7 +10654,7 @@ var SphereTokenEngine = class {
|
|
|
10637
10654
|
const recipient = import_SignaturePredicate.SignaturePredicate.create(params.recipientPubkey);
|
|
10638
10655
|
const tokenType = params.tokenType ? new import_TokenType.TokenType(params.tokenType) : import_TokenType.TokenType.generate();
|
|
10639
10656
|
const salt = params.salt ? import_TokenSalt.TokenSalt.fromBytes(params.salt) : import_TokenSalt.TokenSalt.generate();
|
|
10640
|
-
const mintTx = await import_MintTransaction.MintTransaction.create(this.deps.networkId, recipient, { data: params.data, tokenType, salt });
|
|
10657
|
+
const mintTx = await import_MintTransaction.MintTransaction.create(this.deps.networkId, recipient, { data: params.data, tokenType, salt, justification: params.justification ?? null });
|
|
10641
10658
|
const certificationData = await import_CertificationData.CertificationData.fromMintTransaction(mintTx);
|
|
10642
10659
|
const proof = await this.submitAndAwaitProof(
|
|
10643
10660
|
certificationData,
|
|
@@ -10647,34 +10664,30 @@ var SphereTokenEngine = class {
|
|
|
10647
10664
|
options
|
|
10648
10665
|
);
|
|
10649
10666
|
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
|
|
10650
|
-
const
|
|
10651
|
-
return wrapToken(
|
|
10667
|
+
const verifiers = params.mintJustificationVerifiers;
|
|
10668
|
+
return wrapToken(await import_Token.Token.mint(certified, verifiers ? contextWithMintVerifiers(this.deps, verifiers) : this.deps.verificationContext));
|
|
10652
10669
|
}
|
|
10653
10670
|
buildNftMint(params) {
|
|
10654
10671
|
return planNftMint(this.deps, params);
|
|
10655
10672
|
}
|
|
10656
10673
|
async transfer(params, options) {
|
|
10657
10674
|
this.assertOwned(params.token);
|
|
10658
|
-
|
|
10659
|
-
|
|
10675
|
+
return this.spend(params.token, import_SignaturePredicate.SignaturePredicate.create(params.recipientPubkey), params.data ?? null, options, "Transfer certification failed");
|
|
10676
|
+
}
|
|
10677
|
+
async burn(params, options) {
|
|
10678
|
+
this.assertOwned(params.token);
|
|
10679
|
+
return this.spend(params.token, import_BurnPredicate.BurnPredicate.create(sha256(params.reasonBytes)), params.reasonBytes, options, "Burn certification failed");
|
|
10680
|
+
}
|
|
10681
|
+
async spend(token, recipient, data, options, failLabel) {
|
|
10660
10682
|
const stateMask = import_StateMask.StateMask.fromBytes(
|
|
10661
|
-
deriveRealization(this.privateKeyHex,
|
|
10683
|
+
deriveRealization(this.privateKeyHex, this.resolveTransferId(options), this.resolveOpIndex(options), "stateMask")
|
|
10662
10684
|
);
|
|
10663
|
-
const transferTx = await import_TransferTransaction.TransferTransaction.create(
|
|
10664
|
-
data: params.data ?? null
|
|
10665
|
-
});
|
|
10685
|
+
const transferTx = await import_TransferTransaction.TransferTransaction.create(token.sdkToken, recipient, stateMask, { data });
|
|
10666
10686
|
const unlockScript = await import_SignaturePredicateUnlockScript.SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);
|
|
10667
10687
|
const certificationData = await import_CertificationData.CertificationData.fromTransaction(transferTx, unlockScript);
|
|
10668
|
-
const proof = await this.submitAndAwaitProof(
|
|
10669
|
-
certificationData,
|
|
10670
|
-
transferTx,
|
|
10671
|
-
"Transfer certification failed",
|
|
10672
|
-
"TRANSFER_FAILED",
|
|
10673
|
-
options
|
|
10674
|
-
);
|
|
10688
|
+
const proof = await this.submitAndAwaitProof(certificationData, transferTx, failLabel, "TRANSFER_FAILED", options);
|
|
10675
10689
|
const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
|
|
10676
|
-
|
|
10677
|
-
return wrapToken(transferred);
|
|
10690
|
+
return wrapToken(await token.sdkToken.transfer(certified, this.deps.verificationContext));
|
|
10678
10691
|
}
|
|
10679
10692
|
async split(params, options) {
|
|
10680
10693
|
this.assertOwned(params.token);
|
|
@@ -10989,6 +11002,20 @@ var SphereTokenEngine = class {
|
|
|
10989
11002
|
|
|
10990
11003
|
// token-engine/factory.ts
|
|
10991
11004
|
var DEFAULT_VERIFICATION_POOL_SIZE = 4;
|
|
11005
|
+
function registerPluginVerifiers(registry, plugins) {
|
|
11006
|
+
for (const plugin of plugins ?? []) {
|
|
11007
|
+
for (const verifier of plugin.mintJustificationVerifiers ?? []) {
|
|
11008
|
+
try {
|
|
11009
|
+
registry.register(verifier);
|
|
11010
|
+
} catch (err) {
|
|
11011
|
+
throw new SphereError(
|
|
11012
|
+
`Token plugin '${plugin.id}' registers mint-reason tag ${verifier.tag} twice or over another plugin's: ` + (err instanceof Error ? err.message : String(err)),
|
|
11013
|
+
"INVALID_CONFIG"
|
|
11014
|
+
);
|
|
11015
|
+
}
|
|
11016
|
+
}
|
|
11017
|
+
}
|
|
11018
|
+
}
|
|
10992
11019
|
function disposedError() {
|
|
10993
11020
|
return new SphereError(
|
|
10994
11021
|
"Verification worker pool disposed \u2014 the in-flight verification was cancelled",
|
|
@@ -11070,6 +11097,7 @@ async function createSphereTokenEngine(config) {
|
|
|
11070
11097
|
mintJustificationVerifier.register(
|
|
11071
11098
|
new import_SplitMintJustificationVerifier.SplitMintJustificationVerifier(decodeSpherePaymentData)
|
|
11072
11099
|
);
|
|
11100
|
+
registerPluginVerifiers(mintJustificationVerifier, config.plugins);
|
|
11073
11101
|
const deps = {
|
|
11074
11102
|
client: new import_StateTransitionClient.StateTransitionClient(new import_AggregatorClient.AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
|
|
11075
11103
|
trustBase,
|
|
@@ -12187,6 +12215,8 @@ var STORE_KEYS = {
|
|
|
12187
12215
|
deliveryJournal: "delivery-journal",
|
|
12188
12216
|
mintJournal: "mint-journal",
|
|
12189
12217
|
nftMintJournal: "nft-mint-journal",
|
|
12218
|
+
customMintJournal: "custom-mint-journal",
|
|
12219
|
+
burnJournal: "burn-journal",
|
|
12190
12220
|
shortfalls: "shortfalls",
|
|
12191
12221
|
settlingLinks: "settling",
|
|
12192
12222
|
streamCursor: (s) => `cursor:${s}`,
|
|
@@ -12371,6 +12401,8 @@ function createMachineStores(kv) {
|
|
|
12371
12401
|
deliveryJournal: new DeliveryJournal(kv),
|
|
12372
12402
|
mintJournal: new ListStore(kv, STORE_KEYS.mintJournal, (e) => e.mintId),
|
|
12373
12403
|
nftMintJournal: new ListStore(kv, STORE_KEYS.nftMintJournal, (e) => e.mintId),
|
|
12404
|
+
customMintJournal: new ListStore(kv, STORE_KEYS.customMintJournal, (e) => e.mintId),
|
|
12405
|
+
burnJournal: new ListStore(kv, STORE_KEYS.burnJournal, (e) => e.burnId),
|
|
12374
12406
|
shortfalls: new ListStore(kv, STORE_KEYS.shortfalls, (e) => e.transferId)
|
|
12375
12407
|
};
|
|
12376
12408
|
}
|
|
@@ -13078,7 +13110,12 @@ var Converger = class {
|
|
|
13078
13110
|
} catch (err) {
|
|
13079
13111
|
retryAfterMs ??= retryAfterMsOf(err);
|
|
13080
13112
|
}
|
|
13081
|
-
for (const replay of [
|
|
13113
|
+
for (const replay of [
|
|
13114
|
+
() => this.deps.replayMints(),
|
|
13115
|
+
() => this.deps.replayNftMints(),
|
|
13116
|
+
() => this.deps.replayCustomMints(),
|
|
13117
|
+
() => this.deps.replayBurns()
|
|
13118
|
+
]) {
|
|
13082
13119
|
try {
|
|
13083
13120
|
progress = await replay() > 0 || progress;
|
|
13084
13121
|
} catch (err) {
|
|
@@ -13089,14 +13126,17 @@ var Converger = class {
|
|
|
13089
13126
|
}
|
|
13090
13127
|
/** §4 heartbeat predicate — read fresh from the §6 stores, never cached. */
|
|
13091
13128
|
async pendingWork() {
|
|
13092
|
-
const [backstop, journal, mints, nftMints] = await Promise.all([
|
|
13129
|
+
const [backstop, journal, mints, nftMints, customMints, burns] = await Promise.all([
|
|
13093
13130
|
this.deps.stores.backstop.list(),
|
|
13094
13131
|
this.deps.stores.deliveryJournal.list(),
|
|
13095
13132
|
this.deps.stores.mintJournal.list(),
|
|
13096
|
-
this.deps.stores.nftMintJournal.list()
|
|
13133
|
+
this.deps.stores.nftMintJournal.list(),
|
|
13134
|
+
this.deps.stores.customMintJournal.list(),
|
|
13135
|
+
this.deps.stores.burnJournal.list()
|
|
13097
13136
|
]);
|
|
13098
|
-
const mintsOpen = mints.length > 0 || nftMints.length > 0;
|
|
13099
|
-
|
|
13137
|
+
const mintsOpen = mints.length > 0 || nftMints.length > 0 || customMints.length > 0;
|
|
13138
|
+
const burnsOpen = burns.some((e) => !e.settled);
|
|
13139
|
+
return backstop.length > 0 || journal.some((e) => e.undeliverable !== true) || mintsOpen || burnsOpen;
|
|
13100
13140
|
}
|
|
13101
13141
|
async resumePass() {
|
|
13102
13142
|
const report = await resumeAll(this.filteredMachineDeps(), {
|
|
@@ -13328,6 +13368,12 @@ async function decodeView(deps, tokenId, bytes) {
|
|
|
13328
13368
|
|
|
13329
13369
|
// modules/payments-v2/inventory/token-data.ts
|
|
13330
13370
|
async function readTokenData(deps, tokenId) {
|
|
13371
|
+
return deps.engine.readTokenData(await heldToken(deps, tokenId));
|
|
13372
|
+
}
|
|
13373
|
+
async function readTokenJustification(deps, tokenId) {
|
|
13374
|
+
return deps.engine.readTokenJustification(await heldToken(deps, tokenId));
|
|
13375
|
+
}
|
|
13376
|
+
async function heldToken(deps, tokenId) {
|
|
13331
13377
|
if (deps.view.stateHashOf(tokenId) === void 0) {
|
|
13332
13378
|
throw new SphereError(`Token ${tokenId} is not in inventory`, "VALIDATION_ERROR");
|
|
13333
13379
|
}
|
|
@@ -13335,7 +13381,7 @@ async function readTokenData(deps, tokenId) {
|
|
|
13335
13381
|
if (bytes === void 0) {
|
|
13336
13382
|
throw new SphereError(`Token ${tokenId} has no blob in storage`, "STORAGE_ERROR");
|
|
13337
13383
|
}
|
|
13338
|
-
return deps.engine.
|
|
13384
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
13339
13385
|
}
|
|
13340
13386
|
|
|
13341
13387
|
// modules/payments-v2/history/History.ts
|
|
@@ -15171,6 +15217,7 @@ function mintParams(recipientPubkey, coinId, amount) {
|
|
|
15171
15217
|
|
|
15172
15218
|
// modules/payments-v2/mint.ts
|
|
15173
15219
|
var ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
15220
|
+
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
15174
15221
|
function runMintUnderJournal(deps, input) {
|
|
15175
15222
|
return mintUnderJournal(deps, input);
|
|
15176
15223
|
}
|
|
@@ -15223,6 +15270,15 @@ async function finalizeMint(deps, input) {
|
|
|
15223
15270
|
deps.noteHeldState(token.blob.tokenId, (await deps.engine.deliveryKeys(bytes)).stateHash);
|
|
15224
15271
|
deps.refreshView();
|
|
15225
15272
|
}
|
|
15273
|
+
async function tokenInServerInventory(storagePort, tokenId) {
|
|
15274
|
+
let page = await storagePort.listInventory();
|
|
15275
|
+
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
15276
|
+
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) return true;
|
|
15277
|
+
if (!page.more) return false;
|
|
15278
|
+
page = await storagePort.listInventory(page.cursor);
|
|
15279
|
+
}
|
|
15280
|
+
return false;
|
|
15281
|
+
}
|
|
15226
15282
|
async function replayCoinMints(deps) {
|
|
15227
15283
|
let resolved = 0;
|
|
15228
15284
|
for (const entry of await deps.mintJournal.list()) {
|
|
@@ -15326,6 +15382,199 @@ async function mintJournaled(deps, entry) {
|
|
|
15326
15382
|
await finalizeMint(deps, { mintId: entry.mintId, token, assets: [], journal: deps.nftMintJournal });
|
|
15327
15383
|
}
|
|
15328
15384
|
|
|
15385
|
+
// modules/payments-v2/mint-custom.ts
|
|
15386
|
+
var HEX32 = /^[0-9a-f]{64}$/;
|
|
15387
|
+
function assertRequest(request) {
|
|
15388
|
+
if (request.tokenType.length !== 32) {
|
|
15389
|
+
throw new SphereError("mintCustom: tokenType must be 32 bytes", "VALIDATION_ERROR");
|
|
15390
|
+
}
|
|
15391
|
+
if (request.salt.length < 16 || request.salt.length > 64) {
|
|
15392
|
+
throw new SphereError("mintCustom: salt must be 16 to 64 bytes", "VALIDATION_ERROR");
|
|
15393
|
+
}
|
|
15394
|
+
if (request.data.length === 0) {
|
|
15395
|
+
throw new SphereError("mintCustom: data must not be empty", "VALIDATION_ERROR");
|
|
15396
|
+
}
|
|
15397
|
+
for (const asset of request.assets) {
|
|
15398
|
+
if (!HEX32.test(asset.coinId) || asset.amount <= 0n) {
|
|
15399
|
+
throw new SphereError("mintCustom: assets need a 64-hex coinId and a positive amount", "VALIDATION_ERROR");
|
|
15400
|
+
}
|
|
15401
|
+
}
|
|
15402
|
+
}
|
|
15403
|
+
async function runCustomMintUnderJournal(deps, input) {
|
|
15404
|
+
try {
|
|
15405
|
+
assertRequest(input.request);
|
|
15406
|
+
} catch (err) {
|
|
15407
|
+
return { success: false, error: messageOf(err) };
|
|
15408
|
+
}
|
|
15409
|
+
const entry = journalEntryOf2(input.mintId, input.request, deps.now());
|
|
15410
|
+
await deps.customMintJournal.upsert(entry);
|
|
15411
|
+
let tokenId;
|
|
15412
|
+
try {
|
|
15413
|
+
tokenId = await mintJournaled2(deps, entry, input.request.mintJustificationVerifiers);
|
|
15414
|
+
} catch (err) {
|
|
15415
|
+
deps.armHeartbeat();
|
|
15416
|
+
return { success: false, error: messageOf(err) };
|
|
15417
|
+
}
|
|
15418
|
+
return { success: true, tokenId };
|
|
15419
|
+
}
|
|
15420
|
+
async function replayCustomMints(deps) {
|
|
15421
|
+
let resolved = 0;
|
|
15422
|
+
for (const entry of await deps.customMintJournal.list()) {
|
|
15423
|
+
if (deps.isActiveOp(entry.mintId)) continue;
|
|
15424
|
+
try {
|
|
15425
|
+
if (entry.tokenId !== "" && await deps.tokenInServerInventory(entry.tokenId)) {
|
|
15426
|
+
await deps.customMintJournal.removeByKey(entry.mintId);
|
|
15427
|
+
} else {
|
|
15428
|
+
await mintJournaled2(deps, entry, void 0);
|
|
15429
|
+
}
|
|
15430
|
+
resolved += 1;
|
|
15431
|
+
} catch {
|
|
15432
|
+
continue;
|
|
15433
|
+
}
|
|
15434
|
+
}
|
|
15435
|
+
return resolved;
|
|
15436
|
+
}
|
|
15437
|
+
function journalEntryOf2(mintId, request, createdAt) {
|
|
15438
|
+
return {
|
|
15439
|
+
mintId,
|
|
15440
|
+
tokenId: "",
|
|
15441
|
+
dataHex: bytesToHex3(request.data),
|
|
15442
|
+
saltHex: bytesToHex3(request.salt),
|
|
15443
|
+
tokenTypeHex: bytesToHex3(request.tokenType),
|
|
15444
|
+
justificationHex: request.justification ? bytesToHex3(request.justification) : null,
|
|
15445
|
+
assets: request.assets.map((a) => ({ coinId: a.coinId, amount: a.amount.toString() })),
|
|
15446
|
+
createdAt
|
|
15447
|
+
};
|
|
15448
|
+
}
|
|
15449
|
+
async function mintJournaled2(deps, entry, mintJustificationVerifiers) {
|
|
15450
|
+
const token = await deps.engine.mintDataToken(
|
|
15451
|
+
{
|
|
15452
|
+
recipientPubkey: deps.ownPubkeyBytes,
|
|
15453
|
+
data: hexToBytes4(entry.dataHex),
|
|
15454
|
+
tokenType: hexToBytes4(entry.tokenTypeHex),
|
|
15455
|
+
salt: hexToBytes4(entry.saltHex),
|
|
15456
|
+
...entry.justificationHex !== null ? { justification: hexToBytes4(entry.justificationHex) } : {},
|
|
15457
|
+
...mintJustificationVerifiers ? { mintJustificationVerifiers } : {}
|
|
15458
|
+
},
|
|
15459
|
+
{ transferId: entry.mintId }
|
|
15460
|
+
);
|
|
15461
|
+
if (entry.tokenId !== "" && token.blob.tokenId !== entry.tokenId) {
|
|
15462
|
+
throw new SphereError(`minted token ${token.blob.tokenId}, but the journal names ${entry.tokenId}`, "VALIDATION_ERROR");
|
|
15463
|
+
}
|
|
15464
|
+
if (entry.tokenId === "") {
|
|
15465
|
+
await deps.customMintJournal.upsert({ ...entry, tokenId: token.blob.tokenId });
|
|
15466
|
+
}
|
|
15467
|
+
await finalizeMint(deps, {
|
|
15468
|
+
mintId: entry.mintId,
|
|
15469
|
+
token,
|
|
15470
|
+
assets: entry.assets,
|
|
15471
|
+
journal: deps.customMintJournal
|
|
15472
|
+
});
|
|
15473
|
+
return token.blob.tokenId;
|
|
15474
|
+
}
|
|
15475
|
+
|
|
15476
|
+
// modules/payments-v2/burn.ts
|
|
15477
|
+
var HEX322 = /^[0-9a-f]{64}$/;
|
|
15478
|
+
async function heldToken2(deps, tokenId) {
|
|
15479
|
+
const bytes = (await deps.storagePort.getBlobs([tokenId])).get(tokenId);
|
|
15480
|
+
if (bytes === void 0) {
|
|
15481
|
+
throw new SphereError(`Token ${tokenId} has no blob in storage; it is not held here`, "STORAGE_ERROR");
|
|
15482
|
+
}
|
|
15483
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
15484
|
+
}
|
|
15485
|
+
function assetsOf(token) {
|
|
15486
|
+
return (token.value?.assets ?? []).map((a) => ({ coinId: a.coinId, amount: a.amount.toString() }));
|
|
15487
|
+
}
|
|
15488
|
+
async function runBurnUnderJournal(deps, input) {
|
|
15489
|
+
const { burnId } = input;
|
|
15490
|
+
const { tokenId, reasonBytes } = input.request;
|
|
15491
|
+
if (!HEX322.test(tokenId) || reasonBytes.length === 0) {
|
|
15492
|
+
return { success: false, burnId, tokenId, error: "burn: tokenId must be 64 hex and reasonBytes non-empty" };
|
|
15493
|
+
}
|
|
15494
|
+
let token;
|
|
15495
|
+
try {
|
|
15496
|
+
token = await heldToken2(deps, tokenId);
|
|
15497
|
+
if (!deps.engine.isOwnedBy(token, deps.engine.getIdentity().chainPubkey)) {
|
|
15498
|
+
throw new SphereError(`Token ${tokenId} is not owned by this wallet`, "VALIDATION_ERROR");
|
|
15499
|
+
}
|
|
15500
|
+
} catch (err) {
|
|
15501
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
15502
|
+
}
|
|
15503
|
+
const entry = {
|
|
15504
|
+
burnId,
|
|
15505
|
+
tokenId,
|
|
15506
|
+
reasonHex: bytesToHex3(reasonBytes),
|
|
15507
|
+
burnedTokenHex: null,
|
|
15508
|
+
settled: false,
|
|
15509
|
+
assets: assetsOf(token),
|
|
15510
|
+
createdAt: deps.now()
|
|
15511
|
+
};
|
|
15512
|
+
await deps.burnJournal.upsert(entry);
|
|
15513
|
+
try {
|
|
15514
|
+
const settled = await burnJournaled(deps, entry, token, { applied: false });
|
|
15515
|
+
return { success: true, burnId, tokenId, burnedToken: hexToBytes4(settled.burnedTokenHex ?? "") };
|
|
15516
|
+
} catch (err) {
|
|
15517
|
+
deps.armHeartbeat();
|
|
15518
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
15519
|
+
}
|
|
15520
|
+
}
|
|
15521
|
+
async function replayBurns(deps) {
|
|
15522
|
+
let progressed = 0;
|
|
15523
|
+
for (const entry of await deps.burnJournal.list()) {
|
|
15524
|
+
if (entry.settled || deps.isActiveOp(entry.burnId)) continue;
|
|
15525
|
+
try {
|
|
15526
|
+
const token = entry.burnedTokenHex === null ? await heldToken2(deps, entry.tokenId) : null;
|
|
15527
|
+
const applied = entry.burnedTokenHex !== null && !await deps.tokenInServerInventory(entry.tokenId);
|
|
15528
|
+
await burnJournaled(deps, entry, token, { applied });
|
|
15529
|
+
progressed += 1;
|
|
15530
|
+
} catch {
|
|
15531
|
+
continue;
|
|
15532
|
+
}
|
|
15533
|
+
}
|
|
15534
|
+
return progressed;
|
|
15535
|
+
}
|
|
15536
|
+
async function pendingBurns(deps) {
|
|
15537
|
+
return (await deps.burnJournal.list()).map((entry) => ({
|
|
15538
|
+
burnId: entry.burnId,
|
|
15539
|
+
tokenId: entry.tokenId,
|
|
15540
|
+
reasonBytes: hexToBytes4(entry.reasonHex),
|
|
15541
|
+
burnedToken: entry.burnedTokenHex === null ? null : hexToBytes4(entry.burnedTokenHex),
|
|
15542
|
+
settled: entry.settled,
|
|
15543
|
+
createdAt: entry.createdAt
|
|
15544
|
+
}));
|
|
15545
|
+
}
|
|
15546
|
+
async function acknowledgeBurn(deps, burnId) {
|
|
15547
|
+
const entry = await deps.burnJournal.getByKey(burnId);
|
|
15548
|
+
if (entry === void 0) return;
|
|
15549
|
+
if (!entry.settled) {
|
|
15550
|
+
throw new SphereError(`Burn ${burnId} is not settled yet; it cannot be acknowledged`, "VALIDATION_ERROR");
|
|
15551
|
+
}
|
|
15552
|
+
await deps.burnJournal.removeByKey(burnId);
|
|
15553
|
+
}
|
|
15554
|
+
async function burnJournaled(deps, entry, token, state) {
|
|
15555
|
+
let current = entry;
|
|
15556
|
+
if (current.burnedTokenHex === null) {
|
|
15557
|
+
if (token === null) throw new SphereError("burn replay needs the held token to certify", "VALIDATION_ERROR");
|
|
15558
|
+
const burned = await deps.engine.burn(
|
|
15559
|
+
{ token, reasonBytes: hexToBytes4(current.reasonHex) },
|
|
15560
|
+
{ transferId: current.burnId }
|
|
15561
|
+
);
|
|
15562
|
+
if (burned.blob.tokenId !== current.tokenId) {
|
|
15563
|
+
throw new SphereError(`burned token ${burned.blob.tokenId}, but the journal names ${current.tokenId}`, "VALIDATION_ERROR");
|
|
15564
|
+
}
|
|
15565
|
+
current = { ...current, burnedTokenHex: bytesToHex3(burned.blob.token) };
|
|
15566
|
+
await deps.burnJournal.upsert(current);
|
|
15567
|
+
}
|
|
15568
|
+
if (!state.applied) {
|
|
15569
|
+
await deps.storagePort.applyDelta({ transferId: current.burnId, spent: [current.tokenId], added: [] });
|
|
15570
|
+
}
|
|
15571
|
+
await deps.recordSent({ transferId: current.burnId, assets: current.assets, tokenId: current.tokenId });
|
|
15572
|
+
current = { ...current, settled: true };
|
|
15573
|
+
await deps.burnJournal.upsert(current);
|
|
15574
|
+
deps.refreshView();
|
|
15575
|
+
return current;
|
|
15576
|
+
}
|
|
15577
|
+
|
|
15329
15578
|
// modules/payments-v2/send-whole.ts
|
|
15330
15579
|
async function materializeWholeSpend(deps, input) {
|
|
15331
15580
|
const { engine } = deps;
|
|
@@ -15511,7 +15760,6 @@ async function takeSourceBlobs(deps, sourceIds) {
|
|
|
15511
15760
|
|
|
15512
15761
|
// modules/payments-v2/PaymentsFacade.ts
|
|
15513
15762
|
var MAX_RESELECT = 8;
|
|
15514
|
-
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
15515
15763
|
var PaymentsFacade = class {
|
|
15516
15764
|
constructor(deps) {
|
|
15517
15765
|
this.deps = deps;
|
|
@@ -15548,6 +15796,8 @@ var PaymentsFacade = class {
|
|
|
15548
15796
|
refreshView: () => this.view.delta(),
|
|
15549
15797
|
replayMints: () => replayCoinMints(this.mintDeps()),
|
|
15550
15798
|
replayNftMints: () => replayNftMints(this.mintDeps()),
|
|
15799
|
+
replayCustomMints: () => replayCustomMints(this.mintDeps()),
|
|
15800
|
+
replayBurns: () => replayBurns(this.mintDeps()),
|
|
15551
15801
|
isActiveOp: (id) => this.activeMoneyOps.has(id),
|
|
15552
15802
|
emit: deps.emit,
|
|
15553
15803
|
now: () => this.nowMs()
|
|
@@ -15643,6 +15893,9 @@ var PaymentsFacade = class {
|
|
|
15643
15893
|
tokenData(tokenId) {
|
|
15644
15894
|
return readTokenData(this.readDeps(), tokenId);
|
|
15645
15895
|
}
|
|
15896
|
+
tokenJustification(tokenId) {
|
|
15897
|
+
return readTokenJustification(this.readDeps(), tokenId);
|
|
15898
|
+
}
|
|
15646
15899
|
nft(tokenId) {
|
|
15647
15900
|
return readNft(this.readDeps(), tokenId);
|
|
15648
15901
|
}
|
|
@@ -15691,8 +15944,12 @@ var PaymentsFacade = class {
|
|
|
15691
15944
|
return this.track(this.mintInner(coinId, amount));
|
|
15692
15945
|
}
|
|
15693
15946
|
mintNft(request) {
|
|
15694
|
-
return this.track(this.
|
|
15947
|
+
return this.track(this.ownedOp((mintId) => runNftMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
15695
15948
|
}
|
|
15949
|
+
mintCustom = (request) => this.track(this.ownedOp((mintId) => runCustomMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
15950
|
+
burn = (request) => this.track(this.ownedOp((burnId) => runBurnUnderJournal(this.mintDeps(), { burnId, request })));
|
|
15951
|
+
pendingBurns = () => this.track(pendingBurns(this.mintDeps()));
|
|
15952
|
+
acknowledgeBurn = (burnId) => this.track(acknowledgeBurn(this.mintDeps(), burnId));
|
|
15696
15953
|
// §4 retry verb: coalesces onto the running pass — NEVER re-issue send() (#631/#676).
|
|
15697
15954
|
async resumeNow() {
|
|
15698
15955
|
if (!this.started) return;
|
|
@@ -15716,12 +15973,15 @@ var PaymentsFacade = class {
|
|
|
15716
15973
|
nowMs() {
|
|
15717
15974
|
return (this.deps.now ?? Date.now)();
|
|
15718
15975
|
}
|
|
15719
|
-
/** One snapshot serves the coin and the
|
|
15976
|
+
/** One snapshot serves the coin, NFT and custom mints and the burn, live and replayed. */
|
|
15720
15977
|
mintDeps() {
|
|
15721
15978
|
return {
|
|
15722
15979
|
engine: this.engine(),
|
|
15723
15980
|
mintJournal: this.machineStores.mintJournal,
|
|
15724
15981
|
nftMintJournal: this.machineStores.nftMintJournal,
|
|
15982
|
+
customMintJournal: this.machineStores.customMintJournal,
|
|
15983
|
+
burnJournal: this.machineStores.burnJournal,
|
|
15984
|
+
recordSent: (input) => this.historyStore.recordSent(input),
|
|
15725
15985
|
storagePort: this.deps.storagePort,
|
|
15726
15986
|
recordMint: (input) => this.historyStore.recordMint(input),
|
|
15727
15987
|
armHeartbeat: () => this.heartbeat.arm(),
|
|
@@ -15730,7 +15990,7 @@ var PaymentsFacade = class {
|
|
|
15730
15990
|
ownPubkeyBytes: this.ownPubkeyBytes,
|
|
15731
15991
|
nftTokenType: this.deps.nftTokenType,
|
|
15732
15992
|
isActiveOp: (mintId) => this.activeMoneyOps.has(mintId),
|
|
15733
|
-
tokenInServerInventory: (tokenId) => this.
|
|
15993
|
+
tokenInServerInventory: (tokenId) => tokenInServerInventory(this.deps.storagePort, tokenId),
|
|
15734
15994
|
emit: this.deps.emit,
|
|
15735
15995
|
now: () => this.nowMs()
|
|
15736
15996
|
};
|
|
@@ -16051,28 +16311,17 @@ var PaymentsFacade = class {
|
|
|
16051
16311
|
if (!/^(?:[0-9a-f]{2})+$/.test(coinId) || amount <= 0n) {
|
|
16052
16312
|
return { success: false, error: "coinId must be even-length lowercase hex and amount positive" };
|
|
16053
16313
|
}
|
|
16054
|
-
return this.
|
|
16314
|
+
return this.ownedOp((mintId) => runMintUnderJournal(this.mintDeps(), { mintId, coinId, amount }));
|
|
16055
16315
|
}
|
|
16056
|
-
/** A live
|
|
16057
|
-
async
|
|
16058
|
-
const
|
|
16059
|
-
this.activeMoneyOps.add(
|
|
16316
|
+
/** A live money op under a fresh id — its replay stays hands-off while this attempt runs (§7). */
|
|
16317
|
+
async ownedOp(run) {
|
|
16318
|
+
const id = this.newId();
|
|
16319
|
+
this.activeMoneyOps.add(id);
|
|
16060
16320
|
try {
|
|
16061
|
-
return await run(
|
|
16321
|
+
return await run(id);
|
|
16062
16322
|
} finally {
|
|
16063
|
-
this.activeMoneyOps.delete(
|
|
16064
|
-
}
|
|
16065
|
-
}
|
|
16066
|
-
async tokenInServerInventory(tokenId) {
|
|
16067
|
-
let page = await this.deps.storagePort.listInventory();
|
|
16068
|
-
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
16069
|
-
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) {
|
|
16070
|
-
return true;
|
|
16071
|
-
}
|
|
16072
|
-
if (!page.more) return false;
|
|
16073
|
-
page = await this.deps.storagePort.listInventory(page.cursor);
|
|
16323
|
+
this.activeMoneyOps.delete(id);
|
|
16074
16324
|
}
|
|
16075
|
-
return false;
|
|
16076
16325
|
}
|
|
16077
16326
|
// ── wiring internals ───────────────────────────────────────────────────────
|
|
16078
16327
|
engine() {
|
|
@@ -17464,6 +17713,8 @@ var Sphere = class _Sphere {
|
|
|
17464
17713
|
* the rebuild after an api-key change share one pool configuration.
|
|
17465
17714
|
*/
|
|
17466
17715
|
_verification;
|
|
17716
|
+
/** Token plugins (SphereInitOptions.plugins), applied to every engine like _verification. */
|
|
17717
|
+
_plugins;
|
|
17467
17718
|
/** This Sphere's OWN token registry. Disposed by destroy(); never the process global. */
|
|
17468
17719
|
_registry = null;
|
|
17469
17720
|
// Events
|
|
@@ -17577,6 +17828,7 @@ var Sphere = class _Sphere {
|
|
|
17577
17828
|
communications: options.communications,
|
|
17578
17829
|
password: options.password,
|
|
17579
17830
|
verification: options.verification,
|
|
17831
|
+
plugins: options.plugins,
|
|
17580
17832
|
discoverAddresses: options.discoverAddresses,
|
|
17581
17833
|
onProgress: options.onProgress
|
|
17582
17834
|
});
|
|
@@ -17614,6 +17866,7 @@ var Sphere = class _Sphere {
|
|
|
17614
17866
|
communications: options.communications,
|
|
17615
17867
|
password: options.password,
|
|
17616
17868
|
verification: options.verification,
|
|
17869
|
+
plugins: options.plugins,
|
|
17617
17870
|
discoverAddresses: options.discoverAddresses,
|
|
17618
17871
|
onProgress: options.onProgress
|
|
17619
17872
|
});
|
|
@@ -17792,6 +18045,7 @@ var Sphere = class _Sphere {
|
|
|
17792
18045
|
options.communications
|
|
17793
18046
|
);
|
|
17794
18047
|
sphere._verification = options.verification;
|
|
18048
|
+
sphere._plugins = options.plugins;
|
|
17795
18049
|
sphere._paymentsV2Composition = composition;
|
|
17796
18050
|
sphere._password = options.password ?? null;
|
|
17797
18051
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -17856,6 +18110,7 @@ var Sphere = class _Sphere {
|
|
|
17856
18110
|
options.communications
|
|
17857
18111
|
);
|
|
17858
18112
|
sphere._verification = options.verification;
|
|
18113
|
+
sphere._plugins = options.plugins;
|
|
17859
18114
|
sphere._paymentsV2Composition = composition;
|
|
17860
18115
|
sphere._password = options.password ?? null;
|
|
17861
18116
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -17942,6 +18197,7 @@ var Sphere = class _Sphere {
|
|
|
17942
18197
|
options.communications
|
|
17943
18198
|
);
|
|
17944
18199
|
sphere._verification = options.verification;
|
|
18200
|
+
sphere._plugins = options.plugins;
|
|
17945
18201
|
sphere._paymentsV2Composition = composition;
|
|
17946
18202
|
sphere._password = options.password ?? null;
|
|
17947
18203
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -20252,7 +20508,8 @@ var Sphere = class _Sphere {
|
|
|
20252
20508
|
apiKey: oracle.getApiKey?.(),
|
|
20253
20509
|
privateKey: hexToBytes4(privateKey),
|
|
20254
20510
|
trustBaseJson,
|
|
20255
|
-
...this._verification ? { verification: this._verification } : {}
|
|
20511
|
+
...this._verification ? { verification: this._verification } : {},
|
|
20512
|
+
...this._plugins ? { plugins: this._plugins } : {}
|
|
20256
20513
|
});
|
|
20257
20514
|
} catch (err) {
|
|
20258
20515
|
logger.warn(
|