@unicitylabs/sphere-sdk 0.7.2 → 0.8.0-dev.0

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.
@@ -1971,26 +1971,14 @@ var NostrTransportProvider = class _NostrTransportProvider {
1971
1971
  }
1972
1972
  /**
1973
1973
  * Convert a BindingInfo (from nostr-js-sdk) to PeerInfo (sphere-sdk type).
1974
- * Computes PROXY address from nametag if available.
1975
1974
  */
1976
1975
  async bindingInfoToPeerInfo(binding, nametag) {
1977
- const nametagValue = nametag || binding.nametag;
1978
- let proxyAddress = binding.proxyAddress;
1979
- if (nametagValue && !proxyAddress) {
1980
- try {
1981
- const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
1982
- const proxyAddr = await ProxyAddress.fromNameTag(nametagValue);
1983
- proxyAddress = proxyAddr.toString();
1984
- } catch {
1985
- }
1986
- }
1987
1976
  return {
1988
- nametag: nametagValue,
1977
+ nametag: nametag || binding.nametag,
1989
1978
  transportPubkey: binding.transportPubkey,
1990
1979
  chainPubkey: binding.publicKey || "",
1991
1980
  l1Address: binding.l1Address || "",
1992
1981
  directAddress: binding.directAddress || "",
1993
- proxyAddress,
1994
1982
  timestamp: binding.timestamp
1995
1983
  };
1996
1984
  }
@@ -2016,7 +2004,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
2016
2004
  chainPubkey: content.public_key || "",
2017
2005
  l1Address: content.l1_address || "",
2018
2006
  directAddress: content.direct_address || "",
2019
- proxyAddress: content.proxy_address || void 0,
2020
2007
  timestamp: bindingEvent.created_at * 1e3
2021
2008
  };
2022
2009
  } catch {
@@ -2059,7 +2046,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
2059
2046
  chainPubkey: content.public_key || "",
2060
2047
  l1Address: content.l1_address || "",
2061
2048
  directAddress: content.direct_address || "",
2062
- proxyAddress: content.proxy_address || void 0,
2063
2049
  timestamp: event.created_at * 1e3
2064
2050
  });
2065
2051
  } catch {
@@ -2129,8 +2115,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
2129
2115
  }
2130
2116
  const nostrPubkey = this.getNostrPubkey();
