@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/core/index.js
CHANGED
|
@@ -10287,6 +10287,19 @@ async function readTokenNft(token) {
|
|
|
10287
10287
|
}
|
|
10288
10288
|
}
|
|
10289
10289
|
|
|
10290
|
+
// token-engine/plugin-ops.ts
|
|
10291
|
+
function contextWithMintVerifiers(base, verifiers) {
|
|
10292
|
+
const registry = new MintJustificationVerifierService();
|
|
10293
|
+
for (const verifier of verifiers) registry.register(verifier);
|
|
10294
|
+
return new VerificationContext(
|
|
10295
|
+
base.trustBase,
|
|
10296
|
+
base.predicateVerifier,
|
|
10297
|
+
base.unicityCertificateVerifier,
|
|
10298
|
+
registry,
|
|
10299
|
+
new TokenIssuanceVerifierService(false)
|
|
10300
|
+
);
|
|
10301
|
+
}
|
|
10302
|
+
|
|
10290
10303
|
// token-engine/split-checkpoint.ts
|
|
10291
10304
|
var CHECKPOINT_VERSION = 2;
|
|
10292
10305
|
var CHECKPOINT_SDK_VERSION = "@unicitylabs/state-transition-sdk@3.0.1";
|
|
@@ -10433,6 +10446,10 @@ var SphereTokenEngine = class {
|
|
|
10433
10446
|
const data = token.sdkToken.genesis.data;
|
|
10434
10447
|
return data ? new Uint8Array(data) : null;
|
|
10435
10448
|
}
|
|
10449
|
+
readTokenJustification(token) {
|
|
10450
|
+
const justification = token.sdkToken.genesis.justification;
|
|
10451
|
+
return justification ? new Uint8Array(justification) : null;
|
|
10452
|
+
}
|
|
10436
10453
|
readNft(token) {
|
|
10437
10454
|
return readTokenNft(token);
|
|
10438
10455
|
}
|
|
@@ -10464,7 +10481,7 @@ var SphereTokenEngine = class {
|
|
|
10464
10481
|
const recipient = SignaturePredicate.create(params.recipientPubkey);
|
|
10465
10482
|
const tokenType = params.tokenType ? new TokenType(params.tokenType) : TokenType.generate();
|
|
10466
10483
|
const salt = params.salt ? TokenSalt.fromBytes(params.salt) : TokenSalt.generate();
|
|
10467
|
-
const mintTx = await MintTransaction.create(this.deps.networkId, recipient, { data: params.data, tokenType, salt });
|
|
10484
|
+
const mintTx = await MintTransaction.create(this.deps.networkId, recipient, { data: params.data, tokenType, salt, justification: params.justification ?? null });
|
|
10468
10485
|
const certificationData = await CertificationData.fromMintTransaction(mintTx);
|
|
10469
10486
|
const proof = await this.submitAndAwaitProof(
|
|
10470
10487
|
certificationData,
|
|
@@ -10474,34 +10491,30 @@ var SphereTokenEngine = class {
|
|
|
10474
10491
|
options
|
|
10475
10492
|
);
|
|
10476
10493
|
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
|
|
10477
|
-
const
|
|
10478
|
-
return wrapToken(
|
|
10494
|
+
const verifiers = params.mintJustificationVerifiers;
|
|
10495
|
+
return wrapToken(await Token.mint(certified, verifiers ? contextWithMintVerifiers(this.deps, verifiers) : this.deps.verificationContext));
|
|
10479
10496
|
}
|
|
10480
10497
|
buildNftMint(params) {
|
|
10481
10498
|
return planNftMint(this.deps, params);
|
|
10482
10499
|
}
|
|
10483
10500
|
async transfer(params, options) {
|
|
10484
10501
|
this.assertOwned(params.token);
|
|
10485
|
-
|
|
10486
|
-
|
|
10502
|
+
return this.spend(params.token, SignaturePredicate.create(params.recipientPubkey), params.data ?? null, options, "Transfer certification failed");
|
|
10503
|
+
}
|
|
10504
|
+
async burn(params, options) {
|
|
10505
|
+
this.assertOwned(params.token);
|
|
10506
|
+
return this.spend(params.token, BurnPredicate.create(sha256(params.reasonBytes)), params.reasonBytes, options, "Burn certification failed");
|
|
10507
|
+
}
|
|
10508
|
+
async spend(token, recipient, data, options, failLabel) {
|
|
10487
10509
|
const stateMask = StateMask.fromBytes(
|
|
10488
|
-
deriveRealization(this.privateKeyHex,
|
|
10510
|
+
deriveRealization(this.privateKeyHex, this.resolveTransferId(options), this.resolveOpIndex(options), "stateMask")
|
|
10489
10511
|
);
|
|
10490
|
-
const transferTx = await TransferTransaction.create(
|
|
10491
|
-
data: params.data ?? null
|
|
10492
|
-
});
|
|
10512
|
+
const transferTx = await TransferTransaction.create(token.sdkToken, recipient, stateMask, { data });
|
|
10493
10513
|
const unlockScript = await SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);
|
|
10494
10514
|
const certificationData = await CertificationData.fromTransaction(transferTx, unlockScript);
|
|
10495
|
-
const proof = await this.submitAndAwaitProof(
|
|
10496
|
-
certificationData,
|
|
10497
|
-
transferTx,
|
|
10498
|
-
"Transfer certification failed",
|
|
10499
|
-
"TRANSFER_FAILED",
|
|
10500
|
-
options
|
|
10501
|
-
);
|
|
10515
|
+
const proof = await this.submitAndAwaitProof(certificationData, transferTx, failLabel, "TRANSFER_FAILED", options);
|
|
10502
10516
|
const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, this.deps.unicityCertificateVerifier, proof);
|
|
10503
|
-
|
|
10504
|
-
return wrapToken(transferred);
|
|
10517
|
+
return wrapToken(await token.sdkToken.transfer(certified, this.deps.verificationContext));
|
|
10505
10518
|
}
|
|
10506
10519
|
async split(params, options) {
|
|
10507
10520
|
this.assertOwned(params.token);
|
|
@@ -10816,6 +10829,20 @@ var SphereTokenEngine = class {
|
|
|
10816
10829
|
|
|
10817
10830
|
// token-engine/factory.ts
|
|
10818
10831
|
var DEFAULT_VERIFICATION_POOL_SIZE = 4;
|
|
10832
|
+
function registerPluginVerifiers(registry, plugins) {
|
|
10833
|
+
for (const plugin of plugins ?? []) {
|
|
10834
|
+
for (const verifier of plugin.mintJustificationVerifiers ?? []) {
|
|
10835
|
+
try {
|
|
10836
|
+
registry.register(verifier);
|
|
10837
|
+
} catch (err) {
|
|
10838
|
+
throw new SphereError(
|
|
10839
|
+
`Token plugin '${plugin.id}' registers mint-reason tag ${verifier.tag} twice or over another plugin's: ` + (err instanceof Error ? err.message : String(err)),
|
|
10840
|
+
"INVALID_CONFIG"
|
|
10841
|
+
);
|
|
10842
|
+
}
|
|
10843
|
+
}
|
|
10844
|
+
}
|
|
10845
|
+
}
|
|
10819
10846
|
function disposedError() {
|
|
10820
10847
|
return new SphereError(
|
|
10821
10848
|
"Verification worker pool disposed \u2014 the in-flight verification was cancelled",
|
|
@@ -10897,6 +10924,7 @@ async function createSphereTokenEngine(config) {
|
|
|
10897
10924
|
mintJustificationVerifier.register(
|
|
10898
10925
|
new SplitMintJustificationVerifier(decodeSpherePaymentData)
|
|
10899
10926
|
);
|
|
10927
|
+
registerPluginVerifiers(mintJustificationVerifier, config.plugins);
|
|
10900
10928
|
const deps = {
|
|
10901
10929
|
client: new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
|
|
10902
10930
|
trustBase,
|
|
@@ -12014,6 +12042,8 @@ var STORE_KEYS = {
|
|
|
12014
12042
|
deliveryJournal: "delivery-journal",
|
|
12015
12043
|
mintJournal: "mint-journal",
|
|
12016
12044
|
nftMintJournal: "nft-mint-journal",
|
|
12045
|
+
customMintJournal: "custom-mint-journal",
|
|
12046
|
+
burnJournal: "burn-journal",
|
|
12017
12047
|
shortfalls: "shortfalls",
|
|
12018
12048
|
settlingLinks: "settling",
|
|
12019
12049
|
streamCursor: (s) => `cursor:${s}`,
|
|
@@ -12198,6 +12228,8 @@ function createMachineStores(kv) {
|
|
|
12198
12228
|
deliveryJournal: new DeliveryJournal(kv),
|
|
12199
12229
|
mintJournal: new ListStore(kv, STORE_KEYS.mintJournal, (e) => e.mintId),
|
|
12200
12230
|
nftMintJournal: new ListStore(kv, STORE_KEYS.nftMintJournal, (e) => e.mintId),
|
|
12231
|
+
customMintJournal: new ListStore(kv, STORE_KEYS.customMintJournal, (e) => e.mintId),
|
|
12232
|
+
burnJournal: new ListStore(kv, STORE_KEYS.burnJournal, (e) => e.burnId),
|
|
12201
12233
|
shortfalls: new ListStore(kv, STORE_KEYS.shortfalls, (e) => e.transferId)
|
|
12202
12234
|
};
|
|
12203
12235
|
}
|
|
@@ -12905,7 +12937,12 @@ var Converger = class {
|
|
|
12905
12937
|
} catch (err) {
|
|
12906
12938
|
retryAfterMs ??= retryAfterMsOf(err);
|
|
12907
12939
|
}
|
|
12908
|
-
for (const replay of [
|
|
12940
|
+
for (const replay of [
|
|
12941
|
+
() => this.deps.replayMints(),
|
|
12942
|
+
() => this.deps.replayNftMints(),
|
|
12943
|
+
() => this.deps.replayCustomMints(),
|
|
12944
|
+
() => this.deps.replayBurns()
|
|
12945
|
+
]) {
|
|
12909
12946
|
try {
|
|
12910
12947
|
progress = await replay() > 0 || progress;
|
|
12911
12948
|
} catch (err) {
|
|
@@ -12916,14 +12953,17 @@ var Converger = class {
|
|
|
12916
12953
|
}
|
|
12917
12954
|
/** §4 heartbeat predicate — read fresh from the §6 stores, never cached. */
|
|
12918
12955
|
async pendingWork() {
|
|
12919
|
-
const [backstop, journal, mints, nftMints] = await Promise.all([
|
|
12956
|
+
const [backstop, journal, mints, nftMints, customMints, burns] = await Promise.all([
|
|
12920
12957
|
this.deps.stores.backstop.list(),
|
|
12921
12958
|
this.deps.stores.deliveryJournal.list(),
|
|
12922
12959
|
this.deps.stores.mintJournal.list(),
|
|
12923
|
-
this.deps.stores.nftMintJournal.list()
|
|
12960
|
+
this.deps.stores.nftMintJournal.list(),
|
|
12961
|
+
this.deps.stores.customMintJournal.list(),
|
|
12962
|
+
this.deps.stores.burnJournal.list()
|
|
12924
12963
|
]);
|
|
12925
|
-
const mintsOpen = mints.length > 0 || nftMints.length > 0;
|
|
12926
|
-
|
|
12964
|
+
const mintsOpen = mints.length > 0 || nftMints.length > 0 || customMints.length > 0;
|
|
12965
|
+
const burnsOpen = burns.some((e) => !e.settled);
|
|
12966
|
+
return backstop.length > 0 || journal.some((e) => e.undeliverable !== true) || mintsOpen || burnsOpen;
|
|
12927
12967
|
}
|
|
12928
12968
|
async resumePass() {
|
|
12929
12969
|
const report = await resumeAll(this.filteredMachineDeps(), {
|
|
@@ -13155,6 +13195,12 @@ async function decodeView(deps, tokenId, bytes) {
|
|
|
13155
13195
|
|
|
13156
13196
|
// modules/payments-v2/inventory/token-data.ts
|
|
13157
13197
|
async function readTokenData(deps, tokenId) {
|
|
13198
|
+
return deps.engine.readTokenData(await heldToken(deps, tokenId));
|
|
13199
|
+
}
|
|
13200
|
+
async function readTokenJustification(deps, tokenId) {
|
|
13201
|
+
return deps.engine.readTokenJustification(await heldToken(deps, tokenId));
|
|
13202
|
+
}
|
|
13203
|
+
async function heldToken(deps, tokenId) {
|
|
13158
13204
|
if (deps.view.stateHashOf(tokenId) === void 0) {
|
|
13159
13205
|
throw new SphereError(`Token ${tokenId} is not in inventory`, "VALIDATION_ERROR");
|
|
13160
13206
|
}
|
|
@@ -13162,7 +13208,7 @@ async function readTokenData(deps, tokenId) {
|
|
|
13162
13208
|
if (bytes === void 0) {
|
|
13163
13209
|
throw new SphereError(`Token ${tokenId} has no blob in storage`, "STORAGE_ERROR");
|
|
13164
13210
|
}
|
|
13165
|
-
return deps.engine.
|
|
13211
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
13166
13212
|
}
|
|
13167
13213
|
|
|
13168
13214
|
// modules/payments-v2/history/History.ts
|
|
@@ -14998,6 +15044,7 @@ function mintParams(recipientPubkey, coinId, amount) {
|
|
|
14998
15044
|
|
|
14999
15045
|
// modules/payments-v2/mint.ts
|
|
15000
15046
|
var ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
15047
|
+
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
15001
15048
|
function runMintUnderJournal(deps, input) {
|
|
15002
15049
|
return mintUnderJournal(deps, input);
|
|
15003
15050
|
}
|
|
@@ -15050,6 +15097,15 @@ async function finalizeMint(deps, input) {
|
|
|
15050
15097
|
deps.noteHeldState(token.blob.tokenId, (await deps.engine.deliveryKeys(bytes)).stateHash);
|
|
15051
15098
|
deps.refreshView();
|
|
15052
15099
|
}
|
|
15100
|
+
async function tokenInServerInventory(storagePort, tokenId) {
|
|
15101
|
+
let page = await storagePort.listInventory();
|
|
15102
|
+
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
15103
|
+
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) return true;
|
|
15104
|
+
if (!page.more) return false;
|
|
15105
|
+
page = await storagePort.listInventory(page.cursor);
|
|
15106
|
+
}
|
|
15107
|
+
return false;
|
|
15108
|
+
}
|
|
15053
15109
|
async function replayCoinMints(deps) {
|
|
15054
15110
|
let resolved = 0;
|
|
15055
15111
|
for (const entry of await deps.mintJournal.list()) {
|
|
@@ -15153,6 +15209,199 @@ async function mintJournaled(deps, entry) {
|
|
|
15153
15209
|
await finalizeMint(deps, { mintId: entry.mintId, token, assets: [], journal: deps.nftMintJournal });
|
|
15154
15210
|
}
|
|
15155
15211
|
|
|
15212
|
+
// modules/payments-v2/mint-custom.ts
|
|
15213
|
+
var HEX32 = /^[0-9a-f]{64}$/;
|
|
15214
|
+
function assertRequest(request) {
|
|
15215
|
+
if (request.tokenType.length !== 32) {
|
|
15216
|
+
throw new SphereError("mintCustom: tokenType must be 32 bytes", "VALIDATION_ERROR");
|
|
15217
|
+
}
|
|
15218
|
+
if (request.salt.length < 16 || request.salt.length > 64) {
|
|
15219
|
+
throw new SphereError("mintCustom: salt must be 16 to 64 bytes", "VALIDATION_ERROR");
|
|
15220
|
+
}
|
|
15221
|
+
if (request.data.length === 0) {
|
|
15222
|
+
throw new SphereError("mintCustom: data must not be empty", "VALIDATION_ERROR");
|
|
15223
|
+
}
|
|
15224
|
+
for (const asset of request.assets) {
|
|
15225
|
+
if (!HEX32.test(asset.coinId) || asset.amount <= 0n) {
|
|
15226
|
+
throw new SphereError("mintCustom: assets need a 64-hex coinId and a positive amount", "VALIDATION_ERROR");
|
|
15227
|
+
}
|
|
15228
|
+
}
|
|
15229
|
+
}
|
|
15230
|
+
async function runCustomMintUnderJournal(deps, input) {
|
|
15231
|
+
try {
|
|
15232
|
+
assertRequest(input.request);
|
|
15233
|
+
} catch (err) {
|
|
15234
|
+
return { success: false, error: messageOf(err) };
|
|
15235
|
+
}
|
|
15236
|
+
const entry = journalEntryOf2(input.mintId, input.request, deps.now());
|
|
15237
|
+
await deps.customMintJournal.upsert(entry);
|
|
15238
|
+
let tokenId;
|
|
15239
|
+
try {
|
|
15240
|
+
tokenId = await mintJournaled2(deps, entry, input.request.mintJustificationVerifiers);
|
|
15241
|
+
} catch (err) {
|
|
15242
|
+
deps.armHeartbeat();
|
|
15243
|
+
return { success: false, error: messageOf(err) };
|
|
15244
|
+
}
|
|
15245
|
+
return { success: true, tokenId };
|
|
15246
|
+
}
|
|
15247
|
+
async function replayCustomMints(deps) {
|
|
15248
|
+
let resolved = 0;
|
|
15249
|
+
for (const entry of await deps.customMintJournal.list()) {
|
|
15250
|
+
if (deps.isActiveOp(entry.mintId)) continue;
|
|
15251
|
+
try {
|
|
15252
|
+
if (entry.tokenId !== "" && await deps.tokenInServerInventory(entry.tokenId)) {
|
|
15253
|
+
await deps.customMintJournal.removeByKey(entry.mintId);
|
|
15254
|
+
} else {
|
|
15255
|
+
await mintJournaled2(deps, entry, void 0);
|
|
15256
|
+
}
|
|
15257
|
+
resolved += 1;
|
|
15258
|
+
} catch {
|
|
15259
|
+
continue;
|
|
15260
|
+
}
|
|
15261
|
+
}
|
|
15262
|
+
return resolved;
|
|
15263
|
+
}
|
|
15264
|
+
function journalEntryOf2(mintId, request, createdAt) {
|
|
15265
|
+
return {
|
|
15266
|
+
mintId,
|
|
15267
|
+
tokenId: "",
|
|
15268
|
+
dataHex: bytesToHex3(request.data),
|
|
15269
|
+
saltHex: bytesToHex3(request.salt),
|
|
15270
|
+
tokenTypeHex: bytesToHex3(request.tokenType),
|
|
15271
|
+
justificationHex: request.justification ? bytesToHex3(request.justification) : null,
|
|
15272
|
+
assets: request.assets.map((a) => ({ coinId: a.coinId, amount: a.amount.toString() })),
|
|
15273
|
+
createdAt
|
|
15274
|
+
};
|
|
15275
|
+
}
|
|
15276
|
+
async function mintJournaled2(deps, entry, mintJustificationVerifiers) {
|
|
15277
|
+
const token = await deps.engine.mintDataToken(
|
|
15278
|
+
{
|
|
15279
|
+
recipientPubkey: deps.ownPubkeyBytes,
|
|
15280
|
+
data: hexToBytes4(entry.dataHex),
|
|
15281
|
+
tokenType: hexToBytes4(entry.tokenTypeHex),
|
|
15282
|
+
salt: hexToBytes4(entry.saltHex),
|
|
15283
|
+
...entry.justificationHex !== null ? { justification: hexToBytes4(entry.justificationHex) } : {},
|
|
15284
|
+
...mintJustificationVerifiers ? { mintJustificationVerifiers } : {}
|
|
15285
|
+
},
|
|
15286
|
+
{ transferId: entry.mintId }
|
|
15287
|
+
);
|
|
15288
|
+
if (entry.tokenId !== "" && token.blob.tokenId !== entry.tokenId) {
|
|
15289
|
+
throw new SphereError(`minted token ${token.blob.tokenId}, but the journal names ${entry.tokenId}`, "VALIDATION_ERROR");
|
|
15290
|
+
}
|
|
15291
|
+
if (entry.tokenId === "") {
|
|
15292
|
+
await deps.customMintJournal.upsert({ ...entry, tokenId: token.blob.tokenId });
|
|
15293
|
+
}
|
|
15294
|
+
await finalizeMint(deps, {
|
|
15295
|
+
mintId: entry.mintId,
|
|
15296
|
+
token,
|
|
15297
|
+
assets: entry.assets,
|
|
15298
|
+
journal: deps.customMintJournal
|
|
15299
|
+
});
|
|
15300
|
+
return token.blob.tokenId;
|
|
15301
|
+
}
|
|
15302
|
+
|
|
15303
|
+
// modules/payments-v2/burn.ts
|
|
15304
|
+
var HEX322 = /^[0-9a-f]{64}$/;
|
|
15305
|
+
async function heldToken2(deps, tokenId) {
|
|
15306
|
+
const bytes = (await deps.storagePort.getBlobs([tokenId])).get(tokenId);
|
|
15307
|
+
if (bytes === void 0) {
|
|
15308
|
+
throw new SphereError(`Token ${tokenId} has no blob in storage; it is not held here`, "STORAGE_ERROR");
|
|
15309
|
+
}
|
|
15310
|
+
return deps.engine.decodeToken({ tokenId, token: bytes });
|
|
15311
|
+
}
|
|
15312
|
+
function assetsOf(token) {
|
|
15313
|
+
return (token.value?.assets ?? []).map((a) => ({ coinId: a.coinId, amount: a.amount.toString() }));
|
|
15314
|
+
}
|
|
15315
|
+
async function runBurnUnderJournal(deps, input) {
|
|
15316
|
+
const { burnId } = input;
|
|
15317
|
+
const { tokenId, reasonBytes } = input.request;
|
|
15318
|
+
if (!HEX322.test(tokenId) || reasonBytes.length === 0) {
|
|
15319
|
+
return { success: false, burnId, tokenId, error: "burn: tokenId must be 64 hex and reasonBytes non-empty" };
|
|
15320
|
+
}
|
|
15321
|
+
let token;
|
|
15322
|
+
try {
|
|
15323
|
+
token = await heldToken2(deps, tokenId);
|
|
15324
|
+
if (!deps.engine.isOwnedBy(token, deps.engine.getIdentity().chainPubkey)) {
|
|
15325
|
+
throw new SphereError(`Token ${tokenId} is not owned by this wallet`, "VALIDATION_ERROR");
|
|
15326
|
+
}
|
|
15327
|
+
} catch (err) {
|
|
15328
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
15329
|
+
}
|
|
15330
|
+
const entry = {
|
|
15331
|
+
burnId,
|
|
15332
|
+
tokenId,
|
|
15333
|
+
reasonHex: bytesToHex3(reasonBytes),
|
|
15334
|
+
burnedTokenHex: null,
|
|
15335
|
+
settled: false,
|
|
15336
|
+
assets: assetsOf(token),
|
|
15337
|
+
createdAt: deps.now()
|
|
15338
|
+
};
|
|
15339
|
+
await deps.burnJournal.upsert(entry);
|
|
15340
|
+
try {
|
|
15341
|
+
const settled = await burnJournaled(deps, entry, token, { applied: false });
|
|
15342
|
+
return { success: true, burnId, tokenId, burnedToken: hexToBytes4(settled.burnedTokenHex ?? "") };
|
|
15343
|
+
} catch (err) {
|
|
15344
|
+
deps.armHeartbeat();
|
|
15345
|
+
return { success: false, burnId, tokenId, error: messageOf(err) };
|
|
15346
|
+
}
|
|
15347
|
+
}
|
|
15348
|
+
async function replayBurns(deps) {
|
|
15349
|
+
let progressed = 0;
|
|
15350
|
+
for (const entry of await deps.burnJournal.list()) {
|
|
15351
|
+
if (entry.settled || deps.isActiveOp(entry.burnId)) continue;
|
|
15352
|
+
try {
|
|
15353
|
+
const token = entry.burnedTokenHex === null ? await heldToken2(deps, entry.tokenId) : null;
|
|
15354
|
+
const applied = entry.burnedTokenHex !== null && !await deps.tokenInServerInventory(entry.tokenId);
|
|
15355
|
+
await burnJournaled(deps, entry, token, { applied });
|
|
15356
|
+
progressed += 1;
|
|
15357
|
+
} catch {
|
|
15358
|
+
continue;
|
|
15359
|
+
}
|
|
15360
|
+
}
|
|
15361
|
+
return progressed;
|
|
15362
|
+
}
|
|
15363
|
+
async function pendingBurns(deps) {
|
|
15364
|
+
return (await deps.burnJournal.list()).map((entry) => ({
|
|
15365
|
+
burnId: entry.burnId,
|
|
15366
|
+
tokenId: entry.tokenId,
|
|
15367
|
+
reasonBytes: hexToBytes4(entry.reasonHex),
|
|
15368
|
+
burnedToken: entry.burnedTokenHex === null ? null : hexToBytes4(entry.burnedTokenHex),
|
|
15369
|
+
settled: entry.settled,
|
|
15370
|
+
createdAt: entry.createdAt
|
|
15371
|
+
}));
|
|
15372
|
+
}
|
|
15373
|
+
async function acknowledgeBurn(deps, burnId) {
|
|
15374
|
+
const entry = await deps.burnJournal.getByKey(burnId);
|
|
15375
|
+
if (entry === void 0) return;
|
|
15376
|
+
if (!entry.settled) {
|
|
15377
|
+
throw new SphereError(`Burn ${burnId} is not settled yet; it cannot be acknowledged`, "VALIDATION_ERROR");
|
|
15378
|
+
}
|
|
15379
|
+
await deps.burnJournal.removeByKey(burnId);
|
|
15380
|
+
}
|
|
15381
|
+
async function burnJournaled(deps, entry, token, state) {
|
|
15382
|
+
let current = entry;
|
|
15383
|
+
if (current.burnedTokenHex === null) {
|
|
15384
|
+
if (token === null) throw new SphereError("burn replay needs the held token to certify", "VALIDATION_ERROR");
|
|
15385
|
+
const burned = await deps.engine.burn(
|
|
15386
|
+
{ token, reasonBytes: hexToBytes4(current.reasonHex) },
|
|
15387
|
+
{ transferId: current.burnId }
|
|
15388
|
+
);
|
|
15389
|
+
if (burned.blob.tokenId !== current.tokenId) {
|
|
15390
|
+
throw new SphereError(`burned token ${burned.blob.tokenId}, but the journal names ${current.tokenId}`, "VALIDATION_ERROR");
|
|
15391
|
+
}
|
|
15392
|
+
current = { ...current, burnedTokenHex: bytesToHex3(burned.blob.token) };
|
|
15393
|
+
await deps.burnJournal.upsert(current);
|
|
15394
|
+
}
|
|
15395
|
+
if (!state.applied) {
|
|
15396
|
+
await deps.storagePort.applyDelta({ transferId: current.burnId, spent: [current.tokenId], added: [] });
|
|
15397
|
+
}
|
|
15398
|
+
await deps.recordSent({ transferId: current.burnId, assets: current.assets, tokenId: current.tokenId });
|
|
15399
|
+
current = { ...current, settled: true };
|
|
15400
|
+
await deps.burnJournal.upsert(current);
|
|
15401
|
+
deps.refreshView();
|
|
15402
|
+
return current;
|
|
15403
|
+
}
|
|
15404
|
+
|
|
15156
15405
|
// modules/payments-v2/send-whole.ts
|
|
15157
15406
|
async function materializeWholeSpend(deps, input) {
|
|
15158
15407
|
const { engine } = deps;
|
|
@@ -15338,7 +15587,6 @@ async function takeSourceBlobs(deps, sourceIds) {
|
|
|
15338
15587
|
|
|
15339
15588
|
// modules/payments-v2/PaymentsFacade.ts
|
|
15340
15589
|
var MAX_RESELECT = 8;
|
|
15341
|
-
var INVENTORY_SCAN_PAGE_LIMIT = 50;
|
|
15342
15590
|
var PaymentsFacade = class {
|
|
15343
15591
|
constructor(deps) {
|
|
15344
15592
|
this.deps = deps;
|
|
@@ -15375,6 +15623,8 @@ var PaymentsFacade = class {
|
|
|
15375
15623
|
refreshView: () => this.view.delta(),
|
|
15376
15624
|
replayMints: () => replayCoinMints(this.mintDeps()),
|
|
15377
15625
|
replayNftMints: () => replayNftMints(this.mintDeps()),
|
|
15626
|
+
replayCustomMints: () => replayCustomMints(this.mintDeps()),
|
|
15627
|
+
replayBurns: () => replayBurns(this.mintDeps()),
|
|
15378
15628
|
isActiveOp: (id) => this.activeMoneyOps.has(id),
|
|
15379
15629
|
emit: deps.emit,
|
|
15380
15630
|
now: () => this.nowMs()
|
|
@@ -15470,6 +15720,9 @@ var PaymentsFacade = class {
|
|
|
15470
15720
|
tokenData(tokenId) {
|
|
15471
15721
|
return readTokenData(this.readDeps(), tokenId);
|
|
15472
15722
|
}
|
|
15723
|
+
tokenJustification(tokenId) {
|
|
15724
|
+
return readTokenJustification(this.readDeps(), tokenId);
|
|
15725
|
+
}
|
|
15473
15726
|
nft(tokenId) {
|
|
15474
15727
|
return readNft(this.readDeps(), tokenId);
|
|
15475
15728
|
}
|
|
@@ -15518,8 +15771,12 @@ var PaymentsFacade = class {
|
|
|
15518
15771
|
return this.track(this.mintInner(coinId, amount));
|
|
15519
15772
|
}
|
|
15520
15773
|
mintNft(request) {
|
|
15521
|
-
return this.track(this.
|
|
15774
|
+
return this.track(this.ownedOp((mintId) => runNftMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
15522
15775
|
}
|
|
15776
|
+
mintCustom = (request) => this.track(this.ownedOp((mintId) => runCustomMintUnderJournal(this.mintDeps(), { mintId, request })));
|
|
15777
|
+
burn = (request) => this.track(this.ownedOp((burnId) => runBurnUnderJournal(this.mintDeps(), { burnId, request })));
|
|
15778
|
+
pendingBurns = () => this.track(pendingBurns(this.mintDeps()));
|
|
15779
|
+
acknowledgeBurn = (burnId) => this.track(acknowledgeBurn(this.mintDeps(), burnId));
|
|
15523
15780
|
// §4 retry verb: coalesces onto the running pass — NEVER re-issue send() (#631/#676).
|
|
15524
15781
|
async resumeNow() {
|
|
15525
15782
|
if (!this.started) return;
|
|
@@ -15543,12 +15800,15 @@ var PaymentsFacade = class {
|
|
|
15543
15800
|
nowMs() {
|
|
15544
15801
|
return (this.deps.now ?? Date.now)();
|
|
15545
15802
|
}
|
|
15546
|
-
/** One snapshot serves the coin and the
|
|
15803
|
+
/** One snapshot serves the coin, NFT and custom mints and the burn, live and replayed. */
|
|
15547
15804
|
mintDeps() {
|
|
15548
15805
|
return {
|
|
15549
15806
|
engine: this.engine(),
|
|
15550
15807
|
mintJournal: this.machineStores.mintJournal,
|
|
15551
15808
|
nftMintJournal: this.machineStores.nftMintJournal,
|
|
15809
|
+
customMintJournal: this.machineStores.customMintJournal,
|
|
15810
|
+
burnJournal: this.machineStores.burnJournal,
|
|
15811
|
+
recordSent: (input) => this.historyStore.recordSent(input),
|
|
15552
15812
|
storagePort: this.deps.storagePort,
|
|
15553
15813
|
recordMint: (input) => this.historyStore.recordMint(input),
|
|
15554
15814
|
armHeartbeat: () => this.heartbeat.arm(),
|
|
@@ -15557,7 +15817,7 @@ var PaymentsFacade = class {
|
|
|
15557
15817
|
ownPubkeyBytes: this.ownPubkeyBytes,
|
|
15558
15818
|
nftTokenType: this.deps.nftTokenType,
|
|
15559
15819
|
isActiveOp: (mintId) => this.activeMoneyOps.has(mintId),
|
|
15560
|
-
tokenInServerInventory: (tokenId) => this.
|
|
15820
|
+
tokenInServerInventory: (tokenId) => tokenInServerInventory(this.deps.storagePort, tokenId),
|
|
15561
15821
|
emit: this.deps.emit,
|
|
15562
15822
|
now: () => this.nowMs()
|
|
15563
15823
|
};
|
|
@@ -15878,28 +16138,17 @@ var PaymentsFacade = class {
|
|
|
15878
16138
|
if (!/^(?:[0-9a-f]{2})+$/.test(coinId) || amount <= 0n) {
|
|
15879
16139
|
return { success: false, error: "coinId must be even-length lowercase hex and amount positive" };
|
|
15880
16140
|
}
|
|
15881
|
-
return this.
|
|
16141
|
+
return this.ownedOp((mintId) => runMintUnderJournal(this.mintDeps(), { mintId, coinId, amount }));
|
|
15882
16142
|
}
|
|
15883
|
-
/** A live
|
|
15884
|
-
async
|
|
15885
|
-
const
|
|
15886
|
-
this.activeMoneyOps.add(
|
|
16143
|
+
/** A live money op under a fresh id — its replay stays hands-off while this attempt runs (§7). */
|
|
16144
|
+
async ownedOp(run) {
|
|
16145
|
+
const id = this.newId();
|
|
16146
|
+
this.activeMoneyOps.add(id);
|
|
15887
16147
|
try {
|
|
15888
|
-
return await run(
|
|
16148
|
+
return await run(id);
|
|
15889
16149
|
} finally {
|
|
15890
|
-
this.activeMoneyOps.delete(
|
|
15891
|
-
}
|
|
15892
|
-
}
|
|
15893
|
-
async tokenInServerInventory(tokenId) {
|
|
15894
|
-
let page = await this.deps.storagePort.listInventory();
|
|
15895
|
-
for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
|
|
15896
|
-
if (page.items.some((item) => item.tokenId === tokenId && item.status === "active")) {
|
|
15897
|
-
return true;
|
|
15898
|
-
}
|
|
15899
|
-
if (!page.more) return false;
|
|
15900
|
-
page = await this.deps.storagePort.listInventory(page.cursor);
|
|
16150
|
+
this.activeMoneyOps.delete(id);
|
|
15901
16151
|
}
|
|
15902
|
-
return false;
|
|
15903
16152
|
}
|
|
15904
16153
|
// ── wiring internals ───────────────────────────────────────────────────────
|
|
15905
16154
|
engine() {
|
|
@@ -17291,6 +17540,8 @@ var Sphere = class _Sphere {
|
|
|
17291
17540
|
* the rebuild after an api-key change share one pool configuration.
|
|
17292
17541
|
*/
|
|
17293
17542
|
_verification;
|
|
17543
|
+
/** Token plugins (SphereInitOptions.plugins), applied to every engine like _verification. */
|
|
17544
|
+
_plugins;
|
|
17294
17545
|
/** This Sphere's OWN token registry. Disposed by destroy(); never the process global. */
|
|
17295
17546
|
_registry = null;
|
|
17296
17547
|
// Events
|
|
@@ -17404,6 +17655,7 @@ var Sphere = class _Sphere {
|
|
|
17404
17655
|
communications: options.communications,
|
|
17405
17656
|
password: options.password,
|
|
17406
17657
|
verification: options.verification,
|
|
17658
|
+
plugins: options.plugins,
|
|
17407
17659
|
discoverAddresses: options.discoverAddresses,
|
|
17408
17660
|
onProgress: options.onProgress
|
|
17409
17661
|
});
|
|
@@ -17441,6 +17693,7 @@ var Sphere = class _Sphere {
|
|
|
17441
17693
|
communications: options.communications,
|
|
17442
17694
|
password: options.password,
|
|
17443
17695
|
verification: options.verification,
|
|
17696
|
+
plugins: options.plugins,
|
|
17444
17697
|
discoverAddresses: options.discoverAddresses,
|
|
17445
17698
|
onProgress: options.onProgress
|
|
17446
17699
|
});
|
|
@@ -17619,6 +17872,7 @@ var Sphere = class _Sphere {
|
|
|
17619
17872
|
options.communications
|
|
17620
17873
|
);
|
|
17621
17874
|
sphere._verification = options.verification;
|
|
17875
|
+
sphere._plugins = options.plugins;
|
|
17622
17876
|
sphere._paymentsV2Composition = composition;
|
|
17623
17877
|
sphere._password = options.password ?? null;
|
|
17624
17878
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -17683,6 +17937,7 @@ var Sphere = class _Sphere {
|
|
|
17683
17937
|
options.communications
|
|
17684
17938
|
);
|
|
17685
17939
|
sphere._verification = options.verification;
|
|
17940
|
+
sphere._plugins = options.plugins;
|
|
17686
17941
|
sphere._paymentsV2Composition = composition;
|
|
17687
17942
|
sphere._password = options.password ?? null;
|
|
17688
17943
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -17769,6 +18024,7 @@ var Sphere = class _Sphere {
|
|
|
17769
18024
|
options.communications
|
|
17770
18025
|
);
|
|
17771
18026
|
sphere._verification = options.verification;
|
|
18027
|
+
sphere._plugins = options.plugins;
|
|
17772
18028
|
sphere._paymentsV2Composition = composition;
|
|
17773
18029
|
sphere._password = options.password ?? null;
|
|
17774
18030
|
sphere._passwordProtected = sphere._password !== null;
|
|
@@ -20079,7 +20335,8 @@ var Sphere = class _Sphere {
|
|
|
20079
20335
|
apiKey: oracle.getApiKey?.(),
|
|
20080
20336
|
privateKey: hexToBytes4(privateKey),
|
|
20081
20337
|
trustBaseJson,
|
|
20082
|
-
...this._verification ? { verification: this._verification } : {}
|
|
20338
|
+
...this._verification ? { verification: this._verification } : {},
|
|
20339
|
+
...this._plugins ? { plugins: this._plugins } : {}
|
|
20083
20340
|
});
|
|
20084
20341
|
} catch (err) {
|
|
20085
20342
|
logger.warn(
|