2131
2117
  if (nametag) {
2132
- const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
2133
- const proxyAddr = await ProxyAddress.fromNameTag(nametag);
2134
2118
  try {
2135
2119
  const success2 = await this.nostrClient.publishNametagBinding(
2136
2120
  nametag,
@@ -2138,8 +2122,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
2138
2122
  {
2139
2123
  publicKey: chainPubkey,
2140
2124
  l1Address,
2141
- directAddress,
2142
- proxyAddress: proxyAddr.toString()
2125
+ directAddress
2143
2126
  }
2144
2127
  );
2145
2128
  if (success2) {
@@ -6189,6 +6172,13 @@ var L1PaymentsModule = class {
6189
6172
  }
6190
6173
  };
6191
6174
 
6175
+ // types/v2-transfer.ts
6176
+ function isV2TransferPayload(obj) {
6177
+ if (!obj || typeof obj !== "object") return false;
6178
+ const p = obj;
6179
+ return p.type === "V2_TRANSFER" && typeof p.tokenBlob === "string" && p.tokenBlob.length > 0;
6180
+ }
6181
+
6192
6182
  // modules/payments/TokenSplitExecutor.ts
6193
6183
  init_logger();
6194
6184
  init_errors();
@@ -6562,18 +6552,117 @@ var TokenReservationLedger = class {
6562
6552
  // modules/payments/SpendQueue.ts
6563
6553
  init_logger();
6564
6554
  init_errors();
6565
- var import_Token2 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
6555
+ var import_Token3 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
6566
6556
  var import_CoinId2 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
6557
+
6558
+ // token-engine/sdk.ts
6559
+ var import_StateTransitionClient = require("state-transition-sdk-v2/lib/StateTransitionClient.js");
6560
+ var import_AggregatorClient = require("state-transition-sdk-v2/lib/api/AggregatorClient.js");
6561
+ var import_NetworkId = require("state-transition-sdk-v2/lib/api/NetworkId.js");
6562
+ var import_CertificationData = require("state-transition-sdk-v2/lib/api/CertificationData.js");
6563
+ var import_CertificationResponse = require("state-transition-sdk-v2/lib/api/CertificationResponse.js");
6564
+ var import_StateId = require("state-transition-sdk-v2/lib/api/StateId.js");
6565
+ var import_InclusionProof = require("state-transition-sdk-v2/lib/api/InclusionProof.js");
6566
+ var import_InclusionProofResponse = require("state-transition-sdk-v2/lib/api/InclusionProofResponse.js");
6567
+ var import_RootTrustBase = require("state-transition-sdk-v2/lib/api/bft/RootTrustBase.js");
6568
+ var import_InclusionProofUtils2 = require("state-transition-sdk-v2/lib/util/InclusionProofUtils.js");
6569
+ var import_Token2 = require("state-transition-sdk-v2/lib/transaction/Token.js");
6570
+ var import_MintTransaction = require("state-transition-sdk-v2/lib/transaction/MintTransaction.js");
6571
+ var import_TransferTransaction = require("state-transition-sdk-v2/lib/transaction/TransferTransaction.js");
6572
+ var import_CertifiedMintTransaction = require("state-transition-sdk-v2/lib/transaction/CertifiedMintTransaction.js");
6573
+ var import_CertifiedTransferTransaction = require("state-transition-sdk-v2/lib/transaction/CertifiedTransferTransaction.js");
6574
+ var import_TokenId2 = require("state-transition-sdk-v2/lib/transaction/TokenId.js");
6575
+ var import_TokenType = require("state-transition-sdk-v2/lib/transaction/TokenType.js");
6576
+ var import_TokenSalt = require("state-transition-sdk-v2/lib/transaction/TokenSalt.js");
6577
+ var import_MintJustificationVerifierService = require("state-transition-sdk-v2/lib/transaction/verification/MintJustificationVerifierService.js");
6578
+ var import_EncodedPredicate = require("state-transition-sdk-v2/lib/predicate/EncodedPredicate.js");
6579
+ var import_SignaturePredicate = require("state-transition-sdk-v2/lib/predicate/builtin/SignaturePredicate.js");
6580
+ var import_SignaturePredicateUnlockScript = require("state-transition-sdk-v2/lib/predicate/builtin/SignaturePredicateUnlockScript.js");
6581
+ var import_BurnPredicate = require("state-transition-sdk-v2/lib/predicate/builtin/BurnPredicate.js");
6582
+ var import_PredicateVerifierService = require("state-transition-sdk-v2/lib/predicate/verification/PredicateVerifierService.js");
6583
+ var import_SigningService = require("state-transition-sdk-v2/lib/crypto/secp256k1/SigningService.js");
6584
+ var import_Signature = require("state-transition-sdk-v2/lib/crypto/secp256k1/Signature.js");
6585
+ var import_MintSigningService = require("state-transition-sdk-v2/lib/crypto/MintSigningService.js");
6586
+ var import_HashAlgorithm2 = require("state-transition-sdk-v2/lib/crypto/hash/HashAlgorithm.js");
6587
+ var import_DataHash = require("state-transition-sdk-v2/lib/crypto/hash/DataHash.js");
6588
+ var import_DataHasher = require("state-transition-sdk-v2/lib/crypto/hash/DataHasher.js");
6589
+ var import_DataHasherFactory = require("state-transition-sdk-v2/lib/crypto/hash/DataHasherFactory.js");
6590
+ var import_CborSerializer = require("state-transition-sdk-v2/lib/serialization/cbor/CborSerializer.js");
6591
+ var import_CborDeserializer = require("state-transition-sdk-v2/lib/serialization/cbor/CborDeserializer.js");
6592
+ var import_CborError = require("state-transition-sdk-v2/lib/serialization/cbor/CborError.js");
6593
+ var import_Asset = require("state-transition-sdk-v2/lib/payment/asset/Asset.js");
6594
+ var import_AssetId = require("state-transition-sdk-v2/lib/payment/asset/AssetId.js");
6595
+ var import_PaymentAssetCollection = require("state-transition-sdk-v2/lib/payment/asset/PaymentAssetCollection.js");
6596
+ var import_TokenSplit = require("state-transition-sdk-v2/lib/payment/TokenSplit.js");
6597
+ var import_SplitTokenRequest = require("state-transition-sdk-v2/lib/payment/SplitTokenRequest.js");
6598
+ var import_SplitToken = require("state-transition-sdk-v2/lib/payment/SplitToken.js");
6599
+ var import_SplitAssetProof = require("state-transition-sdk-v2/lib/payment/SplitAssetProof.js");
6600
+ var import_SplitMintJustification = require("state-transition-sdk-v2/lib/payment/SplitMintJustification.js");
6601
+ var import_SplitMintJustificationVerifier = require("state-transition-sdk-v2/lib/payment/SplitMintJustificationVerifier.js");
6602
+ var import_VerificationStatus = require("state-transition-sdk-v2/lib/verification/VerificationStatus.js");
6603
+ var import_VerificationResult = require("state-transition-sdk-v2/lib/verification/VerificationResult.js");
6604
+ var import_HexConverter = require("state-transition-sdk-v2/lib/util/HexConverter.js");
6605
+ var import_BigintConverter = require("state-transition-sdk-v2/lib/util/BigintConverter.js");
6606
+ var import_BitString = require("state-transition-sdk-v2/lib/util/BitString.js");
6607
+
6608
+ // token-engine/token-blob.ts
6609
+ var TOKEN_BLOB_TAG = 39051n;
6610
+ var TOKEN_BLOB_VERSION = 1;
6611
+ function encodeTokenBlob(blob) {
6612
+ return import_CborSerializer.CborSerializer.encodeTag(
6613
+ TOKEN_BLOB_TAG,
6614
+ import_CborSerializer.CborSerializer.encodeArray(
6615
+ import_CborSerializer.CborSerializer.encodeUnsignedInteger(BigInt(blob.v)),
6616
+ import_CborSerializer.CborSerializer.encodeUnsignedInteger(BigInt(blob.network)),
6617
+ import_CborSerializer.CborSerializer.encodeTextString(blob.tokenId),
6618
+ import_CborSerializer.CborSerializer.encodeByteString(blob.token)
6619
+ )
6620
+ );
6621
+ }
6622
+ function decodeTokenBlob(bytes) {
6623
+ const tag = import_CborDeserializer.CborDeserializer.decodeTag(bytes);
6624
+ if (tag.tag !== TOKEN_BLOB_TAG) {
6625
+ throw new import_CborError.CborError(`Invalid TokenBlob tag: ${tag.tag}`);
6626
+ }
6627
+ const fields = import_CborDeserializer.CborDeserializer.decodeArray(tag.data, 4);
6628
+ const v = Number(import_CborDeserializer.CborDeserializer.decodeUnsignedInteger(fields[0]));
6629
+ if (v !== TOKEN_BLOB_VERSION) {
6630
+ throw new import_CborError.CborError(`Unsupported TokenBlob version: ${v}`);
6631
+ }
6632
+ return {
6633
+ v,
6634
+ network: Number(import_CborDeserializer.CborDeserializer.decodeUnsignedInteger(fields[1])),
6635
+ tokenId: import_CborDeserializer.CborDeserializer.decodeTextString(fields[2]),
6636
+ token: import_CborDeserializer.CborDeserializer.decodeByteString(fields[3])
6637
+ };
6638
+ }
6639
+
6640
+ // modules/payments/SpendQueue.ts
6567
6641
  var QUEUE_TIMEOUT_MS = 3e4;
6568
6642
  var QUEUE_MAX_SIZE = 100;
6569
6643
  var TAG2 = "SpendQueue";
6570
6644
  var SpendPlanner = class {
6645
+ /**
6646
+ * Token engine (path B). When injected, value reads go through the v2 engine
6647
+ * (sdkData = engine blob); otherwise the legacy v1 SdkToken path is used. The
6648
+ * engine is wired after construction via {@link setEngine} (it is bound to the
6649
+ * payments deps, which arrive after the planner is built).
6650
+ */
6651
+ engine;
6652
+ constructor(engine) {
6653
+ this.engine = engine;
6654
+ }
6655
+ /** Inject (or clear) the token engine. */
6656
+ setEngine(engine) {
6657
+ this.engine = engine;
6658
+ }
6571
6659
  /**
6572
6660
  * Async pre-computation: parse all tokens for a given coinId.
6573
6661
  * Called BEFORE the synchronous critical section.
6574
6662
  *
6575
- * Filters to confirmed tokens matching the coinId, parses each
6576
- * token's sdkData into an SdkToken, and extracts the bigint amount.
6663
+ * Filters to confirmed tokens matching the coinId, decodes each token's
6664
+ * sdkData and extracts the bigint amount — via the v2 engine when injected,
6665
+ * else the legacy v1 SdkToken path.
6577
6666
  */
6578
6667
  async buildParsedPool(tokens, coinId) {
6579
6668
  const pool = /* @__PURE__ */ new Map();
@@ -6582,20 +6671,28 @@ var SpendPlanner = class {
6582
6671
  if (t.status !== "confirmed") continue;
6583
6672
  if (!t.sdkData) continue;
6584
6673
  try {
6585
- const parsed = JSON.parse(t.sdkData);
6586
- const sdkToken = await import_Token2.Token.fromJSON(parsed);
6587
- const realAmount = this.getTokenBalance(sdkToken, coinId);
6588
- if (realAmount <= 0n) {
6674
+ const { sdkToken, amount } = this.engine ? await this.parseViaEngine(t.sdkData, coinId) : await this.parseViaSdk(t.sdkData, coinId);
6675
+ if (amount <= 0n) {
6589
6676
  logger.warn(TAG2, `Token ${t.id} has 0 balance for coinId ${coinId}`);
6590
6677
  continue;
6591
6678
  }
6592
- pool.set(t.id, { token: t, sdkToken, amount: realAmount });
6679
+ pool.set(t.id, { token: t, sdkToken, amount });
6593
6680
  } catch (e) {
6594
6681
  logger.warn(TAG2, "Failed to parse token", t.id, e);
6595
6682
  }
6596
6683
  }
6597
6684
  return pool;
6598
6685
  }
6686
+ /** Engine path (v2): sdkData is the engine blob (hex of CBOR(TokenBlob)). */
6687
+ async parseViaEngine(sdkData, coinId) {
6688
+ const token = await this.engine.decodeToken(decodeTokenBlob(hexToBytes2(sdkData)));
6689
+ return { sdkToken: token, amount: this.engine.balanceOf(token, coinId) };
6690
+ }
6691
+ /** Legacy v1 path: sdkData is TXF JSON. */
6692
+ async parseViaSdk(sdkData, coinId) {
6693
+ const sdkToken = await import_Token3.Token.fromJSON(JSON.parse(sdkData));
6694
+ return { sdkToken, amount: this.getTokenBalance(sdkToken, coinId) };
6695
+ }
6599
6696
  /**
6600
6697
  * SYNCHRONOUS critical section. NO await allowed anywhere in this method.
6601
6698
  *
@@ -7006,174 +7103,6 @@ var SpendQueue = class {
7006
7103
  }
7007
7104
  };
7008
7105
 
7009
- // modules/payments/NametagMinter.ts
7010
- init_logger();
7011
- var import_Token3 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
7012
- var import_TokenId2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
7013
- var import_TokenType = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
7014
- var import_TokenState2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
7015
- var import_MintTransactionData = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
7016
- var import_MintCommitment = require("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment");
7017
- var import_HashAlgorithm2 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
7018
- var import_UnmaskedPredicate2 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
7019
- var import_InclusionProofUtils2 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
7020
- var import_nostr_js_sdk3 = require("@unicitylabs/nostr-js-sdk");
7021
- var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
7022
- var NametagMinter = class {
7023
- client;
7024
- trustBase;
7025
- signingService;
7026
- skipVerification;
7027
- debug;
7028
- constructor(config) {
7029
- this.client = config.stateTransitionClient;
7030
- this.trustBase = config.trustBase;
7031
- this.signingService = config.signingService;
7032
- this.skipVerification = config.skipVerification ?? false;
7033
- this.debug = config.debug ?? false;
7034
- }
7035
- log(message, ...args) {
7036
- logger.debug("NametagMinter", message, ...args);
7037
- }
7038
- /**
7039
- * Check if a nametag is available (not already minted)
7040
- */
7041
- async isNametagAvailable(nametag) {
7042
- try {
7043
- const stripped = nametag.startsWith("@") ? nametag.slice(1) : nametag;
7044
- const cleanNametag = (0, import_nostr_js_sdk3.normalizeNametag)(stripped);
7045
- const nametagTokenId = await import_TokenId2.TokenId.fromNameTag(cleanNametag);
7046
- const isMinted = await this.client.isMinted(this.trustBase, nametagTokenId);
7047
- return !isMinted;
7048
- } catch (error) {
7049
- this.log("Error checking nametag availability:", error);
7050
- return false;
7051
- }
7052
- }
7053
- /**
7054
- * Mint a nametag token on-chain
7055
- *
7056
- * @param nametag - The nametag to mint (e.g., "alice" or "@alice")
7057
- * @param ownerAddress - The owner's direct address
7058
- * @returns MintNametagResult with token if successful
7059
- */
7060
- async mintNametag(nametag, ownerAddress) {
7061
- const stripped = nametag.startsWith("@") ? nametag.slice(1) : nametag;
7062
- const cleanNametag = (0, import_nostr_js_sdk3.normalizeNametag)(stripped);
7063
- this.log(`Starting mint for nametag: ${cleanNametag}`);
7064
- try {
7065
- const nametagTokenId = await import_TokenId2.TokenId.fromNameTag(cleanNametag);
7066
- const nametagTokenType = new import_TokenType.TokenType(
7067
- Buffer.from(UNICITY_TOKEN_TYPE_HEX, "hex")
7068
- );
7069
- const nametagBytes = new TextEncoder().encode(cleanNametag);
7070
- const pubKey = this.signingService.publicKey;
7071
- const saltInput = new Uint8Array(pubKey.length + nametagBytes.length);
7072
- saltInput.set(pubKey, 0);
7073
- saltInput.set(nametagBytes, pubKey.length);
7074
- const saltBuffer = await crypto.subtle.digest("SHA-256", saltInput);
7075
- const salt = new Uint8Array(saltBuffer);
7076
- this.log("Generated deterministic salt");
7077
- const mintData = await import_MintTransactionData.MintTransactionData.createFromNametag(
7078
- cleanNametag,
7079
- nametagTokenType,
7080
- ownerAddress,
7081
- salt,
7082
- ownerAddress
7083
- );
7084
- this.log("Created MintTransactionData");
7085
- const commitment = await import_MintCommitment.MintCommitment.create(mintData);
7086
- this.log("Created MintCommitment");
7087
- const MAX_RETRIES = 3;
7088
- let submitSuccess = false;
7089
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
7090
- try {
7091
- this.log(`Submitting commitment (attempt ${attempt}/${MAX_RETRIES})...`);
7092
- const response = await this.client.submitMintCommitment(commitment);
7093
- if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
7094
- this.log(`Commitment ${response.status === "REQUEST_ID_EXISTS" ? "already exists" : "submitted successfully"}`);
7095
- submitSuccess = true;
7096
- break;
7097
- } else {
7098
- this.log(`Commitment failed: ${response.status}`);
7099
- if (attempt === MAX_RETRIES) {
7100
- return {
7101
- success: false,
7102
- error: `Failed to submit commitment after ${MAX_RETRIES} attempts: ${response.status}`
7103
- };
7104
- }
7105
- await new Promise((r) => setTimeout(r, 1e3 * attempt));
7106
- }
7107
- } catch (error) {
7108
- this.log(`Attempt ${attempt} error:`, error);
7109
- if (attempt === MAX_RETRIES) {
7110
- return {
7111
- success: false,
7112
- error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`
7113
- };
7114
- }
7115
- await new Promise((r) => setTimeout(r, 1e3 * attempt));
7116
- }
7117
- }
7118
- if (!submitSuccess) {
7119
- return {
7120
- success: false,
7121
- error: "Failed to submit commitment after retries"
7122
- };
7123
- }
7124
- this.log("Waiting for inclusion proof...");
7125
- const inclusionProof = await (0, import_InclusionProofUtils2.waitInclusionProof)(this.trustBase, this.client, commitment);
7126
- this.log("Received inclusion proof");
7127
- const genesisTransaction = commitment.toTransaction(inclusionProof);
7128
- const nametagPredicate = await import_UnmaskedPredicate2.UnmaskedPredicate.create(
7129
- nametagTokenId,
7130
- nametagTokenType,
7131
- this.signingService,
7132
- import_HashAlgorithm2.HashAlgorithm.SHA256,
7133
- salt
7134
- );
7135
- const tokenState = new import_TokenState2.TokenState(nametagPredicate, null);
7136
- let token;
7137
- if (this.skipVerification) {
7138
- this.log("Creating token WITHOUT verification (dev mode)");
7139
- const tokenJson = {
7140
- version: "2.0",
7141
- state: tokenState.toJSON(),
7142
- genesis: genesisTransaction.toJSON(),
7143
- transactions: [],
7144
- nametags: []
7145
- };
7146
- token = await import_Token3.Token.fromJSON(tokenJson);
7147
- } else {
7148
- token = await import_Token3.Token.mint(
7149
- this.trustBase,
7150
- tokenState,
7151
- genesisTransaction
7152
- );
7153
- }
7154
- this.log(`Nametag minted successfully: ${cleanNametag}`);
7155
- const nametagData = {
7156
- name: cleanNametag,
7157
- token: token.toJSON(),
7158
- timestamp: Date.now(),
7159
- format: "txf",
7160
- version: "2.0"
7161
- };
7162
- return {
7163
- success: true,
7164
- token,
7165
- nametagData
7166
- };
7167
- } catch (error) {
7168
- this.log("Minting failed:", error);
7169
- return {
7170
- success: false,
7171
- error: error instanceof Error ? error.message : String(error)
7172
- };
7173
- }
7174
- }
7175
- };
7176
-
7177
7106
  // modules/payments/PaymentsModule.ts
7178
7107
  init_constants();
7179
7108
 
@@ -7756,6 +7685,15 @@ function txfToToken(tokenId, txf) {
7756
7685
  sdkData: JSON.stringify(txf)
7757
7686
  };
7758
7687
  }
7688
+ function isV2TokenBlob(sdkData) {
7689
+ return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
7690
+ }
7691
+ function v2TokenId(token) {
7692
+ return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
7693
+ }
7694
+ function isV2TokenEntry(entry) {
7695
+ return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
7696
+ }
7759
7697
  async function buildTxfStorageData(tokens, meta, options) {
7760
7698
  const storageData = {
7761
7699
  _meta: {
@@ -7786,6 +7724,8 @@ async function buildTxfStorageData(tokens, meta, options) {
7786
7724
  if (txf) {
7787
7725
  const actualTokenId = txf.genesis.data.tokenId;
7788
7726
  storageData[keyFromTokenId(actualTokenId)] = txf;
7727
+ } else if (isV2TokenBlob(token.sdkData)) {
7728
+ storageData[keyFromTokenId(v2TokenId(token))] = token;
7789
7729
  }
7790
7730
  }
7791
7731
  if (options?.archivedTokens && options.archivedTokens.size > 0) {
@@ -7883,6 +7823,8 @@ function parseTxfStorageData(data) {
7883
7823
  if (txfToken?.genesis?.data?.tokenId) {
7884
7824
  const token = txfToToken(tokenId, txfToken);
7885
7825
  result.tokens.push(token);
7826
+ } else if (isV2TokenEntry(storageData[key])) {
7827
+ result.tokens.push(storageData[key]);
7886
7828
  }
7887
7829
  } catch (err) {
7888
7830
  result.validationErrors.push(`Token ${tokenId}: ${err}`);
@@ -8107,12 +8049,12 @@ init_logger();
8107
8049
  init_errors();
8108
8050
  var import_Token4 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
8109
8051
  var import_TokenId3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
8110
- var import_TokenState3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
8052
+ var import_TokenState2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
8111
8053
  var import_CoinId3 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
8112
8054
  var import_TokenCoinData2 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData");
8113
8055
  var import_TokenSplitBuilder2 = require("@unicitylabs/state-transition-sdk/lib/transaction/split/TokenSplitBuilder");
8114
8056
  var import_HashAlgorithm3 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
8115
- var import_UnmaskedPredicate3 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
8057
+ var import_UnmaskedPredicate2 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
8116
8058
  var import_UnmaskedPredicateReference2 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
8117
8059
  var import_TransferCommitment2 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment");
8118
8060
  var import_InclusionProofUtils3 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
@@ -8234,14 +8176,14 @@ var InstantSplitExecutor = class {
8234
8176
  options?.message
8235
8177
  // on-chain message (invoice memo bytes, or null)
8236
8178
  );
8237
- const mintedPredicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
8179
+ const mintedPredicate = await import_UnmaskedPredicate2.UnmaskedPredicate.create(
8238
8180
  recipientTokenId,
8239
8181
  tokenToSplit.type,
8240
8182
  this.signingService,
8241
8183
  import_HashAlgorithm3.HashAlgorithm.SHA256,
8242
8184
  recipientSalt
8243
8185
  );
8244
- const mintedState = new import_TokenState3.TokenState(mintedPredicate, null);
8186
+ const mintedState = new import_TokenState2.TokenState(mintedPredicate, null);
8245
8187
  logger.debug("InstantSplit", "Step 5: Packaging V5 bundle...");
8246
8188
  const senderPubkey = toHex2(this.signingService.publicKey);
8247
8189
  let nametagTokenJson;
@@ -8356,14 +8298,14 @@ var InstantSplitExecutor = class {
8356
8298
  * It does NOT need the genesis transaction or mint proof.
8357
8299
  */
8358
8300
  async createTransferCommitmentFromMintData(mintData, recipientAddress, transferSalt, signingService, nametagTokens, message) {
8359
- const predicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
8301
+ const predicate = await import_UnmaskedPredicate2.UnmaskedPredicate.create(
8360
8302
  mintData.tokenId,
8361
8303
  mintData.tokenType,
8362
8304
  signingService,
8363
8305
  import_HashAlgorithm3.HashAlgorithm.SHA256,
8364
8306
  mintData.salt
8365
8307
  );
8366
- const state = new import_TokenState3.TokenState(predicate, null);
8308
+ const state = new import_TokenState2.TokenState(predicate, null);
8367
8309
  const minimalToken = {
8368
8310
  state,
8369
8311
  nametagTokens: nametagTokens || [],
@@ -8424,14 +8366,14 @@ var InstantSplitExecutor = class {
8424
8366
  message: `Mint proof received in ${proofDuration.toFixed(0)}ms`
8425
8367
  });
8426
8368
  const mintTransaction = senderMintCommitment.toTransaction(senderMintProof);
8427
- const predicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
8369
+ const predicate = await import_UnmaskedPredicate2.UnmaskedPredicate.create(
8428
8370
  context.senderTokenId,
8429
8371
  context.tokenType,
8430
8372
  context.signingService,
8431
8373
  import_HashAlgorithm3.HashAlgorithm.SHA256,
8432
8374
  context.senderSalt
8433
8375
  );
8434
- const state = new import_TokenState3.TokenState(predicate, null);
8376
+ const state = new import_TokenState2.TokenState(predicate, null);
8435
8377
  const changeToken = await import_Token4.Token.mint(this.trustBase, state, mintTransaction);
8436
8378
  if (!this.devMode) {
8437
8379
  const verification = await changeToken.verify(this.trustBase);
@@ -8511,14 +8453,14 @@ var InstantSplitExecutor = class {
8511
8453
  init_logger();
8512
8454
  init_errors();
8513
8455
  var import_Token5 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
8514
- var import_TokenState4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
8456
+ var import_TokenState3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
8515
8457
  var import_TokenType2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
8516
8458
  var import_HashAlgorithm4 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
8517
- var import_UnmaskedPredicate4 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
8459
+ var import_UnmaskedPredicate3 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
8518
8460
  var import_TransferCommitment3 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment");
8519
- var import_TransferTransaction = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction");
8520
- var import_MintCommitment2 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment");
8521
- var import_MintTransactionData2 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
8461
+ var import_TransferTransaction2 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction");
8462
+ var import_MintCommitment = require("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment");
8463
+ var import_MintTransactionData = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
8522
8464
  var import_InclusionProofUtils4 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
8523
8465
 
8524
8466
  // types/instant-split.ts
@@ -8611,11 +8553,11 @@ var InstantSplitProcessor = class {
8611
8553
  logger.warn("InstantSplit", "Sender pubkey mismatch (non-fatal)");
8612
8554
  }
8613
8555
  const burnTxJson = JSON.parse(bundle.burnTransaction);
8614
- const _burnTransaction = await import_TransferTransaction.TransferTransaction.fromJSON(burnTxJson);
8556
+ const _burnTransaction = await import_TransferTransaction2.TransferTransaction.fromJSON(burnTxJson);
8615
8557
  logger.debug("InstantSplit", "Burn transaction validated");
8616
8558
  const mintDataJson = JSON.parse(bundle.recipientMintData);
8617
- const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
8618
- const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
8559
+ const mintData = await import_MintTransactionData.MintTransactionData.fromJSON(mintDataJson);
8560
+ const mintCommitment = await import_MintCommitment.MintCommitment.create(mintData);
8619
8561
  logger.debug("InstantSplit", "Mint commitment recreated");
8620
8562
  const mintResponse = await this.client.submitMintCommitment(mintCommitment);
8621
8563
  if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
@@ -8647,14 +8589,14 @@ var InstantSplitProcessor = class {
8647
8589
  const transferTransaction = transferCommitment.toTransaction(transferProof);
8648
8590
  logger.debug("InstantSplit", "Transfer proof received");
8649
8591
  const transferSalt = fromHex3(bundle.transferSaltHex);
8650
- const finalRecipientPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
8592
+ const finalRecipientPredicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
8651
8593
  mintData.tokenId,
8652
8594
  tokenType,
8653
8595
  signingService,
8654
8596
  import_HashAlgorithm4.HashAlgorithm.SHA256,
8655
8597
  transferSalt
8656
8598
  );
8657
- const finalRecipientState = new import_TokenState4.TokenState(finalRecipientPredicate, null);
8599
+ const finalRecipientState = new import_TokenState3.TokenState(finalRecipientPredicate, null);
8658
8600
  logger.debug("InstantSplit", "Final recipient state created");
8659
8601
  let nametagTokens = [];
8660
8602
  const recipientAddressStr = bundle.recipientAddressJson;
@@ -8761,8 +8703,8 @@ var InstantSplitProcessor = class {
8761
8703
  await this.waitInclusionProofWithDevBypass(burnCommitment, options?.proofTimeoutMs);
8762
8704
  logger.debug("InstantSplit", "V4: Burn proof received");
8763
8705
  const mintDataJson = JSON.parse(bundle.recipientMintData);
8764
- const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
8765
- const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
8706
+ const mintData = await import_MintTransactionData.MintTransactionData.fromJSON(mintDataJson);
8707
+ const mintCommitment = await import_MintCommitment.MintCommitment.create(mintData);
8766
8708
  const mintResponse = await this.client.submitMintCommitment(mintCommitment);
8767
8709
  if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
8768
8710
  throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
@@ -8775,14 +8717,14 @@ var InstantSplitProcessor = class {
8775
8717
  logger.debug("InstantSplit", "V4: Mint proof received");
8776
8718
  const tokenType = new import_TokenType2.TokenType(fromHex3(bundle.tokenTypeHex));
8777
8719
  const recipientSalt = fromHex3(bundle.recipientSaltHex);
8778
- const recipientPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
8720
+ const recipientPredicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
8779
8721
  mintData.tokenId,
8780
8722
  tokenType,
8781
8723
  signingService,
8782
8724
  import_HashAlgorithm4.HashAlgorithm.SHA256,
8783
8725
  recipientSalt
8784
8726
  );
8785
- const recipientState = new import_TokenState4.TokenState(recipientPredicate, null);
8727
+ const recipientState = new import_TokenState3.TokenState(recipientPredicate, null);
8786
8728
  const tokenJson = {
8787
8729
  version: "2.0",
8788
8730
  state: recipientState.toJSON(),
@@ -8805,14 +8747,14 @@ var InstantSplitProcessor = class {
8805
8747
  const transferTransaction = transferCommitment.toTransaction(transferProof);
8806
8748
  logger.debug("InstantSplit", "V4: Transfer proof received");
8807
8749
  const transferSalt = fromHex3(bundle.transferSaltHex);
8808
- const finalPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
8750
+ const finalPredicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
8809
8751
  mintData.tokenId,
8810
8752
  tokenType,
8811
8753
  signingService,
8812
8754
  import_HashAlgorithm4.HashAlgorithm.SHA256,
8813
8755
  transferSalt
8814
8756
  );
8815
- const finalState = new import_TokenState4.TokenState(finalPredicate, null);
8757
+ const finalState = new import_TokenState3.TokenState(finalPredicate, null);
8816
8758
  const finalTokenJson = mintedToken.toJSON();
8817
8759
  finalTokenJson.state = finalState.toJSON();
8818
8760
  finalTokenJson.transactions = [transferTransaction.toJSON()];
@@ -8863,17 +8805,17 @@ var InstantSplitProcessor = class {
8863
8805
  var import_Token6 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
8864
8806
  var import_CoinId4 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
8865
8807
  var import_TransferCommitment4 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment");
8866
- var import_TransferTransaction2 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction");
8867
- var import_SigningService = require("@unicitylabs/state-transition-sdk/lib/sign/SigningService");
8808
+ var import_TransferTransaction3 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction");
8809
+ var import_SigningService2 = require("@unicitylabs/state-transition-sdk/lib/sign/SigningService");
8868
8810
  var import_AddressScheme = require("@unicitylabs/state-transition-sdk/lib/address/AddressScheme");
8869
- var import_UnmaskedPredicate5 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
8870
- var import_TokenState5 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
8811
+ var import_UnmaskedPredicate4 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
8812
+ var import_TokenState4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
8871
8813
  var import_HashAlgorithm5 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
8872
8814
  var import_TokenType3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
8873
- var import_MintCommitment3 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment");
8874
- var import_MintTransactionData3 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
8815
+ var import_MintCommitment2 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment");
8816
+ var import_MintTransactionData2 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
8875
8817
  var import_InclusionProofUtils5 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
8876
- var import_InclusionProof = require("@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof");
8818
+ var import_InclusionProof2 = require("@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof");
8877
8819
  function computeHistoryDedupKey(type, tokenId, transferId) {
8878
8820
  if (type === "SENT" && transferId) return `${type}_transfer_${transferId}`;
8879
8821
  if (tokenId) return `${type}_${tokenId}`;
@@ -8894,7 +8836,7 @@ function enrichWithRegistry(info) {
8894
8836
  }
8895
8837
  return info;
8896
8838
  }
8897
- async function parseTokenInfo(tokenData) {
8839
+ async function parseTokenInfo(tokenData, engine) {
8898
8840
  const defaultInfo = {
8899
8841
  coinId: "ALPHA",
8900
8842
  symbol: "ALPHA",
@@ -8902,6 +8844,25 @@ async function parseTokenInfo(tokenData) {
8902
8844
  decimals: 0,
8903
8845
  amount: "0"
8904
8846
  };
8847
+ if (engine && typeof tokenData === "string" && looksLikeTokenBlob(tokenData)) {
8848
+ try {
8849
+ const token = await engine.decodeToken(decodeTokenBlob(hexToBytes2(tokenData)));
8850
+ const first = engine.readValue(token)?.assets[0];
8851
+ if (first) {
8852
+ return enrichWithRegistry({
8853
+ coinId: first.coinId,
8854
+ symbol: first.coinId.slice(0, 8),
8855
+ name: `Token ${first.coinId.slice(0, 8)}`,
8856
+ decimals: 0,
8857
+ amount: String(first.amount),
8858
+ tokenId: engine.tokenId(token)
8859
+ });
8860
+ }
8861
+ return { ...defaultInfo, tokenId: engine.tokenId(token) };
8862
+ } catch (error) {
8863
+ logger.warn("Payments", "Failed to parse token info via engine:", error);
8864
+ }
8865
+ }
8905
8866
  try {
8906
8867
  const data = typeof tokenData === "string" ? JSON.parse(tokenData) : tokenData;
8907
8868
  try {
@@ -9040,27 +9001,41 @@ async function parseTokenInfo(tokenData) {
9040
9001
  }
9041
9002
  var sdkDataCache = /* @__PURE__ */ new Map();
9042
9003
  var SDK_DATA_CACHE_MAX = 2e3;
9004
+ function looksLikeTokenBlob(sdkData) {
9005
+ return sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
9006
+ }
9007
+ function tryParseBlobKeys(sdkData) {
9008
+ try {
9009
+ const blob = decodeTokenBlob(hexToBytes2(sdkData));
9010
+ return { tokenId: blob.tokenId, stateHash: sha2562(bytesToHex3(blob.token), "hex") };
9011
+ } catch {
9012
+ return null;
9013
+ }
9014
+ }
9043
9015
  function parseSdkDataCached(sdkData) {
9044
9016
  const cached = sdkDataCache.get(sdkData);
9045
9017
  if (cached) return cached;
9046
- let tokenId = null;
9047
- let stateHash = "";
9048
- try {
9049
- const txf = JSON.parse(sdkData);
9050
- tokenId = txf.genesis?.data?.tokenId || null;
9051
- stateHash = getCurrentStateHash(txf) || "";
9052
- if (!stateHash) {
9053
- if (txf.state?.hash) {
9054
- stateHash = txf.state.hash;
9055
- } else if (txf.stateHash) {
9056
- stateHash = txf.stateHash;
9057
- } else if (txf.currentStateHash) {
9058
- stateHash = txf.currentStateHash;
9018
+ let entry = looksLikeTokenBlob(sdkData) ? tryParseBlobKeys(sdkData) : null;
9019
+ if (!entry) {
9020
+ let tokenId = null;
9021
+ let stateHash = "";
9022
+ try {
9023
+ const txf = JSON.parse(sdkData);
9024
+ tokenId = txf.genesis?.data?.tokenId || null;
9025
+ stateHash = getCurrentStateHash(txf) || "";
9026
+ if (!stateHash) {
9027
+ if (txf.state?.hash) {
9028
+ stateHash = txf.state.hash;
9029
+ } else if (txf.stateHash) {
9030
+ stateHash = txf.stateHash;
9031
+ } else if (txf.currentStateHash) {
9032
+ stateHash = txf.currentStateHash;
9033
+ }
9059
9034
  }
9035
+ } catch {
9060
9036
  }
9061
- } catch {
9037
+ entry = { tokenId, stateHash };
9062
9038
  }
9063
- const entry = { tokenId, stateHash };
9064
9039
  if (sdkDataCache.size >= SDK_DATA_CACHE_MAX) {
9065
9040
  sdkDataCache.clear();
9066
9041
  }
@@ -9337,6 +9312,7 @@ var PaymentsModule = class _PaymentsModule {
9337
9312
  );
9338
9313
  this.deps = deps;
9339
9314
  this.priceProvider = deps.price ?? null;
9315
+ this.spendPlanner.setEngine(deps.tokenEngine);
9340
9316
  if (this.l1) {
9341
9317
  this.l1.initialize({
9342
9318
  identity: deps.identity,
@@ -9561,7 +9537,59 @@ var PaymentsModule = class _PaymentsModule {
9561
9537
  request.invoiceRefundAddress,
9562
9538
  request.invoiceContact
9563
9539
  );
9564
- if (transferMode === "conservative") {
9540
+ if (this.deps?.tokenEngine && peerInfo?.chainPubkey) {
9541
+ const engine = this.deps.tokenEngine;
9542
+ const recipientChainPubkey = hexToBytes2(peerInfo.chainPubkey);
9543
+ const memoData = onChainMessage ?? void 0;
9544
+ const handToRecipient = async (finished) => {
9545
+ const tokenBlob = bytesToHex3(encodeTokenBlob(engine.encodeToken(finished)));
9546
+ await this.deps.transport.sendTokenTransfer(recipientPubkey, {
9547
+ type: "V2_TRANSFER",
9548
+ version: "2.0",
9549
+ tokenBlob,
9550
+ memo: request.memo
9551
+ });
9552
+ };
9553
+ for (const tw of splitPlan.tokensToTransferDirectly) {
9554
+ const finished = await engine.transfer({
9555
+ token: tw.sdkToken,
9556
+ recipientPubkey: recipientChainPubkey,
9557
+ data: memoData
9558
+ });
9559
+ await handToRecipient(finished);
9560
+ result.tokenTransfers.push({ sourceTokenId: tw.uiToken.id, method: "direct" });
9561
+ await this.removeToken(tw.uiToken.id, result.id);
9562
+ }
9563
+ if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
9564
+ const selfChainPubkey = hexToBytes2(this.deps.identity.chainPubkey);
9565
+ const { outputs } = await engine.split({
9566
+ token: splitPlan.tokenToSplit.sdkToken,
9567
+ outputs: [
9568
+ { recipientPubkey: recipientChainPubkey, coinId: request.coinId, amount: splitPlan.splitAmount, data: memoData },
9569
+ { recipientPubkey: selfChainPubkey, coinId: request.coinId, amount: splitPlan.remainderAmount }
9570
+ ]
9571
+ });
9572
+ await handToRecipient(outputs[0]);
9573
+ const changeToken = outputs[1];
9574
+ const changeBlob = bytesToHex3(encodeTokenBlob(engine.encodeToken(changeToken)));
9575
+ const changeInfo = await parseTokenInfo(changeBlob, engine);
9576
+ const registry = TokenRegistry.getInstance();
9577
+ await this.addToken({
9578
+ id: `v2_${engine.tokenId(changeToken)}`,
9579
+ coinId: changeInfo.coinId,
9580
+ symbol: registry.getSymbol(changeInfo.coinId) || changeInfo.symbol,
9581
+ name: registry.getName(changeInfo.coinId) || changeInfo.name,
9582
+ decimals: registry.getDecimals(changeInfo.coinId) ?? changeInfo.decimals,
9583
+ amount: changeInfo.amount,
9584
+ status: "confirmed",
9585
+ createdAt: Date.now(),
9586
+ updatedAt: Date.now(),
9587
+ sdkData: changeBlob
9588
+ });
9589
+ result.tokenTransfers.push({ sourceTokenId: splitPlan.tokenToSplit.uiToken.id, method: "split" });
9590
+ await this.removeToken(splitPlan.tokenToSplit.uiToken.id, result.id);
9591
+ }
9592
+ } else if (transferMode === "conservative") {
9565
9593
  if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
9566
9594
  logger.debug("Payments", "Executing conservative split...");
9567
9595
  const splitExecutor = new TokenSplitExecutor({
@@ -10157,7 +10185,7 @@ var PaymentsModule = class _PaymentsModule {
10157
10185
  * because bundle-level dedup protects against replays, and split children share genesis IDs.
10158
10186
  */
10159
10187
  async saveCommitmentOnlyToken(sourceTokenInput, commitmentInput, senderPubkey, deferPersistence = false, skipGenesisDedup = false) {
10160
- const tokenInfo = await parseTokenInfo(sourceTokenInput);
10188
+ const tokenInfo = await parseTokenInfo(sourceTokenInput, this.deps?.tokenEngine);
10161
10189
  const sdkData = typeof sourceTokenInput === "string" ? sourceTokenInput : JSON.stringify(sourceTokenInput);
10162
10190
  const nostrTokenId = extractTokenIdFromSdkData(sdkData);
10163
10191
  const nostrStateHash = extractStateHashFromSdkData(sdkData);
@@ -11249,8 +11277,8 @@ var PaymentsModule = class _PaymentsModule {
11249
11277
  if (pending2.stage === "RECEIVED") {
11250
11278
  logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: RECEIVED \u2192 submitting mint commitment...`);
11251
11279
  const mintDataJson = JSON.parse(bundle.recipientMintData);
11252
- const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
11253
- const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
11280
+ const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
11281
+ const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
11254
11282
  const mintResponse = await stClient.submitMintCommitment(mintCommitment);
11255
11283
  logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint response status=${mintResponse.status}`);
11256
11284
  if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
@@ -11262,8 +11290,8 @@ var PaymentsModule = class _PaymentsModule {
11262
11290
  if (pending2.stage === "MINT_SUBMITTED") {
11263
11291
  logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_SUBMITTED \u2192 checking mint proof...`);
11264
11292
  const mintDataJson = JSON.parse(bundle.recipientMintData);
11265
- const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
11266
- const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
11293
+ const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
11294
+ const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
11267
11295
  const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);
11268
11296
  if (!proof) {
11269
11297
  logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof not yet available, staying MINT_SUBMITTED`);
@@ -11360,10 +11388,10 @@ var PaymentsModule = class _PaymentsModule {
11360
11388
  */
11361
11389
  async finalizeFromV5Bundle(bundle, pending2, signingService, stClient, trustBase) {
11362
11390
  const mintDataJson = JSON.parse(bundle.recipientMintData);
11363
- const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
11364
- const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
11391
+ const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
11392
+ const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
11365
11393
  const mintProofJson = JSON.parse(pending2.mintProofJson);
11366
- const mintProof = import_InclusionProof.InclusionProof.fromJSON(mintProofJson);
11394
+ const mintProof = import_InclusionProof2.InclusionProof.fromJSON(mintProofJson);
11367
11395
  const mintTransaction = mintCommitment.toTransaction(mintProof);
11368
11396
  const tokenType = new import_TokenType3.TokenType(fromHex4(bundle.tokenTypeHex));
11369
11397
  const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);
@@ -11380,14 +11408,14 @@ var PaymentsModule = class _PaymentsModule {
11380
11408
  const transferProof = await (0, import_InclusionProofUtils5.waitInclusionProof)(trustBase, stClient, transferCommitment);
11381
11409
  const transferTransaction = transferCommitment.toTransaction(transferProof);
11382
11410
  const transferSalt = fromHex4(bundle.transferSaltHex);
11383
- const recipientPredicate = await import_UnmaskedPredicate5.UnmaskedPredicate.create(
11411
+ const recipientPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
11384
11412
  mintData.tokenId,
11385
11413
  tokenType,
11386
11414
  signingService,
11387
11415
  import_HashAlgorithm5.HashAlgorithm.SHA256,
11388
11416
  transferSalt
11389
11417
  );
11390
- const recipientState = new import_TokenState5.TokenState(recipientPredicate, null);
11418
+ const recipientState = new import_TokenState4.TokenState(recipientPredicate, null);
11391
11419
  let nametagTokens = [];
11392
11420
  const recipientAddressStr = bundle.recipientAddressJson;
11393
11421
  if (recipientAddressStr.startsWith("PROXY://")) {
@@ -12124,68 +12152,6 @@ var PaymentsModule = class _PaymentsModule {
12124
12152
  }
12125
12153
  }
12126
12154
  }
12127
- /**
12128
- * Mint a nametag token on-chain (like Sphere wallet and lottery)
12129
- * This creates the nametag token required for receiving tokens via PROXY addresses
12130
- *
12131
- * @param nametag - The nametag to mint (e.g., "alice" or "@alice")
12132
- * @returns MintNametagResult with success status and token if successful
12133
- */
12134
- async mintNametag(nametag) {
12135
- this.ensureInitialized();
12136
- const stClient = this.deps.oracle.getStateTransitionClient?.();
12137
- if (!stClient) {
12138
- return {
12139
- success: false,
12140
- error: "State transition client not available. Oracle provider must implement getStateTransitionClient()"
12141
- };
12142
- }
12143
- const trustBase = this.deps.oracle.getTrustBase?.();
12144
- if (!trustBase) {
12145
- return {
12146
- success: false,
12147
- error: "Trust base not available. Oracle provider must implement getTrustBase()"
12148
- };
12149
- }
12150
- try {
12151
- const signingService = await this.createSigningService();
12152
- const { UnmaskedPredicateReference: UnmaskedPredicateReference4 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
12153
- const { TokenType: TokenType6 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
12154
- const UNICITY_TOKEN_TYPE_HEX3 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
12155
- const tokenType = new TokenType6(Buffer.from(UNICITY_TOKEN_TYPE_HEX3, "hex"));
12156
- const addressRef = await UnmaskedPredicateReference4.create(
12157
- tokenType,
12158
- signingService.algorithm,
12159
- signingService.publicKey,
12160
- import_HashAlgorithm5.HashAlgorithm.SHA256
12161
- );
12162
- const ownerAddress = await addressRef.toAddress();
12163
- const minter = new NametagMinter({
12164
- stateTransitionClient: stClient,
12165
- trustBase,
12166
- signingService,
12167
- debug: this.moduleConfig.debug
12168
- });
12169
- const result = await minter.mintNametag(nametag, ownerAddress);
12170
- if (result.success && result.nametagData) {
12171
- await this.setNametag(result.nametagData);
12172
- logger.debug("Payments", `Unicity ID minted and saved: ${result.nametagData.name}`);
12173
- this.deps.emitEvent("nametag:registered", {
12174
- nametag: result.nametagData.name,
12175
- addressIndex: 0
12176
- // Primary address
12177
- });
12178
- }
12179
- return result;
12180
- } catch (error) {
12181
- const errorMsg = error instanceof Error ? error.message : String(error);
12182
- logger.debug("Payments", "mintNametag failed:", errorMsg);
12183
- return {
12184
- success: false,
12185
- error: errorMsg
12186
- };
12187
- }
12188
- }
12189
12155
  /**
12190
12156
  * Mint a fungible token directly to this wallet (genesis mint).
12191
12157
  *
@@ -12226,18 +12192,18 @@ var PaymentsModule = class _PaymentsModule {
12226
12192
  }
12227
12193
  try {
12228
12194
  const signingService = await this.createSigningService();
12229
- const { TokenId: TokenId5 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId");
12195
+ const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId");
12230
12196
  const { TokenCoinData: TokenCoinData3 } = await import("@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData");
12231
- const { UnmaskedPredicateReference: UnmaskedPredicateReference4 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
12197
+ const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
12232
12198
  const tokenTypeBytes = fromHex4("f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509");
12233
12199
  const tokenType = new import_TokenType3.TokenType(tokenTypeBytes);
12234
12200
  const tokenIdBytes = new Uint8Array(32);
12235
12201
  crypto.getRandomValues(tokenIdBytes);
12236
- const tokenId = new TokenId5(tokenIdBytes);
12202
+ const tokenId = new TokenId4(tokenIdBytes);
12237
12203
  const coinIdBytes = fromHex4(coinIdHex);
12238
12204
  const coinId = new import_CoinId4.CoinId(coinIdBytes);
12239
12205
  const coinData = TokenCoinData3.create([[coinId, amount]]);
12240
- const addressRef = await UnmaskedPredicateReference4.create(
12206
+ const addressRef = await UnmaskedPredicateReference3.create(
12241
12207
  tokenType,
12242
12208
  signingService.algorithm,
12243
12209
  signingService.publicKey,
@@ -12246,7 +12212,7 @@ var PaymentsModule = class _PaymentsModule {
12246
12212
  const ownerAddress = await addressRef.toAddress();
12247
12213
  const salt = new Uint8Array(32);
12248
12214
  crypto.getRandomValues(salt);
12249
- const mintData = await import_MintTransactionData3.MintTransactionData.create(
12215
+ const mintData = await import_MintTransactionData2.MintTransactionData.create(
12250
12216
  tokenId,
12251
12217
  tokenType,
12252
12218
  null,
@@ -12261,7 +12227,7 @@ var PaymentsModule = class _PaymentsModule {
12261
12227
  null
12262
12228
  // reason: null (genesis, no burn predecessor)
12263
12229
  );
12264
- const commitment = await import_MintCommitment3.MintCommitment.create(mintData);
12230
+ const commitment = await import_MintCommitment2.MintCommitment.create(mintData);
12265
12231
  const MAX_RETRIES = 3;
12266
12232
  let lastStatus;
12267
12233
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
@@ -12278,14 +12244,14 @@ var PaymentsModule = class _PaymentsModule {
12278
12244
  }
12279
12245
  const inclusionProof = await (0, import_InclusionProofUtils5.waitInclusionProof)(trustBase, stClient, commitment);
12280
12246
  const genesisTransaction = commitment.toTransaction(inclusionProof);
12281
- const predicate = await import_UnmaskedPredicate5.UnmaskedPredicate.create(
12247
+ const predicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
12282
12248
  tokenId,
12283
12249
  tokenType,
12284
12250
  signingService,
12285
12251
  import_HashAlgorithm5.HashAlgorithm.SHA256,
12286
12252
  salt
12287
12253
  );
12288
- const tokenState = new import_TokenState5.TokenState(predicate, null);
12254
+ const tokenState = new import_TokenState4.TokenState(predicate, null);
12289
12255
  const sdkToken = await import_Token6.Token.mint(trustBase, tokenState, genesisTransaction);
12290
12256
  const tokenIdHex = tokenId.toJSON();
12291
12257
  const symbol = this.getCoinSymbol(coinIdHex);
@@ -12312,29 +12278,6 @@ var PaymentsModule = class _PaymentsModule {
12312
12278
  return { success: false, error: `Local mint failed: ${msg}` };
12313
12279
  }
12314
12280
  }
12315
- /**
12316
- * Check if a nametag is available for minting
12317
- * @param nametag - The nametag to check (e.g., "alice" or "@alice")
12318
- */
12319
- async isNametagAvailable(nametag) {
12320
- this.ensureInitialized();
12321
- const stClient = this.deps.oracle.getStateTransitionClient?.();
12322
- const trustBase = this.deps.oracle.getTrustBase?.();
12323
- if (!stClient || !trustBase) {
12324
- return false;
12325
- }
12326
- try {
12327
- const signingService = await this.createSigningService();
12328
- const minter = new NametagMinter({
12329
- stateTransitionClient: stClient,
12330
- trustBase,
12331
- signingService
12332
- });
12333
- return await minter.isNametagAvailable(nametag);
12334
- } catch {
12335
- return false;
12336
- }
12337
- }
12338
12281
  // ===========================================================================
12339
12282
  // Public API - Sync & Validate
12340
12283
  // ===========================================================================
@@ -12650,7 +12593,7 @@ var PaymentsModule = class _PaymentsModule {
12650
12593
  const privateKeyBytes = new Uint8Array(
12651
12594
  privateKeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
12652
12595
  );
12653
- return import_SigningService.SigningService.createFromSecret(privateKeyBytes);
12596
+ return import_SigningService2.SigningService.createFromSecret(privateKeyBytes);
12654
12597
  }
12655
12598
  /**
12656
12599
  * Get the wallet's signing public key (used for token ownership predicates).
@@ -12665,14 +12608,14 @@ var PaymentsModule = class _PaymentsModule {
12665
12608
  * Create DirectAddress from a public key using UnmaskedPredicateReference
12666
12609
  */
12667
12610
  async createDirectAddressFromPubkey(pubkeyHex) {
12668
- const { UnmaskedPredicateReference: UnmaskedPredicateReference4 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
12669
- const { TokenType: TokenType6 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
12670
- const UNICITY_TOKEN_TYPE_HEX3 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
12671
- const tokenType = new TokenType6(Buffer.from(UNICITY_TOKEN_TYPE_HEX3, "hex"));
12611
+ const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
12612
+ const { TokenType: TokenType4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
12613
+ const UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
12614
+ const tokenType = new TokenType4(Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex"));
12672
12615
  const pubkeyBytes = new Uint8Array(
12673
12616
  pubkeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
12674
12617
  );
12675
- const addressRef = await UnmaskedPredicateReference4.create(
12618
+ const addressRef = await UnmaskedPredicateReference3.create(
12676
12619
  tokenType,
12677
12620
  "secp256k1",
12678
12621
  pubkeyBytes,
@@ -12684,10 +12627,9 @@ var PaymentsModule = class _PaymentsModule {
12684
12627
  * Resolve recipient to IAddress for L3 transfers.
12685
12628
  * Uses pre-resolved PeerInfo when available to avoid redundant network queries.
12686
12629
  */
12687
- async resolveRecipientAddress(recipient, addressMode = "auto", peerInfo) {
12630
+ async resolveRecipientAddress(recipient, _addressMode = "auto", peerInfo) {
12688
12631
  const { AddressFactory } = await import("@unicitylabs/state-transition-sdk/lib/address/AddressFactory");
12689
- const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
12690
- if (recipient.startsWith("PROXY:") || recipient.startsWith("DIRECT:")) {
12632
+ if (recipient.startsWith("DIRECT:")) {
12691
12633
  return AddressFactory.createAddress(recipient);
12692
12634
  }
12693
12635
  if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {
@@ -12697,28 +12639,19 @@ var PaymentsModule = class _PaymentsModule {
12697
12639
  const info = peerInfo ?? await this.deps?.transport.resolve?.(recipient) ?? null;
12698
12640
  if (!info) {
12699
12641
  throw new SphereError(
12700
- `Recipient "${recipient}" not found. Use @nametag, a valid PROXY:/DIRECT: address, or a 33-byte hex pubkey.`,
12642
+ `Recipient "${recipient}" not found. Use @nametag, a DIRECT: address, or a 33-byte hex pubkey.`,
12701
12643
  "INVALID_RECIPIENT"
12702
12644
  );
12703
12645
  }
12704
12646
  const nametag = recipient.startsWith("@") ? recipient.slice(1) : info.nametag || recipient;
12705
- if (addressMode === "proxy") {
12706
- logger.debug("Payments", `Using PROXY address for "${nametag}" (forced)`);
12707
- return ProxyAddress.fromNameTag(nametag);
12708
- }
12709
- if (addressMode === "direct") {
12710
- if (!info.directAddress) {
12711
- throw new SphereError(`"${nametag}" has no DirectAddress stored. It may be a legacy registration.`, "INVALID_RECIPIENT");
12712
- }
12713
- logger.debug("Payments", `Using DirectAddress for "${nametag}" (forced): ${info.directAddress.slice(0, 30)}...`);
12714
- return AddressFactory.createAddress(info.directAddress);
12715
- }
12716
- if (info.directAddress) {
12717
- logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
12718
- return AddressFactory.createAddress(info.directAddress);
12647
+ if (!info.directAddress) {
12648
+ throw new SphereError(
12649
+ `"${nametag}" has no DirectAddress \u2014 the recipient must publish a key-based identity binding.`,
12650
+ "INVALID_RECIPIENT"
12651
+ );
12719
12652
  }
12720
- logger.debug("Payments", `Using PROXY address for legacy nametag "${nametag}"`);
12721
- return ProxyAddress.fromNameTag(nametag);
12653
+ logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
12654
+ return AddressFactory.createAddress(info.directAddress);
12722
12655
  }
12723
12656
  /**
12724
12657
  * Handle NOSTR-FIRST commitment-only transfer (recipient side)
@@ -12773,14 +12706,14 @@ var PaymentsModule = class _PaymentsModule {
12773
12706
  const addressScheme = recipientAddress.scheme;
12774
12707
  const signingService = await this.createSigningService();
12775
12708
  const transferSalt = transferTx.data.salt;
12776
- const recipientPredicate = await import_UnmaskedPredicate5.UnmaskedPredicate.create(
12709
+ const recipientPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
12777
12710
  sourceToken.id,
12778
12711
  sourceToken.type,
12779
12712
  signingService,
12780
12713
  import_HashAlgorithm5.HashAlgorithm.SHA256,
12781
12714
  transferSalt
12782
12715
  );
12783
- const recipientState = new import_TokenState5.TokenState(recipientPredicate, null);
12716
+ const recipientState = new import_TokenState4.TokenState(recipientPredicate, null);
12784
12717
  let nametagTokens = [];
12785
12718
  if (addressScheme === import_AddressScheme.AddressScheme.PROXY) {
12786
12719
  const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
@@ -12877,6 +12810,67 @@ var PaymentsModule = class _PaymentsModule {
12877
12810
  }
12878
12811
  }
12879
12812
  }
12813
+ /**
12814
+ * v2 engine transfer (sender-driven): the sender handed us a FINISHED token.
12815
+ * Decode the blob, dedup by the genesis-stable token id, store it as a
12816
+ * confirmed token, and emit/record the receipt. No commitment / inclusion-proof
12817
+ * / finalization round-trip (contrast the v1 sourceToken+transferTx path).
12818
+ */
12819
+ async handleV2Transfer(payload, senderPubkey) {
12820
+ this.ensureInitialized();
12821
+ if (!this.loaded && this.loadedPromise) {
12822
+ await this.loadedPromise;
12823
+ }
12824
+ const engine = this.deps.tokenEngine;
12825
+ if (!engine) return;
12826
+ let token;
12827
+ try {
12828
+ token = await engine.decodeToken(decodeTokenBlob(hexToBytes2(payload.tokenBlob)));
12829
+ } catch (err) {
12830
+ logger.error("Payments", "V2 transfer: failed to decode token blob:", err);
12831
+ return;
12832
+ }
12833
+ const id = `v2_${engine.tokenId(token)}`;
12834
+ if (this.tokens.has(id)) {
12835
+ logger.debug("Payments", `V2 transfer ${id.slice(0, 16)}... already present, skipping`);
12836
+ return;
12837
+ }
12838
+ const info = await parseTokenInfo(payload.tokenBlob, engine);
12839
+ const registry = TokenRegistry.getInstance();
12840
+ const uiToken = {
12841
+ id,
12842
+ coinId: info.coinId,
12843
+ symbol: registry.getSymbol(info.coinId) || info.symbol,
12844
+ name: registry.getName(info.coinId) || info.name,
12845
+ decimals: registry.getDecimals(info.coinId) ?? info.decimals,
12846
+ amount: info.amount,
12847
+ status: "confirmed",
12848
+ createdAt: Date.now(),
12849
+ updatedAt: Date.now(),
12850
+ sdkData: payload.tokenBlob
12851
+ };
12852
+ await this.addToken(uiToken);
12853
+ const senderInfo = await this.resolveSenderInfo(senderPubkey);
12854
+ this.deps.emitEvent("transfer:incoming", {
12855
+ id,
12856
+ senderPubkey,
12857
+ senderNametag: senderInfo.senderNametag,
12858
+ tokens: [uiToken],
12859
+ memo: payload.memo,
12860
+ receivedAt: Date.now()
12861
+ });
12862
+ await this.addToHistory({
12863
+ type: "RECEIVED",
12864
+ amount: info.amount,
12865
+ coinId: info.coinId,
12866
+ symbol: uiToken.symbol,
12867
+ timestamp: Date.now(),
12868
+ senderPubkey,
12869
+ ...senderInfo,
12870
+ memo: payload.memo,
12871
+ tokenId: id
12872
+ });
12873
+ }
12880
12874
  async handleIncomingTransfer(transfer) {
12881
12875
  if (!this.loaded && this.loadedPromise) {
12882
12876
  await this.loadedPromise;
@@ -12884,6 +12878,10 @@ var PaymentsModule = class _PaymentsModule {
12884
12878
  try {
12885
12879
  const payload = transfer.payload;
12886
12880
  logger.debug("Payments", "handleIncomingTransfer: keys=", Object.keys(payload).join(","));
12881
+ if (this.deps.tokenEngine && isV2TransferPayload(transfer.payload)) {
12882
+ await this.handleV2Transfer(transfer.payload, transfer.senderTransportPubkey);
12883
+ return;
12884
+ }
12887
12885
  let combinedBundle = null;
12888
12886
  if (isCombinedTransferBundleV6(payload)) {
12889
12887
  combinedBundle = payload;
@@ -12965,7 +12963,7 @@ var PaymentsModule = class _PaymentsModule {
12965
12963
  const hasTransactionData = transferTxInput.transactionData !== void 0;
12966
12964
  const hasAuthenticator = transferTxInput.authenticator !== void 0;
12967
12965
  if (hasData && hasInclusionProof) {
12968
- transferTx = await import_TransferTransaction2.TransferTransaction.fromJSON(transferTxInput);
12966
+ transferTx = await import_TransferTransaction3.TransferTransaction.fromJSON(transferTxInput);
12969
12967
  } else if (hasTransactionData && hasAuthenticator) {
12970
12968
  const commitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferTxInput);
12971
12969
  const stClient = this.deps.oracle.getStateTransitionClient?.();
@@ -12986,7 +12984,7 @@ var PaymentsModule = class _PaymentsModule {
12986
12984
  transferTx = commitment.toTransaction(inclusionProof);
12987
12985
  } else {
12988
12986
  try {
12989
- transferTx = await import_TransferTransaction2.TransferTransaction.fromJSON(transferTxInput);
12987
+ transferTx = await import_TransferTransaction3.TransferTransaction.fromJSON(transferTxInput);
12990
12988
  } catch {
12991
12989
  const commitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferTxInput);
12992
12990
  const stClient = this.deps.oracle.getStateTransitionClient?.();
@@ -13028,7 +13026,7 @@ var PaymentsModule = class _PaymentsModule {
13028
13026
  logger.warn("Payments", "Received invalid token");
13029
13027
  return;
13030
13028
  }
13031
- const tokenInfo = await parseTokenInfo(tokenData);
13029
+ const tokenInfo = await parseTokenInfo(tokenData, this.deps?.tokenEngine);
13032
13030
  const token = {
13033
13031
  id: tokenInfo.tokenId ?? crypto.randomUUID(),
13034
13032
  coinId: tokenInfo.coinId,
@@ -13334,24 +13332,6 @@ function createPaymentsModule(config) {
13334
13332
  return new PaymentsModule(config);
13335
13333
  }
13336
13334
 
13337
- // modules/payments/TokenSplitCalculator.ts
13338
- init_logger();
13339
- var import_Token7 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
13340
- var import_CoinId5 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
13341
-
13342
- // modules/payments/BackgroundCommitmentService.ts
13343
- init_logger();
13344
- init_errors();
13345
-
13346
- // modules/payments/TokenRecoveryService.ts
13347
- init_logger();
13348
- var import_TokenId4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
13349
- var import_TokenState6 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
13350
- var import_TokenType4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
13351
- var import_CoinId6 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
13352
- var import_HashAlgorithm6 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
13353
- var import_UnmaskedPredicate6 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
13354
-
13355
13335
  // modules/communications/CommunicationsModule.ts
13356
13336
  init_logger();
13357
13337
  init_errors();
@@ -14033,7 +14013,7 @@ function createCommunicationsModule(config) {
14033
14013
  }
14034
14014
 
14035
14015
  // modules/groupchat/GroupChatModule.ts
14036
- var import_nostr_js_sdk4 = require("@unicitylabs/nostr-js-sdk");
14016
+ var import_nostr_js_sdk3 = require("@unicitylabs/nostr-js-sdk");
14037
14017
  init_logger();
14038
14018
  init_errors();
14039
14019
  init_constants();
@@ -14051,7 +14031,7 @@ var GroupVisibility = {
14051
14031
 
14052
14032
  // modules/groupchat/GroupChatModule.ts
14053
14033
  function createNip29Filter(data) {
14054
- return new import_nostr_js_sdk4.Filter(data);
14034
+ return new import_nostr_js_sdk3.Filter(data);
14055
14035
  }
14056
14036
  var GroupChatModule = class {
14057
14037
  config;
@@ -14100,7 +14080,7 @@ var GroupChatModule = class {
14100
14080
  }
14101
14081
  this.deps = deps;
14102
14082
  const secretKey = Buffer.from(deps.identity.privateKey, "hex");
14103
- this.keyManager = import_nostr_js_sdk4.NostrKeyManager.fromPrivateKey(secretKey);
14083
+ this.keyManager = import_nostr_js_sdk3.NostrKeyManager.fromPrivateKey(secretKey);
14104
14084
  }
14105
14085
  async load() {
14106
14086
  this.ensureInitialized();
@@ -14241,7 +14221,7 @@ var GroupChatModule = class {
14241
14221
  }
14242
14222
  this.subscriptionIds = [];
14243
14223
  const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
14244
- this.keyManager = import_nostr_js_sdk4.NostrKeyManager.fromPrivateKey(secretKey);
14224
+ this.keyManager = import_nostr_js_sdk3.NostrKeyManager.fromPrivateKey(secretKey);
14245
14225
  if (this.groups.size === 0) {
14246
14226
  await this.restoreJoinedGroups();
14247
14227
  } else {
@@ -14253,13 +14233,13 @@ var GroupChatModule = class {
14253
14233
  this.ensureInitialized();
14254
14234
  if (!this.keyManager) {
14255
14235
  const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
14256
- this.keyManager = import_nostr_js_sdk4.NostrKeyManager.fromPrivateKey(secretKey);
14236
+ this.keyManager = import_nostr_js_sdk3.NostrKeyManager.fromPrivateKey(secretKey);
14257
14237
  }
14258
14238
  const primaryRelay = this.config.relays[0];
14259
14239
  if (primaryRelay) {
14260
14240
  await this.checkAndClearOnRelayChange(primaryRelay);
14261
14241
  }
14262
- this.client = new import_nostr_js_sdk4.NostrClient(this.keyManager);
14242
+ this.client = new import_nostr_js_sdk3.NostrClient(this.keyManager);
14263
14243
  try {
14264
14244
  await this.client.connect(...this.config.relays);
14265
14245
  this.connected = true;
@@ -14546,7 +14526,7 @@ var GroupChatModule = class {
14546
14526
  if (!this.client) return [];
14547
14527
  const groupsMap = /* @__PURE__ */ new Map();
14548
14528
  await this.oneshotSubscription(
14549
- new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_METADATA] }),
14529
+ new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_METADATA] }),
14550
14530
  {
14551
14531
  onEvent: (event) => {
14552
14532
  const group = this.parseGroupMetadata(event);
@@ -15046,7 +15026,7 @@ var GroupChatModule = class {
15046
15026
  if (!this.client) return /* @__PURE__ */ new Set();
15047
15027
  const adminPubkeys = /* @__PURE__ */ new Set();
15048
15028
  return this.oneshotSubscription(
15049
- new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": ["", "_"] }),
15029
+ new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": ["", "_"] }),
15050
15030
  {
15051
15031
  onEvent: (event) => {
15052
15032
  const pTags = event.tags.filter((t) => t[0] === "p");
@@ -15068,7 +15048,7 @@ var GroupChatModule = class {
15068
15048
  if (!this.client) return null;
15069
15049
  let result = null;
15070
15050
  return this.oneshotSubscription(
15071
- new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_METADATA], "#d": [groupId] }),
15051
+ new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_METADATA], "#d": [groupId] }),
15072
15052
  {
15073
15053
  onEvent: (event) => {
15074
15054
  if (!result) result = this.parseGroupMetadata(event);
@@ -15105,7 +15085,7 @@ var GroupChatModule = class {
15105
15085
  if (!this.client) return [];
15106
15086
  const members = [];
15107
15087
  return this.oneshotSubscription(
15108
- new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#d": [groupId] }),
15088
+ new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#d": [groupId] }),
15109
15089
  {
15110
15090
  onEvent: (event) => {
15111
15091
  const pTags = event.tags.filter((t) => t[0] === "p");
@@ -15126,7 +15106,7 @@ var GroupChatModule = class {
15126
15106
  if (!this.client) return [];
15127
15107
  const adminPubkeys = [];
15128
15108
  return this.oneshotSubscription(
15129
- new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": [groupId] }),
15109
+ new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": [groupId] }),
15130
15110
  {
15131
15111
  onEvent: (event) => {
15132
15112
  const pTags = event.tags.filter((t) => t[0] === "p");
@@ -16989,7 +16969,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
16989
16969
  const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : void 0;
16990
16970
  return abytes(bytes, sizer);
16991
16971
  }
16992
- class Signature {
16972
+ class Signature2 {
16993
16973
  r;
16994
16974
  s;
16995
16975
  recovery;
@@ -17009,7 +16989,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
17009
16989
  let recid;
17010
16990
  if (format === "der") {
17011
16991
  const { r: r2, s: s2 } = DER.toSig(abytes(bytes));
17012
- return new Signature(r2, s2);
16992
+ return new Signature2(r2, s2);
17013
16993
  }
17014
16994
  if (format === "recovered") {
17015
16995
  recid = bytes[0];
@@ -17019,7 +16999,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
17019
16999
  const L = lengths.signature / 2;
17020
17000
  const r = bytes.subarray(0, L);
17021
17001
  const s = bytes.subarray(L, L * 2);
17022
- return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
17002
+ return new Signature2(Fn.fromBytes(r), Fn.fromBytes(s), recid);
17023
17003
  }
17024
17004
  static fromHex(hex, format) {
17025
17005
  return this.fromBytes(hexToBytes(hex), format);
@@ -17031,7 +17011,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
17031
17011
  return recovery;
17032
17012
  }
17033
17013
  addRecoveryBit(recovery) {
17034
- return new Signature(this.r, this.s, recovery);
17014
+ return new Signature2(this.r, this.s, recovery);
17035
17015
  }
17036
17016
  recoverPublicKey(messageHash) {
17037
17017
  const { r, s } = this;
@@ -17123,7 +17103,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
17123
17103
  normS = Fn.neg(s);
17124
17104
  recovery ^= 1;
17125
17105
  }
17126
- return new Signature(r, normS, hasLargeCofactor ? void 0 : recovery);
17106
+ return new Signature2(r, normS, hasLargeCofactor ? void 0 : recovery);
17127
17107
  }
17128
17108
  return { seed, k2sig };
17129
17109
  }
@@ -17138,12 +17118,12 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
17138
17118
  publicKey = abytes(publicKey, void 0, "publicKey");
17139
17119
  message = validateMsgAndHash(message, prehash);
17140
17120
  if (!isBytes(signature)) {
17141
- const end = signature instanceof Signature ? ", use sig.toBytes()" : "";
17121
+ const end = signature instanceof Signature2 ? ", use sig.toBytes()" : "";
17142
17122
  throw new Error("verify expects Uint8Array signature" + end);
17143
17123
  }
17144
17124
  validateSigLength(signature, format);
17145
17125
  try {
17146
- const sig = Signature.fromBytes(signature, format);
17126
+ const sig = Signature2.fromBytes(signature, format);
17147
17127
  const P = Point.fromBytes(publicKey);
17148
17128
  if (lowS && sig.hasHighS())
17149
17129
  return false;
@@ -17164,7 +17144,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
17164
17144
  function recoverPublicKey(signature, message, opts = {}) {
17165
17145
  const { prehash } = validateSigOpts(opts, defaultSigOpts);
17166
17146
  message = validateMsgAndHash(message, prehash);
17167
- return Signature.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes();
17147
+ return Signature2.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes();
17168
17148
  }
17169
17149
  return Object.freeze({
17170
17150
  keygen,
@@ -17176,7 +17156,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
17176
17156
  sign,
17177
17157
  verify,
17178
17158
  recoverPublicKey,
17179
- Signature,
17159
+ Signature: Signature2,
17180
17160
  hash
17181
17161
  });
17182
17162
  }
@@ -18448,7 +18428,7 @@ function freezeCoinAsset(coinAsset, state, latestSender) {
18448
18428
  }
18449
18429
 
18450
18430
  // modules/accounting/AccountingModule.ts
18451
- var import_Token8 = require("@unicitylabs/state-transition-sdk/lib/token/Token.js");
18431
+ var import_Token7 = require("@unicitylabs/state-transition-sdk/lib/token/Token.js");
18452
18432
  var LOG_TAG2 = "Accounting";
18453
18433
  var INV_LEDGER_PREFIX = "inv_ledger:";
18454
18434
  var AccountingModule = class _AccountingModule {
@@ -19101,129 +19081,146 @@ var AccountingModule = class _AccountingModule {
19101
19081
  );
19102
19082
  }
19103
19083
  try {
19104
- const { TokenId: TokenId5 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
19105
- const { TokenType: TokenType6 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType.js");
19106
- const { MintTransactionData: MintTransactionData4 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData.js");
19107
- const { MintCommitment: MintCommitment4 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment.js");
19108
- const { SigningService: SigningService3 } = await import("@unicitylabs/state-transition-sdk/lib/sign/SigningService.js");
19109
- const { HashAlgorithm: HashAlgorithm8 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
19110
- const { DataHasher } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
19111
- const { UnmaskedPredicate: UnmaskedPredicate7 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate.js");
19112
- const { UnmaskedPredicateReference: UnmaskedPredicateReference4 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference.js");
19113
- const { TokenState: TokenState7 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenState.js");
19114
- const { Token: SdkToken5 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token.js");
19115
- const { waitInclusionProof: waitInclusionProof6 } = await import("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js");
19116
- const hash = await new DataHasher(HashAlgorithm8.SHA256).update(invoiceBytesEncoded).digest();
19117
- const invoiceTokenId = new TokenId5(hash.imprint);
19118
- const invoiceId = invoiceTokenId.toJSON();
19119
- if (this.invoiceTermsCache.has(invoiceId)) {
19120
- throw new SphereError(
19121
- `Invoice already exists locally: ${invoiceId}`,
19122
- "INVOICE_ALREADY_EXISTS"
19084
+ let invoiceId;
19085
+ let sdkData;
19086
+ const engine = deps.tokenEngine;
19087
+ if (engine) {
19088
+ const invoiceToken = await engine.mintDataToken({
19089
+ recipientPubkey: hexToBytes(deps.identity.chainPubkey),
19090
+ data: invoiceBytesEncoded,
19091
+ tokenType: hexToBytes(INVOICE_TOKEN_TYPE_HEX),
19092
+ salt
19093
+ });
19094
+ invoiceId = engine.tokenId(invoiceToken);
19095
+ if (this.invoiceTermsCache.has(invoiceId)) {
19096
+ throw new SphereError(`Invoice already exists locally: ${invoiceId}`, "INVOICE_ALREADY_EXISTS");
19097
+ }
19098
+ sdkData = bytesToHex(encodeTokenBlob(engine.encodeToken(invoiceToken)));
19099
+ } else {
19100
+ const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
19101
+ const { TokenType: TokenType4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType.js");
19102
+ const { MintTransactionData: MintTransactionData3 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData.js");
19103
+ const { MintCommitment: MintCommitment3 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment.js");
19104
+ const { SigningService: SigningService3 } = await import("@unicitylabs/state-transition-sdk/lib/sign/SigningService.js");
19105
+ const { HashAlgorithm: HashAlgorithm6 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
19106
+ const { DataHasher: DataHasher2 } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
19107
+ const { UnmaskedPredicate: UnmaskedPredicate5 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate.js");
19108
+ const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference.js");
19109
+ const { TokenState: TokenState5 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenState.js");
19110
+ const { Token: SdkToken4 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token.js");
19111
+ const { waitInclusionProof: waitInclusionProof6 } = await import("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js");
19112
+ const hash = await new DataHasher2(HashAlgorithm6.SHA256).update(invoiceBytesEncoded).digest();
19113
+ const invoiceTokenId = new TokenId4(hash.imprint);
19114
+ invoiceId = invoiceTokenId.toJSON();
19115
+ if (this.invoiceTermsCache.has(invoiceId)) {
19116
+ throw new SphereError(
19117
+ `Invoice already exists locally: ${invoiceId}`,
19118
+ "INVOICE_ALREADY_EXISTS"
19119
+ );
19120
+ }
19121
+ const invoiceTokenType = new TokenType4(
19122
+ Buffer.from(INVOICE_TOKEN_TYPE_HEX, "hex")
19123
19123
  );
19124
- }
19125
- const invoiceTokenType = new TokenType6(
19126
- Buffer.from(INVOICE_TOKEN_TYPE_HEX, "hex")
19127
- );
19128
- const signingService = await SigningService3.createFromSecret(signingKeyBytes);
19129
- const addressRef = await UnmaskedPredicateReference4.create(
19130
- invoiceTokenType,
19131
- signingService.algorithm,
19132
- signingService.publicKey,
19133
- HashAlgorithm8.SHA256
19134
- );
19135
- const ownerAddress = await addressRef.toAddress();
19136
- const mintData = await MintTransactionData4.create(
19137
- invoiceTokenId,
19138
- invoiceTokenType,
19139
- invoiceBytesEncoded,
19140
- // tokenData: serialized InvoiceTerms (UTF-8 JSON)
19141
- null,
19142
- // coinData: null (non-fungible invoice token)
19143
- ownerAddress,
19144
- salt,
19145
- null,
19146
- // recipientDataHash: null
19147
- null
19148
- // reason: null
19149
- );
19150
- if (this.config.debug) {
19151
- logger.debug(LOG_TAG2, `Created MintTransactionData for invoice ${invoiceId}`);
19152
- }
19153
- const commitment = await MintCommitment4.create(mintData);
19154
- if (this.config.debug) {
19155
- logger.debug(LOG_TAG2, "Created MintCommitment for invoice");
19156
- }
19157
- const MAX_RETRIES = 3;
19158
- let submitSuccess = false;
19159
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
19160
- try {
19161
- if (this.config.debug) {
19162
- logger.debug(
19163
- LOG_TAG2,
19164
- `Submitting invoice commitment (attempt ${attempt}/${MAX_RETRIES})...`
19165
- );
19166
- }
19167
- const response = await stClient.submitMintCommitment(commitment);
19168
- if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
19124
+ const signingService = await SigningService3.createFromSecret(signingKeyBytes);
19125
+ const addressRef = await UnmaskedPredicateReference3.create(
19126
+ invoiceTokenType,
19127
+ signingService.algorithm,
19128
+ signingService.publicKey,
19129
+ HashAlgorithm6.SHA256
19130
+ );
19131
+ const ownerAddress = await addressRef.toAddress();
19132
+ const mintData = await MintTransactionData3.create(
19133
+ invoiceTokenId,
19134
+ invoiceTokenType,
19135
+ invoiceBytesEncoded,
19136
+ // tokenData: serialized InvoiceTerms (UTF-8 JSON)
19137
+ null,
19138
+ // coinData: null (non-fungible invoice token)
19139
+ ownerAddress,
19140
+ salt,
19141
+ null,
19142
+ // recipientDataHash: null
19143
+ null
19144
+ // reason: null
19145
+ );
19146
+ if (this.config.debug) {
19147
+ logger.debug(LOG_TAG2, `Created MintTransactionData for invoice ${invoiceId}`);
19148
+ }
19149
+ const commitment = await MintCommitment3.create(mintData);
19150
+ if (this.config.debug) {
19151
+ logger.debug(LOG_TAG2, "Created MintCommitment for invoice");
19152
+ }
19153
+ const MAX_RETRIES = 3;
19154
+ let submitSuccess = false;
19155
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
19156
+ try {
19169
19157
  if (this.config.debug) {
19170
19158
  logger.debug(
19171
19159
  LOG_TAG2,
19172
- response.status === "REQUEST_ID_EXISTS" ? "Invoice commitment already exists (idempotent re-mint)" : "Invoice commitment submitted successfully"
19160
+ `Submitting invoice commitment (attempt ${attempt}/${MAX_RETRIES})...`
19173
19161
  );
19174
19162
  }
19175
- submitSuccess = true;
19176
- break;
19177
- } else {
19178
- logger.warn(LOG_TAG2, `Invoice commitment submission failed: ${response.status}`);
19163
+ const response = await stClient.submitMintCommitment(commitment);
19164
+ if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
19165
+ if (this.config.debug) {
19166
+ logger.debug(
19167
+ LOG_TAG2,
19168
+ response.status === "REQUEST_ID_EXISTS" ? "Invoice commitment already exists (idempotent re-mint)" : "Invoice commitment submitted successfully"
19169
+ );
19170
+ }
19171
+ submitSuccess = true;
19172
+ break;
19173
+ } else {
19174
+ logger.warn(LOG_TAG2, `Invoice commitment submission failed: ${response.status}`);
19175
+ if (attempt === MAX_RETRIES) {
19176
+ throw new SphereError(
19177
+ `Failed to mint invoice token: commitment rejected after ${MAX_RETRIES} attempts: ${response.status}`,
19178
+ "INVOICE_MINT_FAILED"
19179
+ );
19180
+ }
19181
+ await new Promise((r) => setTimeout(r, 1e3 * attempt));
19182
+ }
19183
+ } catch (retryErr) {
19184
+ if (retryErr instanceof SphereError && (retryErr.code === "INVOICE_ORACLE_REQUIRED" || retryErr.code === "INVOICE_INVALID_PROOF" || retryErr.code === "INVOICE_MINT_FAILED" || retryErr.code === "NOT_INITIALIZED" || retryErr.code === "MODULE_DESTROYED")) throw retryErr;
19185
+ logger.warn(LOG_TAG2, `Invoice commitment attempt ${attempt} error:`, retryErr);
19179
19186
  if (attempt === MAX_RETRIES) {
19180
19187
  throw new SphereError(
19181
- `Failed to mint invoice token: commitment rejected after ${MAX_RETRIES} attempts: ${response.status}`,
19182
- "INVOICE_MINT_FAILED"
19188
+ `Failed to mint invoice token: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
19189
+ "INVOICE_MINT_FAILED",
19190
+ retryErr
19183
19191
  );
19184
19192
  }
19185
19193
  await new Promise((r) => setTimeout(r, 1e3 * attempt));
19186
19194
  }
19187
- } catch (retryErr) {
19188
- if (retryErr instanceof SphereError && (retryErr.code === "INVOICE_ORACLE_REQUIRED" || retryErr.code === "INVOICE_INVALID_PROOF" || retryErr.code === "INVOICE_MINT_FAILED" || retryErr.code === "NOT_INITIALIZED" || retryErr.code === "MODULE_DESTROYED")) throw retryErr;
19189
- logger.warn(LOG_TAG2, `Invoice commitment attempt ${attempt} error:`, retryErr);
19190
- if (attempt === MAX_RETRIES) {
19191
- throw new SphereError(
19192
- `Failed to mint invoice token: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
19193
- "INVOICE_MINT_FAILED",
19194
- retryErr
19195
- );
19196
- }
19197
- await new Promise((r) => setTimeout(r, 1e3 * attempt));
19198
19195
  }
19199
- }
19200
- if (!submitSuccess) {
19201
- throw new SphereError(
19202
- "Failed to mint invoice token: commitment submission failed after retries",
19203
- "INVOICE_MINT_FAILED"
19196
+ if (!submitSuccess) {
19197
+ throw new SphereError(
19198
+ "Failed to mint invoice token: commitment submission failed after retries",
19199
+ "INVOICE_MINT_FAILED"
19200
+ );
19201
+ }
19202
+ if (this.config.debug) {
19203
+ logger.debug(LOG_TAG2, "Waiting for invoice inclusion proof...");
19204
+ }
19205
+ const inclusionProof = await waitInclusionProof6(trustBase, stClient, commitment);
19206
+ if (this.config.debug) {
19207
+ logger.debug(LOG_TAG2, "Invoice inclusion proof received");
19208
+ }
19209
+ const genesisTransaction = commitment.toTransaction(inclusionProof);
19210
+ const invoicePredicate = await UnmaskedPredicate5.create(
19211
+ invoiceTokenId,
19212
+ invoiceTokenType,
19213
+ signingService,
19214
+ HashAlgorithm6.SHA256,
19215
+ salt
19204
19216
  );
19217
+ const tokenState = new TokenState5(invoicePredicate, null);
19218
+ const sdkToken = await SdkToken4.mint(trustBase, tokenState, genesisTransaction);
19219
+ if (this.config.debug) {
19220
+ logger.debug(LOG_TAG2, "Invoice token minted successfully");
19221
+ }
19222
+ sdkData = JSON.stringify(sdkToken.toJSON());
19205
19223
  }
19206
- if (this.config.debug) {
19207
- logger.debug(LOG_TAG2, "Waiting for invoice inclusion proof...");
19208
- }
19209
- const inclusionProof = await waitInclusionProof6(trustBase, stClient, commitment);
19210
- if (this.config.debug) {
19211
- logger.debug(LOG_TAG2, "Invoice inclusion proof received");
19212
- }
19213
- const genesisTransaction = commitment.toTransaction(inclusionProof);
19214
- const invoicePredicate = await UnmaskedPredicate7.create(
19215
- invoiceTokenId,
19216
- invoiceTokenType,
19217
- signingService,
19218
- HashAlgorithm8.SHA256,
19219
- salt
19220
- );
19221
- const tokenState = new TokenState7(invoicePredicate, null);
19222
- const sdkToken = await SdkToken5.mint(trustBase, tokenState, genesisTransaction);
19223
- if (this.config.debug) {
19224
- logger.debug(LOG_TAG2, "Invoice token minted successfully");
19225
- }
19226
- const sdkTokenJson = sdkToken.toJSON();
19227
19224
  const uiToken = {
19228
19225
  id: invoiceId,
19229
19226
  coinId: INVOICE_TOKEN_TYPE_HEX,
@@ -19234,7 +19231,7 @@ var AccountingModule = class _AccountingModule {
19234
19231
  status: "confirmed",
19235
19232
  createdAt: terms.createdAt,
19236
19233
  updatedAt: terms.createdAt,
19237
- sdkData: JSON.stringify(sdkTokenJson)
19234
+ sdkData
19238
19235
  };
19239
19236
  await deps.payments.addToken(uiToken);
19240
19237
  this.invoiceTermsCache.set(invoiceId, this._normalizeInvoiceTerms(terms));
@@ -19245,17 +19242,7 @@ var AccountingModule = class _AccountingModule {
19245
19242
  const allTokens = deps.payments.getTokens();
19246
19243
  let anyScanDirty = false;
19247
19244
  for (const token of allTokens) {
19248
- if (!token.sdkData) continue;
19249
- let txf;
19250
- try {
19251
- txf = JSON.parse(token.sdkData);
19252
- } catch {
19253
- continue;
19254
- }
19255
- const txCount = txf.transactions?.length ?? 0;
19256
- if (txCount === 0) continue;
19257
- this._processTokenTransactions(token.id, txf, 0);
19258
- anyScanDirty = true;
19245
+ if (await this._scanTokenForAttribution(token, 0)) anyScanDirty = true;
19259
19246
  }
19260
19247
  const archivedTokensForScan = deps.payments.getArchivedTokens();
19261
19248
  for (const [archivedId, txf] of archivedTokensForScan) {
@@ -19271,7 +19258,7 @@ var AccountingModule = class _AccountingModule {
19271
19258
  if (this.config.debug) {
19272
19259
  logger.debug(LOG_TAG2, `Invoice created and stored: ${invoiceId}`);
19273
19260
  }
19274
- const txfToken = sdkTokenJson;
19261
+ const txfToken = engine ? sdkData : JSON.parse(sdkData);
19275
19262
  return {
19276
19263
  success: true,
19277
19264
  invoiceId,
@@ -19312,36 +19299,60 @@ var AccountingModule = class _AccountingModule {
19312
19299
  this.ensureNotDestroyed();
19313
19300
  this.ensureInitialized();
19314
19301
  const deps = this.deps;
19315
- const tokenType = token.genesis?.data?.tokenType;
19316
- if (tokenType !== INVOICE_TOKEN_TYPE_HEX) {
19317
- throw new SphereError(
19318
- `Invoice import failed: token type "${tokenType}" is not the expected invoice type.`,
19319
- "INVOICE_WRONG_TOKEN_TYPE"
19320
- );
19321
- }
19322
- const tokenData = token.genesis?.data?.tokenData;
19323
- if (!tokenData || typeof tokenData !== "string") {
19324
- throw new SphereError(
19325
- "Invoice import failed: missing or invalid tokenData field.",
19326
- "INVOICE_INVALID_DATA"
19327
- );
19328
- }
19329
- let jsonString = tokenData;
19330
- if (!/^\s*[\[{"]/.test(tokenData)) {
19302
+ let terms;
19303
+ let tokenId;
19304
+ let sdkDataForStore;
19305
+ const engine = deps.tokenEngine;
19306
+ const isV2 = !!engine && typeof token === "string";
19307
+ if (isV2) {
19308
+ const sphereToken = await engine.decodeToken(decodeTokenBlob(hexToBytes(token)));
19309
+ const verifyResult = await engine.verify(sphereToken);
19310
+ if (!verifyResult.ok) {
19311
+ throw new SphereError("Invoice import failed: inclusion proof is invalid.", "INVOICE_INVALID_PROOF");
19312
+ }
19313
+ tokenId = engine.tokenId(sphereToken);
19314
+ const data = engine.readTokenData(sphereToken);
19315
+ if (!data) {
19316
+ throw new SphereError("Invoice import failed: missing or invalid tokenData field.", "INVOICE_INVALID_DATA");
19317
+ }
19331
19318
  try {
19332
- const bytes = hexToBytes(tokenData);
19333
- jsonString = new TextDecoder().decode(bytes);
19319
+ terms = JSON.parse(new TextDecoder().decode(data));
19334
19320
  } catch {
19321
+ throw new SphereError("Invoice import failed: tokenData is not valid JSON.", "INVOICE_INVALID_DATA");
19335
19322
  }
19336
- }
19337
- let terms;
19338
- try {
19339
- terms = JSON.parse(jsonString);
19340
- } catch {
19341
- throw new SphereError(
19342
- "Invoice import failed: tokenData is not valid JSON.",
19343
- "INVOICE_INVALID_DATA"
19344
- );
19323
+ sdkDataForStore = token;
19324
+ } else {
19325
+ const tokenType = token.genesis?.data?.tokenType;
19326
+ if (tokenType !== INVOICE_TOKEN_TYPE_HEX) {
19327
+ throw new SphereError(
19328
+ `Invoice import failed: token type "${tokenType}" is not the expected invoice type.`,
19329
+ "INVOICE_WRONG_TOKEN_TYPE"
19330
+ );
19331
+ }
19332
+ const tokenData = token.genesis?.data?.tokenData;
19333
+ if (!tokenData || typeof tokenData !== "string") {
19334
+ throw new SphereError(
19335
+ "Invoice import failed: missing or invalid tokenData field.",
19336
+ "INVOICE_INVALID_DATA"
19337
+ );
19338
+ }
19339
+ let jsonString = tokenData;
19340
+ if (!/^\s*[[{"]/.test(tokenData)) {
19341
+ try {
19342
+ const bytes = hexToBytes(tokenData);
19343
+ jsonString = new TextDecoder().decode(bytes);
19344
+ } catch {
19345
+ }
19346
+ }
19347
+ try {
19348
+ terms = JSON.parse(jsonString);
19349
+ } catch {
19350
+ throw new SphereError(
19351
+ "Invoice import failed: tokenData is not valid JSON.",
19352
+ "INVOICE_INVALID_DATA"
19353
+ );
19354
+ }
19355
+ sdkDataForStore = JSON.stringify(token);
19345
19356
  }
19346
19357
  if (!terms || typeof terms !== "object") {
19347
19358
  throw new SphereError(
@@ -19428,7 +19439,7 @@ var AccountingModule = class _AccountingModule {
19428
19439
  }
19429
19440
  }
19430
19441
  }
19431
- const tokenId = token.genesis?.data?.tokenId;
19442
+ if (!isV2) tokenId = token.genesis?.data?.tokenId;
19432
19443
  if (!tokenId || typeof tokenId !== "string") {
19433
19444
  throw new SphereError(
19434
19445
  "Invoice import failed: missing tokenId in genesis data.",
@@ -19441,13 +19452,13 @@ var AccountingModule = class _AccountingModule {
19441
19452
  "INVOICE_ALREADY_EXISTS"
19442
19453
  );
19443
19454
  }
19444
- {
19445
- const { DataHasher } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
19446
- const { HashAlgorithm: HashAlgorithm8 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
19447
- const { TokenId: TokenId5 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
19455
+ if (!isV2) {
19456
+ const { DataHasher: DataHasher2 } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
19457
+ const { HashAlgorithm: HashAlgorithm6 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
19458
+ const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
19448
19459
  const reSerializedBytes = new TextEncoder().encode(canonicalSerialize(terms));
19449
- const hash = await new DataHasher(HashAlgorithm8.SHA256).update(reSerializedBytes).digest();
19450
- const reTokenId = new TokenId5(hash.imprint).toJSON();
19460
+ const hash = await new DataHasher2(HashAlgorithm6.SHA256).update(reSerializedBytes).digest();
19461
+ const reTokenId = new TokenId4(hash.imprint).toJSON();
19451
19462
  if (reTokenId !== tokenId) {
19452
19463
  throw new SphereError(
19453
19464
  "Invoice import failed: parsed terms do not match on-chain token ID (canonical hash mismatch).",
@@ -19455,35 +19466,37 @@ var AccountingModule = class _AccountingModule {
19455
19466
  );
19456
19467
  }
19457
19468
  }
19458
- if (!deps.trustBase || deps.trustBase instanceof Uint8Array && deps.trustBase.length === 0) {
19459
- throw new SphereError(
19460
- "Trust base unavailable \u2014 cannot verify invoice proof. Ensure oracle supports getTrustBase().",
19461
- "INVOICE_INVALID_PROOF"
19462
- );
19463
- }
19464
- try {
19465
- const sdkToken = await import_Token8.Token.fromJSON(token);
19466
- const verifyResult = await sdkToken.verify(deps.trustBase);
19467
- const verifyOk = verifyResult.isSuccessful === true;
19468
- if (!verifyOk) {
19469
+ if (!isV2) {
19470
+ if (!deps.trustBase || deps.trustBase instanceof Uint8Array && deps.trustBase.length === 0) {
19469
19471
  throw new SphereError(
19470
- "Invoice import failed: inclusion proof is invalid.",
19472
+ "Trust base unavailable \u2014 cannot verify invoice proof. Ensure oracle supports getTrustBase().",
19471
19473
  "INVOICE_INVALID_PROOF"
19472
19474
  );
19473
19475
  }
19474
- const canonicalTokenId = sdkToken.id?.toJSON?.() ?? null;
19475
- if (!canonicalTokenId || canonicalTokenId !== tokenId) {
19476
+ try {
19477
+ const sdkToken = await import_Token7.Token.fromJSON(token);
19478
+ const verifyResult = await sdkToken.verify(deps.trustBase);
19479
+ const verifyOk = verifyResult.isSuccessful === true;
19480
+ if (!verifyOk) {
19481
+ throw new SphereError(
19482
+ "Invoice import failed: inclusion proof is invalid.",
19483
+ "INVOICE_INVALID_PROOF"
19484
+ );
19485
+ }
19486
+ const canonicalTokenId = sdkToken.id?.toJSON?.() ?? null;
19487
+ if (!canonicalTokenId || canonicalTokenId !== tokenId) {
19488
+ throw new SphereError(
19489
+ `Invoice import failed: tokenId mismatch or unverifiable \u2014 JSON claims ${tokenId}, cryptographic identity is ${canonicalTokenId ?? "unknown"}`,
19490
+ "INVOICE_INVALID_DATA"
19491
+ );
19492
+ }
19493
+ } catch (err) {
19494
+ if (err instanceof SphereError) throw err;
19476
19495
  throw new SphereError(
19477
- `Invoice import failed: tokenId mismatch or unverifiable \u2014 JSON claims ${tokenId}, cryptographic identity is ${canonicalTokenId ?? "unknown"}`,
19478
- "INVOICE_INVALID_DATA"
19496
+ `Invoice import failed: proof verification error \u2014 ${err instanceof Error ? err.message : String(err)}`,
19497
+ "INVOICE_INVALID_PROOF"
19479
19498
  );
19480
19499
  }
19481
- } catch (err) {
19482
- if (err instanceof SphereError) throw err;
19483
- throw new SphereError(
19484
- `Invoice import failed: proof verification error \u2014 ${err instanceof Error ? err.message : String(err)}`,
19485
- "INVOICE_INVALID_PROOF"
19486
- );
19487
19500
  }
19488
19501
  try {
19489
19502
  const uiToken = {
@@ -19496,7 +19509,7 @@ var AccountingModule = class _AccountingModule {
19496
19509
  status: "confirmed",
19497
19510
  createdAt: terms.createdAt,
19498
19511
  updatedAt: terms.createdAt,
19499
- sdkData: JSON.stringify(token)
19512
+ sdkData: sdkDataForStore
19500
19513
  };
19501
19514
  await deps.payments.addToken(uiToken);
19502
19515
  } catch (err) {
@@ -19551,17 +19564,8 @@ var AccountingModule = class _AccountingModule {
19551
19564
  const allTokens = deps.payments.getTokens();
19552
19565
  let anyDirty = false;
19553
19566
  for (const existingToken of allTokens) {
19554
- if (!existingToken.sdkData) continue;
19555
- let txf;
19556
- try {
19557
- txf = JSON.parse(existingToken.sdkData);
19558
- } catch {
19559
- continue;
19560
- }
19561
- const transactions = txf.transactions ?? [];
19562
19567
  const startIndex = this.tokenScanState.get(existingToken.id) ?? 0;
19563
- if (transactions.length > startIndex) {
19564
- this._processTokenTransactions(existingToken.id, txf, startIndex);
19568
+ if (await this._scanTokenForAttribution(existingToken, startIndex)) {
19565
19569
  anyDirty = true;
19566
19570
  }
19567
19571
  }
@@ -21451,17 +21455,8 @@ var AccountingModule = class _AccountingModule {
21451
21455
  const allTokens = deps.payments.getTokens();
21452
21456
  let anyDirty = false;
21453
21457
  for (const token of allTokens) {
21454
- if (!token.sdkData) continue;
21455
- let txf;
21456
- try {
21457
- txf = JSON.parse(token.sdkData);
21458
- } catch {
21459
- continue;
21460
- }
21461
- const transactions = txf.transactions ?? [];
21462
21458
  const startIndex = this.tokenScanState.get(token.id) ?? 0;
21463
- if (transactions.length > startIndex) {
21464
- this._processTokenTransactions(token.id, txf, startIndex);
21459
+ if (await this._scanTokenForAttribution(token, startIndex)) {
21465
21460
  anyDirty = true;
21466
21461
  }
21467
21462
  }
@@ -21516,6 +21511,53 @@ var AccountingModule = class _AccountingModule {
21516
21511
  * @param txf - Parsed TxfToken.
21517
21512
  * @param startIndex - First unprocessed transaction index.
21518
21513
  */
21514
+ /**
21515
+ * Attribute one payment token's invoice memo(s) to the ledger.
21516
+ *
21517
+ * v2 (engine blob): the token carries a single on-chain memo. We decode it and
21518
+ * shim the token into a v1-shaped `txf` (coinData ← engine.readValue, the memo
21519
+ * ← engine.readMemo) so the battle-hardened `_processTokenTransactions` runs
21520
+ * UNCHANGED — same dedup, direction, provisional/synthetic/orphan handling.
21521
+ * v1 (TXF JSON): parse and scan transactions directly.
21522
+ *
21523
+ * `startIndex` is the per-token watermark (0 for a full retroactive scan).
21524
+ * Returns true when the token was scanned. Async because engine.decodeToken is.
21525
+ */
21526
+ async _scanTokenForAttribution(token, startIndex) {
21527
+ if (!token.sdkData) return false;
21528
+ const engine = this.deps?.tokenEngine;
21529
+ const isBlob = token.sdkData.length >= 2 && token.sdkData.length % 2 === 0 && token.sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(token.sdkData);
21530
+ if (engine && isBlob) {
21531
+ let syntheticTxf;
21532
+ try {
21533
+ const sphereToken = await engine.decodeToken(decodeTokenBlob(hexToBytes(token.sdkData)));
21534
+ const memo = engine.readMemo(sphereToken);
21535
+ if (!memo) return false;
21536
+ const coinData = (engine.readValue(sphereToken)?.assets ?? []).map(
21537
+ (a) => [a.coinId, a.amount.toString()]
21538
+ );
21539
+ syntheticTxf = {
21540
+ genesis: { data: { coinData } },
21541
+ transactions: [{ data: { message: bytesToHex(memo) }, inclusionProof: {} }]
21542
+ };
21543
+ } catch {
21544
+ return false;
21545
+ }
21546
+ this._processTokenTransactions(token.id, syntheticTxf, startIndex);
21547
+ return true;
21548
+ }
21549
+ let txf;
21550
+ try {
21551
+ txf = JSON.parse(token.sdkData);
21552
+ } catch {
21553
+ return false;
21554
+ }
21555
+ if ((txf.transactions?.length ?? 0) > startIndex) {
21556
+ this._processTokenTransactions(token.id, txf, startIndex);
21557
+ return true;
21558
+ }
21559
+ return false;
21560
+ }
21519
21561
  _processTokenTransactions(tokenId, txf, startIndex) {
21520
21562
  const transactions = txf.transactions ?? [];
21521
21563
  let lastSuccessIdx = startIndex;
@@ -21795,17 +21837,7 @@ var AccountingModule = class _AccountingModule {
21795
21837
  async _handleIncomingTransfer(transfer) {
21796
21838
  if (this.destroyed) return;
21797
21839
  for (const token of transfer.tokens) {
21798
- if (!token.sdkData) continue;
21799
- let txf;
21800
- try {
21801
- txf = JSON.parse(token.sdkData);
21802
- } catch {
21803
- continue;
21804
- }
21805
- const startIndex = this.tokenScanState.get(token.id) ?? 0;
21806
- if ((txf.transactions?.length ?? 0) > startIndex) {
21807
- this._processTokenTransactions(token.id, txf, startIndex);
21808
- }
21840
+ await this._scanTokenForAttribution(token, this.tokenScanState.get(token.id) ?? 0);
21809
21841
  }
21810
21842
  if (this.destroyed) return;
21811
21843
  await this._flushDirtyLedgerEntries();
@@ -21943,16 +21975,7 @@ var AccountingModule = class _AccountingModule {
21943
21975
  if (this.destroyed) return;
21944
21976
  for (const token of result.tokens) {
21945
21977
  if (!token.sdkData) continue;
21946
- let txf;
21947
- try {
21948
- txf = JSON.parse(token.sdkData);
21949
- } catch {
21950
- continue;
21951
- }
21952
- const startIndex = this.tokenScanState.get(token.id) ?? 0;
21953
- if ((txf.transactions?.length ?? 0) > startIndex) {
21954
- this._processTokenTransactions(token.id, txf, startIndex);
21955
- }
21978
+ await this._scanTokenForAttribution(token, this.tokenScanState.get(token.id) ?? 0);
21956
21979
  const relatedInvoices = this.tokenInvoiceMap.get(token.id);
21957
21980
  if (relatedInvoices) {
21958
21981
  for (const invoiceId of relatedInvoices) {
@@ -22023,15 +22046,8 @@ var AccountingModule = class _AccountingModule {
22023
22046
  const tokens = this.deps.payments.getTokens();
22024
22047
  const token = tokens.find((t) => t.id === tokenId);
22025
22048
  if (!token?.sdkData) return;
22026
- let txf;
22027
- try {
22028
- txf = JSON.parse(token.sdkData);
22029
- } catch {
22030
- return;
22031
- }
22032
22049
  const startIndex = this.tokenScanState.get(tokenId) ?? 0;
22033
- if ((txf.transactions?.length ?? 0) > startIndex) {
22034
- this._processTokenTransactions(tokenId, txf, startIndex);
22050
+ if (await this._scanTokenForAttribution(token, startIndex)) {
22035
22051
  if (this.destroyed) return;
22036
22052
  await this._flushDirtyLedgerEntries();
22037
22053
  }
@@ -27469,29 +27485,421 @@ async function parseAndDecryptWalletDat(data, password, onProgress) {
27469
27485
  };
27470
27486
  }
27471
27487
 
27488
+ // token-engine/identity.ts
27489
+ var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
27490
+ var SIGNING_ALGORITHM = "secp256k1";
27491
+ var EMBEDDED_PREDICATE_UNMASKED = 0;
27492
+ var HASH_ALGORITHM_SHA256 = 0n;
27493
+ var SHA256_IMPRINT_PREFIX = new Uint8Array([0, 0]);
27494
+ function hexToBytes4(hex) {
27495
+ const bytes = new Uint8Array(hex.length / 2);
27496
+ for (let i = 0; i < bytes.length; i++) {
27497
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
27498
+ }
27499
+ return bytes;
27500
+ }
27501
+ function sha2565(data) {
27502
+ return new import_DataHasher.DataHasher(import_HashAlgorithm2.HashAlgorithm.SHA256).update(data).digest().then((h) => h.data);
27503
+ }
27504
+ async function deriveDirectAddress(publicKey) {
27505
+ const tokenTypeCbor = import_CborSerializer.CborSerializer.encodeByteString(hexToBytes4(UNICITY_TOKEN_TYPE_HEX));
27506
+ const reference = import_CborSerializer.CborSerializer.encodeArray(
27507
+ import_CborSerializer.CborSerializer.encodeByteString(new Uint8Array([EMBEDDED_PREDICATE_UNMASKED])),
27508
+ import_CborSerializer.CborSerializer.encodeByteString(tokenTypeCbor),
27509
+ import_CborSerializer.CborSerializer.encodeTextString(SIGNING_ALGORITHM),
27510
+ import_CborSerializer.CborSerializer.encodeUnsignedInteger(HASH_ALGORITHM_SHA256),
27511
+ import_CborSerializer.CborSerializer.encodeByteString(publicKey)
27512
+ );
27513
+ const refHash = await sha2565(reference);
27514
+ const imprint = new Uint8Array([...SHA256_IMPRINT_PREFIX, ...refHash]);
27515
+ const checksum = (await sha2565(imprint)).slice(0, 4);
27516
+ return `DIRECT://${import_HexConverter.HexConverter.encode(imprint)}${import_HexConverter.HexConverter.encode(checksum)}`;
27517
+ }
27518
+
27519
+ // token-engine/factory.ts
27520
+ init_errors();
27521
+
27522
+ // token-engine/SpherePaymentData.ts
27523
+ init_errors();
27524
+ var COIN_ID_PATTERN = /^([0-9a-f]{2})+$/;
27525
+ function assertAsset(coinId, amount) {
27526
+ if (!COIN_ID_PATTERN.test(coinId)) {
27527
+ throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
27528
+ }
27529
+ if (amount < 0n) {
27530
+ throw new SphereError(`Asset amount must be non-negative: ${amount.toString()}`, "VALIDATION_ERROR");
27531
+ }
27532
+ }
27533
+ function sphereAssetToSdk(coinId, amount) {
27534
+ assertAsset(coinId, amount);
27535
+ return new import_Asset.Asset(new import_AssetId.AssetId(import_HexConverter.HexConverter.decode(coinId)), amount);
27536
+ }
27537
+ var SpherePaymentData = class _SpherePaymentData {
27538
+ constructor(assets, _memo = null) {
27539
+ this.assets = assets;
27540
+ this._memo = _memo;
27541
+ }
27542
+ /** Sphere-private CBOR tag (verified free in the v2 SDK tag space). */
27543
+ static CBOR_TAG = 39050n;
27544
+ /** Envelope version; bump when the structure changes. */
27545
+ static VERSION = 1n;
27546
+ /** Opaque, app-defined memo carried alongside the value (e.g. invoice attribution). */
27547
+ get memo() {
27548
+ return this._memo ? new Uint8Array(this._memo) : null;
27549
+ }
27550
+ /** Wrap an existing SDK asset collection (+ optional opaque memo). */
27551
+ static create(assets, memo = null) {
27552
+ return new _SpherePaymentData(assets, memo);
27553
+ }
27554
+ /** Build from a sphere-domain value (hex coin id → bigint amount) + optional opaque memo. */
27555
+ static fromValue(value, memo = null) {
27556
+ const assets = value.assets.map((a) => sphereAssetToSdk(a.coinId, a.amount));
27557
+ return new _SpherePaymentData(import_PaymentAssetCollection.PaymentAssetCollection.create(...assets), memo);
27558
+ }
27559
+ /** Decode from the CBOR envelope produced by {@link encode}. */
27560
+ static fromCBOR(bytes) {
27561
+ const tag = import_CborDeserializer.CborDeserializer.decodeTag(bytes);
27562
+ if (tag.tag !== _SpherePaymentData.CBOR_TAG) {
27563
+ throw new import_CborError.CborError(`Invalid SpherePaymentData tag: ${tag.tag}`);
27564
+ }
27565
+ const fields = import_CborDeserializer.CborDeserializer.decodeArray(tag.data, 3);
27566
+ const version = import_CborDeserializer.CborDeserializer.decodeUnsignedInteger(fields[0]);
27567
+ if (version !== _SpherePaymentData.VERSION) {
27568
+ throw new import_CborError.CborError(`Unsupported SpherePaymentData version: ${version}`);
27569
+ }
27570
+ const memo = import_CborDeserializer.CborDeserializer.decodeNullable(fields[2], import_CborDeserializer.CborDeserializer.decodeByteString);
27571
+ return new _SpherePaymentData(import_PaymentAssetCollection.PaymentAssetCollection.fromCBOR(fields[1]), memo);
27572
+ }
27573
+ /** Deterministic, versioned, tagged CBOR: `tag(39050)[ version, assets, memo? ]`. */
27574
+ encode() {
27575
+ return Promise.resolve(
27576
+ import_CborSerializer.CborSerializer.encodeTag(
27577
+ _SpherePaymentData.CBOR_TAG,
27578
+ import_CborSerializer.CborSerializer.encodeArray(
27579
+ import_CborSerializer.CborSerializer.encodeUnsignedInteger(_SpherePaymentData.VERSION),
27580
+ this.assets.toCBOR(),
27581
+ import_CborSerializer.CborSerializer.encodeNullable(this._memo, import_CborSerializer.CborSerializer.encodeByteString)
27582
+ )
27583
+ )
27584
+ );
27585
+ }
27586
+ /** Project to a sphere-domain value (hex coin id + bigint amount), preserving order. */
27587
+ toValue() {
27588
+ return {
27589
+ assets: this.assets.toArray().map((a) => ({
27590
+ coinId: import_HexConverter.HexConverter.encode(a.id.bytes),
27591
+ amount: a.value
27592
+ }))
27593
+ };
27594
+ }
27595
+ /** Balance of a single coin within this payload (0n when absent). */
27596
+ balanceOf(coinId) {
27597
+ if (!COIN_ID_PATTERN.test(coinId)) {
27598
+ throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
27599
+ }
27600
+ const asset = this.assets.get(new import_AssetId.AssetId(import_HexConverter.HexConverter.decode(coinId)));
27601
+ return asset ? asset.value : 0n;
27602
+ }
27603
+ };
27604
+ function decodeSpherePaymentData(bytes) {
27605
+ return Promise.resolve(SpherePaymentData.fromCBOR(bytes));
27606
+ }
27607
+
27608
+ // token-engine/SphereTokenEngine.ts
27609
+ init_errors();
27610
+ var SphereTokenEngine = class {
27611
+ constructor(deps) {
27612
+ this.deps = deps;
27613
+ }
27614
+ // ── identity ────────────────────────────────────────────────────────────────
27615
+ getIdentity() {
27616
+ return { chainPubkey: new Uint8Array(this.deps.signingService.publicKey) };
27617
+ }
27618
+ /** Legacy DIRECT:// address (Path A). Async: the derivation hashes via the SDK. */
27619
+ deriveIdentityAddress(pubkey) {
27620
+ return deriveDirectAddress(pubkey ?? this.deps.signingService.publicKey);
27621
+ }
27622
+ // ── value (read) ─────────────────────────────────────────────────────────────
27623
+ readValue(token) {
27624
+ return token.value;
27625
+ }
27626
+ balanceOf(token, coinId) {
27627
+ let sum = 0n;
27628
+ for (const asset of token.value?.assets ?? []) {
27629
+ if (asset.coinId === coinId) sum += asset.amount;
27630
+ }
27631
+ return sum;
27632
+ }
27633
+ tokenId(token) {
27634
+ return token.blob.tokenId;
27635
+ }
27636
+ readMemo(token) {
27637
+ const sdkToken = token.sdkToken;
27638
+ if (sdkToken.transactions.length > 0) {
27639
+ return sdkToken.latestTransaction.data;
27640
+ }
27641
+ const data = sdkToken.genesis.data;
27642
+ if (data && this.isSpherePaymentData(data)) {
27643
+ return SpherePaymentData.fromCBOR(data).memo;
27644
+ }
27645
+ return null;
27646
+ }
27647
+ readTokenData(token) {
27648
+ const data = token.sdkToken.genesis.data;
27649
+ return data ? new Uint8Array(data) : null;
27650
+ }
27651
+ // ── lifecycle ────────────────────────────────────────────────────────────────
27652
+ async mint(params, options) {
27653
+ const recipient = import_SignaturePredicate.SignaturePredicate.create(params.recipientPubkey);
27654
+ const data = params.value ? await SpherePaymentData.fromValue(params.value).encode() : null;
27655
+ const mintTx = await import_MintTransaction.MintTransaction.create(this.deps.networkId, recipient, data);
27656
+ const certificationData = await import_CertificationData.CertificationData.fromMintTransaction(mintTx);
27657
+ const response = await this.deps.client.submitCertificationRequest(certificationData);
27658
+ if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
27659
+ throw new SphereError(`Mint certification failed: ${response.status}`, "AGGREGATOR_ERROR");
27660
+ }
27661
+ const proof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
27662
+ this.deps.client,
27663
+ this.deps.trustBase,
27664
+ this.deps.predicateVerifier,
27665
+ mintTx,
27666
+ options?.signal
27667
+ );
27668
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
27669
+ const token = await import_Token2.Token.mint(
27670
+ this.deps.trustBase,
27671
+ this.deps.predicateVerifier,
27672
+ this.deps.mintJustificationVerifier,
27673
+ certified
27674
+ );
27675
+ return this.wrapToken(token);
27676
+ }
27677
+ async mintDataToken(params, options) {
27678
+ const recipient = import_SignaturePredicate.SignaturePredicate.create(params.recipientPubkey);
27679
+ const tokenType = params.tokenType ? new import_TokenType.TokenType(params.tokenType) : import_TokenType.TokenType.generate();
27680
+ const salt = params.salt ? import_TokenSalt.TokenSalt.fromBytes(params.salt) : import_TokenSalt.TokenSalt.generate();
27681
+ const mintTx = await import_MintTransaction.MintTransaction.create(this.deps.networkId, recipient, params.data, tokenType, salt);
27682
+ const certificationData = await import_CertificationData.CertificationData.fromMintTransaction(mintTx);
27683
+ const response = await this.deps.client.submitCertificationRequest(certificationData);
27684
+ if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
27685
+ throw new SphereError(`Data-token mint failed: ${response.status}`, "AGGREGATOR_ERROR");
27686
+ }
27687
+ const proof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
27688
+ this.deps.client,
27689
+ this.deps.trustBase,
27690
+ this.deps.predicateVerifier,
27691
+ mintTx,
27692
+ options?.signal
27693
+ );
27694
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
27695
+ const token = await import_Token2.Token.mint(
27696
+ this.deps.trustBase,
27697
+ this.deps.predicateVerifier,
27698
+ this.deps.mintJustificationVerifier,
27699
+ certified
27700
+ );
27701
+ return this.wrapToken(token);
27702
+ }
27703
+ async transfer(params, options) {
27704
+ this.assertOwned(params.token);
27705
+ const recipient = import_SignaturePredicate.SignaturePredicate.create(params.recipientPubkey);
27706
+ const stateMask = crypto.getRandomValues(new Uint8Array(32));
27707
+ const transferTx = await import_TransferTransaction.TransferTransaction.create(params.token.sdkToken, recipient, stateMask, params.data ?? null);
27708
+ const unlockScript = await import_SignaturePredicateUnlockScript.SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);
27709
+ const certificationData = await import_CertificationData.CertificationData.fromTransaction(transferTx, unlockScript);
27710
+ const response = await this.deps.client.submitCertificationRequest(certificationData);
27711
+ if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
27712
+ throw new SphereError(`Transfer certification failed: ${response.status}`, "TRANSFER_FAILED");
27713
+ }
27714
+ const proof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
27715
+ this.deps.client,
27716
+ this.deps.trustBase,
27717
+ this.deps.predicateVerifier,
27718
+ transferTx,
27719
+ options?.signal
27720
+ );
27721
+ const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
27722
+ const transferred = await params.token.sdkToken.transfer(this.deps.trustBase, this.deps.predicateVerifier, certified);
27723
+ return this.wrapToken(transferred);
27724
+ }
27725
+ async split(params, options) {
27726
+ this.assertOwned(params.token);
27727
+ if (params.outputs.length === 0) {
27728
+ throw new SphereError("Split requires at least one output", "VALIDATION_ERROR");
27729
+ }
27730
+ const requests = params.outputs.map(
27731
+ (o) => import_SplitTokenRequest.SplitTokenRequest.create(
27732
+ import_SignaturePredicate.SignaturePredicate.create(o.recipientPubkey),
27733
+ import_PaymentAssetCollection.PaymentAssetCollection.create(sphereAssetToSdk(o.coinId, o.amount))
27734
+ )
27735
+ );
27736
+ const split = await import_TokenSplit.TokenSplit.split(params.token.sdkToken, decodeSpherePaymentData, requests);
27737
+ const burnUnlock = await import_SignaturePredicateUnlockScript.SignaturePredicateUnlockScript.create(split.burn.transaction, this.deps.signingService);
27738
+ const burnCert = await import_CertificationData.CertificationData.fromTransaction(split.burn.transaction, burnUnlock);
27739
+ const burnResponse = await this.deps.client.submitCertificationRequest(burnCert);
27740
+ if (burnResponse.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
27741
+ throw new SphereError(`Split burn failed: ${burnResponse.status}`, "TRANSFER_FAILED");
27742
+ }
27743
+ const burnProof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
27744
+ this.deps.client,
27745
+ this.deps.trustBase,
27746
+ this.deps.predicateVerifier,
27747
+ split.burn.transaction,
27748
+ options?.signal
27749
+ );
27750
+ const burnCertified = await split.burn.transaction.toCertifiedTransaction(
27751
+ this.deps.trustBase,
27752
+ this.deps.predicateVerifier,
27753
+ burnProof
27754
+ );
27755
+ const burntToken = await params.token.sdkToken.transfer(
27756
+ this.deps.trustBase,
27757
+ this.deps.predicateVerifier,
27758
+ burnCertified
27759
+ );
27760
+ const outputs = [];
27761
+ for (let i = 0; i < split.tokens.length; i++) {
27762
+ const splitToken = split.tokens[i];
27763
+ const data = await SpherePaymentData.create(splitToken.assets, params.outputs[i].data ?? null).encode();
27764
+ const justification = import_SplitMintJustification.SplitMintJustification.create(burntToken, splitToken.proofs).toCBOR();
27765
+ const mintTx = await import_MintTransaction.MintTransaction.create(
27766
+ splitToken.networkId,
27767
+ splitToken.recipient,
27768
+ data,
27769
+ splitToken.tokenType,
27770
+ splitToken.salt,
27771
+ justification
27772
+ );
27773
+ const certData = await import_CertificationData.CertificationData.fromMintTransaction(mintTx);
27774
+ const response = await this.deps.client.submitCertificationRequest(certData);
27775
+ if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
27776
+ throw new SphereError(`Split mint failed: ${response.status}`, "AGGREGATOR_ERROR");
27777
+ }
27778
+ const proof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
27779
+ this.deps.client,
27780
+ this.deps.trustBase,
27781
+ this.deps.predicateVerifier,
27782
+ mintTx,
27783
+ options?.signal
27784
+ );
27785
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
27786
+ const token = await import_Token2.Token.mint(
27787
+ this.deps.trustBase,
27788
+ this.deps.predicateVerifier,
27789
+ this.deps.mintJustificationVerifier,
27790
+ certified
27791
+ );
27792
+ outputs.push(this.wrapToken(token));
27793
+ }
27794
+ return { outputs };
27795
+ }
27796
+ // ── verification ─────────────────────────────────────────────────────────────
27797
+ async verify(token, _options) {
27798
+ const result = await token.sdkToken.verify(
27799
+ this.deps.trustBase,
27800
+ this.deps.predicateVerifier,
27801
+ this.deps.mintJustificationVerifier
27802
+ );
27803
+ return result.status === import_VerificationStatus.VerificationStatus.OK ? { ok: true } : { ok: false, reason: String(result.status) };
27804
+ }
27805
+ async isSpent(token, _options) {
27806
+ const probe = await import_TransferTransaction.TransferTransaction.create(
27807
+ token.sdkToken,
27808
+ import_SignaturePredicate.SignaturePredicate.create(this.deps.signingService.publicKey),
27809
+ new Uint8Array(32)
27810
+ );
27811
+ const stateId = await import_StateId.StateId.fromTransaction(probe);
27812
+ const response = await this.deps.client.getInclusionProof(stateId);
27813
+ return response.inclusionProof.inclusionCertificate !== null;
27814
+ }
27815
+ // ── serialization ────────────────────────────────────────────────────────────
27816
+ encodeToken(token) {
27817
+ return token.blob;
27818
+ }
27819
+ async decodeToken(blob) {
27820
+ const sdkToken = await import_Token2.Token.fromCBOR(blob.token);
27821
+ if (sdkToken.genesis.networkId.id !== this.deps.networkId.id) {
27822
+ throw new SphereError(
27823
+ `Token network mismatch: token is on network ${sdkToken.genesis.networkId.id}, engine on ${this.deps.networkId.id}`,
27824
+ "VALIDATION_ERROR"
27825
+ );
27826
+ }
27827
+ return this.wrapToken(sdkToken);
27828
+ }
27829
+ // ── internals ────────────────────────────────────────────────────────────────
27830
+ /** Fail fast if this engine's key does not own the token's current state. */
27831
+ assertOwned(token) {
27832
+ const owner = token.sdkToken.latestTransaction.recipient;
27833
+ const mine = import_EncodedPredicate.EncodedPredicate.fromPredicate(import_SignaturePredicate.SignaturePredicate.create(this.deps.signingService.publicKey));
27834
+ if (!import_EncodedPredicate.EncodedPredicate.equals(owner, mine)) {
27835
+ throw new SphereError("Cannot transfer a token not owned by this engine identity", "VALIDATION_ERROR");
27836
+ }
27837
+ }
27838
+ /** Wrap an SDK token into a SphereToken: cache its blob (incl. stable tokenId) + decoded value. */
27839
+ wrapToken(sdkToken) {
27840
+ const data = sdkToken.genesis.data;
27841
+ let value = null;
27842
+ if (data && this.isSpherePaymentData(data)) {
27843
+ try {
27844
+ value = SpherePaymentData.fromCBOR(data).toValue();
27845
+ } catch (err) {
27846
+ throw new SphereError(
27847
+ `Failed to decode token payment data: ${err instanceof Error ? err.message : String(err)}`,
27848
+ "VALIDATION_ERROR"
27849
+ );
27850
+ }
27851
+ }
27852
+ const blob = {
27853
+ v: TOKEN_BLOB_VERSION,
27854
+ network: sdkToken.genesis.networkId.id,
27855
+ tokenId: import_HexConverter.HexConverter.encode(sdkToken.id.bytes),
27856
+ token: sdkToken.toCBOR()
27857
+ };
27858
+ return { sdkToken, blob, value };
27859
+ }
27860
+ /** True if the bytes are a SpherePaymentData envelope (value token) vs a raw data token. */
27861
+ isSpherePaymentData(data) {
27862
+ try {
27863
+ return import_CborDeserializer.CborDeserializer.decodeTag(data).tag === SpherePaymentData.CBOR_TAG;
27864
+ } catch {
27865
+ return false;
27866
+ }
27867
+ }
27868
+ };
27869
+
27870
+ // token-engine/factory.ts
27871
+ async function createSphereTokenEngine(config) {
27872
+ if (config.trustBaseJson == null) {
27873
+ throw new SphereError("Engine config requires a trust base (trustBaseJson)", "INVALID_CONFIG");
27874
+ }
27875
+ const trustBase = import_RootTrustBase.RootTrustBase.fromJSON(config.trustBaseJson);
27876
+ const predicateVerifier = import_PredicateVerifierService.PredicateVerifierService.create();
27877
+ const mintJustificationVerifier = new import_MintJustificationVerifierService.MintJustificationVerifierService();
27878
+ mintJustificationVerifier.register(
27879
+ new import_SplitMintJustificationVerifier.SplitMintJustificationVerifier(trustBase, predicateVerifier, decodeSpherePaymentData)
27880
+ );
27881
+ const deps = {
27882
+ client: new import_StateTransitionClient.StateTransitionClient(new import_AggregatorClient.AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
27883
+ trustBase,
27884
+ predicateVerifier,
27885
+ mintJustificationVerifier,
27886
+ signingService: new import_SigningService.SigningService(config.privateKey),
27887
+ // The trust base is the single source of truth for the network id (it carries
27888
+ // NetworkId.fromId, so any id works — e.g. testnet2 = 4 — with no enum entry).
27889
+ networkId: trustBase.networkId
27890
+ };
27891
+ return new SphereTokenEngine(deps);
27892
+ }
27893
+
27472
27894
  // core/Sphere.ts
27473
- var import_SigningService2 = require("@unicitylabs/state-transition-sdk/lib/sign/SigningService");
27474
- var import_TokenType5 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
27475
- var import_HashAlgorithm7 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
27476
- var import_UnmaskedPredicateReference3 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
27477
- var import_nostr_js_sdk5 = require("@unicitylabs/nostr-js-sdk");
27895
+ var import_nostr_js_sdk4 = require("@unicitylabs/nostr-js-sdk");
27478
27896
  function isValidNametag2(nametag) {
27479
- if ((0, import_nostr_js_sdk5.isPhoneNumber)(nametag)) return true;
27897
+ if ((0, import_nostr_js_sdk4.isPhoneNumber)(nametag)) return true;
27480
27898
  return /^[a-z0-9_-]{3,20}$/.test(nametag);
27481
27899
  }
27482
- var UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
27483
27900
  async function deriveL3PredicateAddress(privateKey) {
27484
- const secret = Buffer.from(privateKey, "hex");
27485
- const signingService = await import_SigningService2.SigningService.createFromSecret(secret);
27486
- const tokenTypeBytes = Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex");
27487
- const tokenType = new import_TokenType5.TokenType(tokenTypeBytes);
27488
- const predicateRef = import_UnmaskedPredicateReference3.UnmaskedPredicateReference.create(
27489
- tokenType,
27490
- signingService.algorithm,
27491
- signingService.publicKey,
27492
- import_HashAlgorithm7.HashAlgorithm.SHA256
27493
- );
27494
- return (await (await predicateRef).toAddress()).toString();
27901
+ const prehashedPublicKey = getPublicKey(sha2562(privateKey, "hex"));
27902
+ return deriveDirectAddress(hexToBytes2(prehashedPublicKey));
27495
27903
  }
27496
27904
  var Sphere = class _Sphere {
27497
27905
  // Singleton
@@ -27513,14 +27921,14 @@ var Sphere = class _Sphere {
27513
27921
  _addressIdToIndex = /* @__PURE__ */ new Map();
27514
27922
  /** Nametag cache: addressId -> (nametagIndex -> nametag). Separate from tracked addresses. */
27515
27923
  _addressNametags = /* @__PURE__ */ new Map();
27516
- /** Cached PROXY address (computed once when nametag is set) */
27517
- _cachedProxyAddress = void 0;
27518
27924
  // Providers
27519
27925
  _storage;
27520
27926
  _tokenStorageProviders = /* @__PURE__ */ new Map();
27521
27927
  _transport;
27522
27928
  _oracle;
27523
27929
  _priceProvider;
27930
+ /** v2 token engine (built per active address from the oracle); injected into modules. */
27931
+ _tokenEngine;
27524
27932
  // Modules (single-instance — backward compat, delegates to active address)
27525
27933
  _payments;
27526
27934
  _communications;
@@ -27861,20 +28269,6 @@ var Sphere = class _Sphere {
27861
28269
  await sphere.syncIdentityWithTransport();
27862
28270
  sphere._initialized = true;
27863
28271
  _Sphere.instance = sphere;
27864
- if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {
27865
- progress?.({ step: "registering_nametag", message: "Restoring nametag token..." });
27866
- logger.debug("Sphere", `Unicity ID @${sphere._identity.nametag} has no token, attempting to mint...`);
27867
- try {
27868
- const result = await sphere.mintNametag(sphere._identity.nametag);
27869
- if (result.success) {
27870
- logger.debug("Sphere", `Nametag token minted successfully on load`);
27871
- } else {
27872
- logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
27873
- }
27874
- } catch (err) {
27875
- logger.warn("Sphere", `Nametag token mint failed:`, err);
27876
- }
27877
- }
27878
28272
  if (options.discoverAddresses !== false && sphere._transport.discoverAddresses && sphere._masterKey) {
27879
28273
  progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
27880
28274
  try {
@@ -28926,13 +29320,13 @@ var Sphere = class _Sphere {
28926
29320
  oracle: this._oracle,
28927
29321
  emitEvent: this.emitEvent.bind(this),
28928
29322
  chainCode: this._masterKey?.chainCode || void 0,
28929
- price: this._priceProvider ?? void 0
29323
+ price: this._priceProvider ?? void 0,
29324
+ tokenEngine: moduleSet.tokenEngine
28930
29325
  });
28931
29326
  }
28932
29327
  }
28933
29328
  this._identity = newIdentity;
28934
29329
  this._currentAddressIndex = index;
28935
- await this._updateCachedProxyAddress();
28936
29330
  const activeModules = this._addressModules.get(index);
28937
29331
  this._payments = activeModules.payments;
28938
29332
  this._communications = activeModules.communications;
@@ -28970,35 +29364,10 @@ var Sphere = class _Sphere {
28970
29364
  }
28971
29365
  if (newNametag) {
28972
29366
  await this.persistAddressNametags();
28973
- if (!this._payments.hasNametag()) {
28974
- logger.debug("Sphere", `Minting nametag token for @${newNametag}...`);
28975
- try {
28976
- const result = await this.mintNametag(newNametag);
28977
- if (result.success) {
28978
- logger.debug("Sphere", `Nametag token minted successfully`);
28979
- } else {
28980
- logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
28981
- }
28982
- } catch (err) {
28983
- logger.warn("Sphere", `Nametag token mint failed:`, err);
28984
- }
28985
- }
28986
29367
  this.emitEvent("nametag:registered", {
28987
29368
  nametag: newNametag,
28988
29369
  addressIndex: index
28989
29370
  });
28990
- } else if (this._identity?.nametag && !this._payments.hasNametag()) {
28991
- logger.debug("Sphere", `Unicity ID @${this._identity.nametag} has no token after switch, minting...`);
28992
- try {
28993
- const result = await this.mintNametag(this._identity.nametag);
28994
- if (result.success) {
28995
- logger.debug("Sphere", `Nametag token minted successfully after switch`);
28996
- } else {
28997
- logger.warn("Sphere", `Could not mint nametag token after switch: ${result.error}`);
28998
- }
28999
- } catch (err) {
29000
- logger.warn("Sphere", `Nametag token mint failed after switch:`, err);
29001
- }
29002
29371
  }
29003
29372
  }
29004
29373
  /**
@@ -29028,6 +29397,7 @@ var Sphere = class _Sphere {
29028
29397
  const communications = createCommunicationsModule(this._communicationsConfig);
29029
29398
  const groupChat = this._groupChatConfig ? createGroupChatModule(this._groupChatConfig) : null;
29030
29399
  const market = this._marketConfig ? createMarketModule(this._marketConfig) : null;
29400
+ const tokenEngine = await this.buildTokenEngine(identity);
29031
29401
  payments.initialize({
29032
29402
  identity,
29033
29403
  storage: this._storage,
@@ -29036,7 +29406,8 @@ var Sphere = class _Sphere {
29036
29406
  oracle: this._oracle,
29037
29407
  emitEvent,
29038
29408
  chainCode: this._masterKey?.chainCode || void 0,
29039
- price: this._priceProvider ?? void 0
29409
+ price: this._priceProvider ?? void 0,
29410
+ tokenEngine
29040
29411
  });
29041
29412
  communications.initialize({
29042
29413
  identity,
@@ -29072,7 +29443,8 @@ var Sphere = class _Sphere {
29072
29443
  emitEvent,
29073
29444
  on: this.on.bind(this),
29074
29445
  storage: this._storage,
29075
- communications
29446
+ communications,
29447
+ tokenEngine
29076
29448
  });
29077
29449
  } else {
29078
29450
  logger.warn("Sphere", "Accounting module enabled but no token storage available \u2014 disabling");
@@ -29132,6 +29504,7 @@ var Sphere = class _Sphere {
29132
29504
  market,
29133
29505
  transportAdapter: adapter,
29134
29506
  tokenStorageProviders: new Map(tokenStorageProviders),
29507
+ tokenEngine,
29135
29508
  initialized: true
29136
29509
  };
29137
29510
  this._addressModules.set(index, moduleSet);
@@ -29687,15 +30060,6 @@ var Sphere = class _Sphere {
29687
30060
  hasNametag() {
29688
30061
  return !!this._identity?.nametag;
29689
30062
  }
29690
- /**
29691
- * Get the PROXY address for the current nametag
29692
- * PROXY addresses are derived from the nametag hash and require
29693
- * the nametag token to claim funds sent to them
29694
- * @returns PROXY address string or undefined if no nametag
29695
- */
29696
- getProxyAddress() {
29697
- return this._cachedProxyAddress;
29698
- }
29699
30063
  /**
29700
30064
  * Resolve any identifier to full peer information.
29701
30065
  * Accepts @nametag, bare nametag, DIRECT://, PROXY://, L1 address, or transport pubkey.
@@ -29730,17 +30094,6 @@ var Sphere = class _Sphere {
29730
30094
  throw new SphereError(`Cannot resolve address: ${address.slice(0, 30)}`, "INVALID_RECIPIENT");
29731
30095
  }
29732
30096
  }
29733
- /** Compute and cache the PROXY address from the current nametag */
29734
- async _updateCachedProxyAddress() {
29735
- const nametag = this._identity?.nametag;
29736
- if (!nametag) {
29737
- this._cachedProxyAddress = void 0;
29738
- return;
29739
- }
29740
- const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
29741
- const proxyAddr = await ProxyAddress.fromNameTag(nametag);
29742
- this._cachedProxyAddress = proxyAddr.toString();
29743
- }
29744
30097
  /**
29745
30098
  * Register a nametag for the current active address
29746
30099
  * Each address can have its own independent nametag
@@ -29768,17 +30121,6 @@ var Sphere = class _Sphere {
29768
30121
  if (this._identity?.nametag) {
29769
30122
  throw new SphereError(`Unicity ID already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`, "ALREADY_INITIALIZED");
29770
30123
  }
29771
- if (!this._payments.hasNametag()) {
29772
- logger.debug("Sphere", `Minting nametag token for @${cleanNametag}...`);
29773
- const result = await this.mintNametag(cleanNametag);
29774
- if (!result.success) {
29775
- throw new SphereError(
29776
- `Failed to mint nametag token: ${result.error}`,
29777
- "AGGREGATOR_ERROR"
29778
- );
29779
- }
29780
- logger.debug("Sphere", `Nametag token minted successfully`);
29781
- }
29782
30124
  if (this._transport.publishIdentityBinding) {
29783
30125
  const success = await this._transport.publishIdentityBinding(
29784
30126
  this._identity.chainPubkey,
@@ -29791,7 +30133,6 @@ var Sphere = class _Sphere {
29791
30133
  }
29792
30134
  }
29793
30135
  this._identity.nametag = cleanNametag;
29794
- await this._updateCachedProxyAddress();
29795
30136
  const currentAddressId = this._trackedAddresses.get(this._currentAddressIndex)?.addressId;
29796
30137
  if (currentAddressId) {
29797
30138
  let nametags = this._addressNametags.get(currentAddressId);
@@ -29824,35 +30165,19 @@ var Sphere = class _Sphere {
29824
30165
  await this._storage.saveTrackedAddresses(entries);
29825
30166
  }
29826
30167
  /**
29827
- * Mint a nametag token on-chain (like Sphere wallet and lottery)
29828
- * This creates the nametag token required for receiving tokens via PROXY addresses (@nametag)
30168
+ * Check whether a nametag is available to register.
29829
30169
  *
29830
- * @param nametag - The nametag to mint (e.g., "alice" or "@alice")
29831
- * @returns MintNametagResult with success status and token if successful
30170
+ * D5: nametags are Nostr bindings (name chainPubkey), not on-chain tokens. Availability is
30171
+ * first-seen-wins a name is available iff no binding resolves for it.
29832
30172
  *
29833
- * @example
29834
- * ```typescript
29835
- * // Mint nametag token for receiving via @alice
29836
- * const result = await sphere.mintNametag('alice');
29837
- * if (result.success) {
29838
- * console.log('Nametag minted:', result.nametagData?.name);
29839
- * } else {
29840
- * console.error('Mint failed:', result.error);
29841
- * }
29842
- * ```
29843
- */
29844
- async mintNametag(nametag) {
29845
- this.ensureReady();
29846
- return this._payments.mintNametag(nametag);
29847
- }
29848
- /**
29849
- * Check if a nametag is available for minting
29850
30173
  * @param nametag - The nametag to check (e.g., "alice" or "@alice")
29851
- * @returns true if available, false if taken or error
30174
+ * @returns true if available, false if already taken
29852
30175
  */
29853
30176
  async isNametagAvailable(nametag) {
29854
30177
  this.ensureReady();
29855
- return this._payments.isNametagAvailable(nametag);
30178
+ if (!this._transport.resolveNametag) return true;
30179
+ const bound = await this._transport.resolveNametag(this.cleanNametag(nametag));
30180
+ return bound == null;
29856
30181
  }
29857
30182
  /**
29858
30183
  * Load tracked addresses from storage.
@@ -30027,7 +30352,6 @@ var Sphere = class _Sphere {
30027
30352
  }
30028
30353
  if (recoveredNametag && !this._identity?.nametag) {
30029
30354
  this._identity.nametag = recoveredNametag;
30030
- await this._updateCachedProxyAddress();
30031
30355
  const entry = await this.ensureAddressTracked(this._currentAddressIndex);
30032
30356
  let nametags = this._addressNametags.get(entry.addressId);
30033
30357
  if (!nametags) {
@@ -30116,7 +30440,6 @@ var Sphere = class _Sphere {
30116
30440
  try {
30117
30441
  if (this._identity) {
30118
30442
  this._identity.nametag = recoveredNametag;
30119
- await this._updateCachedProxyAddress();
30120
30443
  }
30121
30444
  const entry = await this.ensureAddressTracked(this._currentAddressIndex);
30122
30445
  let nametags = this._addressNametags.get(entry.addressId);
@@ -30136,7 +30459,7 @@ var Sphere = class _Sphere {
30136
30459
  */
30137
30460
  cleanNametag(raw) {
30138
30461
  const stripped = raw.startsWith("@") ? raw.slice(1) : raw;
30139
- return (0, import_nostr_js_sdk5.normalizeNametag)(stripped);
30462
+ return (0, import_nostr_js_sdk4.normalizeNametag)(stripped);
30140
30463
  }
30141
30464
  // ===========================================================================
30142
30465
  // Public Methods - Lifecycle
@@ -30316,7 +30639,6 @@ var Sphere = class _Sphere {
30316
30639
  } else if (this._identity && nametag) {
30317
30640
  this._identity.nametag = nametag;
30318
30641
  }
30319
- await this._updateCachedProxyAddress();
30320
30642
  }
30321
30643
  async initializeIdentityFromMnemonic(mnemonic, derivationPath) {
30322
30644
  const basePath = derivationPath ?? DEFAULT_BASE_PATH;
@@ -30467,10 +30789,45 @@ var Sphere = class _Sphere {
30467
30789
  this._providerEventCleanups = [];
30468
30790
  this._lastProviderConnected.clear();
30469
30791
  }
30792
+ /**
30793
+ * Construct the v2 token engine for a given address identity (defaults to the
30794
+ * active one) from the oracle's gateway URL + trust base and that address's
30795
+ * signing key. The engine is per-address — each address signs with its own key.
30796
+ * The trust base is the single source of truth for the network id (so any id
30797
+ * works — e.g. testnet2 = 4 — with no enum entry). Returns undefined (modules
30798
+ * keep their legacy path) when the oracle can't supply a trust base / url, or
30799
+ * construction fails — a misconfigured oracle never breaks initialization.
30800
+ */
30801
+ async buildTokenEngine(identity) {
30802
+ const oracle = this._oracle;
30803
+ const privateKey = (identity ?? this._identity)?.privateKey;
30804
+ const trustBaseJson = oracle.getTrustBaseJson?.() ?? null;
30805
+ const aggregatorUrl = oracle.getAggregatorUrl?.();
30806
+ if (!trustBaseJson || !aggregatorUrl || !privateKey) {
30807
+ logger.warn("Sphere", "v2 token engine not constructed (oracle has no trust base / url, or no identity) \u2014 legacy path");
30808
+ return void 0;
30809
+ }
30810
+ try {
30811
+ return await createSphereTokenEngine({
30812
+ aggregatorUrl,
30813
+ apiKey: oracle.getApiKey?.(),
30814
+ privateKey: hexToBytes2(privateKey),
30815
+ trustBaseJson
30816
+ });
30817
+ } catch (err) {
30818
+ logger.warn(
30819
+ "Sphere",
30820
+ `Failed to construct v2 token engine \u2014 modules use the legacy path: ${err instanceof Error ? err.message : String(err)}`
30821
+ );
30822
+ return void 0;
30823
+ }
30824
+ }
30470
30825
  async initializeModules() {
30471
30826
  const emitEvent = this.emitEvent.bind(this);
30472
30827
  const adapter = await this.ensureTransportMux(this._currentAddressIndex, this._identity);
30473
30828
  const moduleTransport = adapter ?? this._transport;
30829
+ this._tokenEngine = await this.buildTokenEngine();
30830
+ const tokenEngine = this._tokenEngine;
30474
30831
  this._payments.initialize({
30475
30832
  identity: this._identity,
30476
30833
  storage: this._storage,
@@ -30481,7 +30838,8 @@ var Sphere = class _Sphere {
30481
30838
  // Pass chain code for L1 HD derivation
30482
30839
  chainCode: this._masterKey?.chainCode || void 0,
30483
30840
  price: this._priceProvider ?? void 0,
30484
- disabledProviderIds: this._disabledProviders
30841
+ disabledProviderIds: this._disabledProviders,
30842
+ tokenEngine
30485
30843
  });
30486
30844
  this._communications.initialize({
30487
30845
  identity: this._identity,
@@ -30517,7 +30875,8 @@ var Sphere = class _Sphere {
30517
30875
  emitEvent,
30518
30876
  on: this.on.bind(this),
30519
30877
  storage: this._storage,
30520
- communications: this._communications
30878
+ communications: this._communications,
30879
+ tokenEngine
30521
30880
  });
30522
30881
  } else {
30523
30882
  logger.warn("Sphere", "Accounting module enabled but no token storage available \u2014 disabling");
@@ -30579,6 +30938,7 @@ var Sphere = class _Sphere {
30579
30938
  market: this._market,
30580
30939
  transportAdapter: adapter,
30581
30940
  tokenStorageProviders: new Map(this._tokenStorageProviders),
30941
+ tokenEngine: this._tokenEngine,
30582
30942
  initialized: true
30583
30943
  });
30584
30944
  }