@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.
- package/dist/core/index.cjs +1127 -767
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +3383 -3147
- package/dist/core/index.d.ts +3383 -3147
- package/dist/core/index.js +1115 -755
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +17 -19
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +17 -19
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +17 -19
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +10 -5
- package/dist/impl/nodejs/index.d.ts +10 -5
- package/dist/impl/nodejs/index.js +17 -19
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +1181 -1041
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3906 -3728
- package/dist/index.d.ts +3906 -3728
- package/dist/index.js +1159 -1019
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/core/index.js
CHANGED
|
@@ -1890,26 +1890,14 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
1890
1890
|
}
|
|
1891
1891
|
/**
|
|
1892
1892
|
* Convert a BindingInfo (from nostr-js-sdk) to PeerInfo (sphere-sdk type).
|
|
1893
|
-
* Computes PROXY address from nametag if available.
|
|
1894
1893
|
*/
|
|
1895
1894
|
async bindingInfoToPeerInfo(binding, nametag) {
|
|
1896
|
-
const nametagValue = nametag || binding.nametag;
|
|
1897
|
-
let proxyAddress = binding.proxyAddress;
|
|
1898
|
-
if (nametagValue && !proxyAddress) {
|
|
1899
|
-
try {
|
|
1900
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
1901
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametagValue);
|
|
1902
|
-
proxyAddress = proxyAddr.toString();
|
|
1903
|
-
} catch {
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
1895
|
return {
|
|
1907
|
-
nametag:
|
|
1896
|
+
nametag: nametag || binding.nametag,
|
|
1908
1897
|
transportPubkey: binding.transportPubkey,
|
|
1909
1898
|
chainPubkey: binding.publicKey || "",
|
|
1910
1899
|
l1Address: binding.l1Address || "",
|
|
1911
1900
|
directAddress: binding.directAddress || "",
|
|
1912
|
-
proxyAddress,
|
|
1913
1901
|
timestamp: binding.timestamp
|
|
1914
1902
|
};
|
|
1915
1903
|
}
|
|
@@ -1935,7 +1923,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
1935
1923
|
chainPubkey: content.public_key || "",
|
|
1936
1924
|
l1Address: content.l1_address || "",
|
|
1937
1925
|
directAddress: content.direct_address || "",
|
|
1938
|
-
proxyAddress: content.proxy_address || void 0,
|
|
1939
1926
|
timestamp: bindingEvent.created_at * 1e3
|
|
1940
1927
|
};
|
|
1941
1928
|
} catch {
|
|
@@ -1978,7 +1965,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
1978
1965
|
chainPubkey: content.public_key || "",
|
|
1979
1966
|
l1Address: content.l1_address || "",
|
|
1980
1967
|
directAddress: content.direct_address || "",
|
|
1981
|
-
proxyAddress: content.proxy_address || void 0,
|
|
1982
1968
|
timestamp: event.created_at * 1e3
|
|
1983
1969
|
});
|
|
1984
1970
|
} catch {
|
|
@@ -2048,8 +2034,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2048
2034
|
}
|
|
2049
2035
|
const nostrPubkey = this.getNostrPubkey();
|
|
2050
2036
|
if (nametag) {
|
|
2051
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
2052
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
2053
2037
|
try {
|
|
2054
2038
|
const success2 = await this.nostrClient.publishNametagBinding(
|
|
2055
2039
|
nametag,
|
|
@@ -2057,8 +2041,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2057
2041
|
{
|
|
2058
2042
|
publicKey: chainPubkey,
|
|
2059
2043
|
l1Address,
|
|
2060
|
-
directAddress
|
|
2061
|
-
proxyAddress: proxyAddr.toString()
|
|
2044
|
+
directAddress
|
|
2062
2045
|
}
|
|
2063
2046
|
);
|
|
2064
2047
|
if (success2) {
|
|
@@ -6108,6 +6091,13 @@ var L1PaymentsModule = class {
|
|
|
6108
6091
|
}
|
|
6109
6092
|
};
|
|
6110
6093
|
|
|
6094
|
+
// types/v2-transfer.ts
|
|
6095
|
+
function isV2TransferPayload(obj) {
|
|
6096
|
+
if (!obj || typeof obj !== "object") return false;
|
|
6097
|
+
const p = obj;
|
|
6098
|
+
return p.type === "V2_TRANSFER" && typeof p.tokenBlob === "string" && p.tokenBlob.length > 0;
|
|
6099
|
+
}
|
|
6100
|
+
|
|
6111
6101
|
// modules/payments/TokenSplitExecutor.ts
|
|
6112
6102
|
init_logger();
|
|
6113
6103
|
init_errors();
|
|
@@ -6483,16 +6473,115 @@ init_logger();
|
|
|
6483
6473
|
init_errors();
|
|
6484
6474
|
import { Token as SdkToken } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
6485
6475
|
import { CoinId as CoinId2 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
6476
|
+
|
|
6477
|
+
// token-engine/sdk.ts
|
|
6478
|
+
import { StateTransitionClient } from "state-transition-sdk-v2/lib/StateTransitionClient.js";
|
|
6479
|
+
import { AggregatorClient } from "state-transition-sdk-v2/lib/api/AggregatorClient.js";
|
|
6480
|
+
import { NetworkId } from "state-transition-sdk-v2/lib/api/NetworkId.js";
|
|
6481
|
+
import { CertificationData } from "state-transition-sdk-v2/lib/api/CertificationData.js";
|
|
6482
|
+
import { CertificationResponse, CertificationStatus } from "state-transition-sdk-v2/lib/api/CertificationResponse.js";
|
|
6483
|
+
import { StateId } from "state-transition-sdk-v2/lib/api/StateId.js";
|
|
6484
|
+
import { InclusionProof } from "state-transition-sdk-v2/lib/api/InclusionProof.js";
|
|
6485
|
+
import { InclusionProofResponse } from "state-transition-sdk-v2/lib/api/InclusionProofResponse.js";
|
|
6486
|
+
import { RootTrustBase } from "state-transition-sdk-v2/lib/api/bft/RootTrustBase.js";
|
|
6487
|
+
import { waitInclusionProof as waitInclusionProof2 } from "state-transition-sdk-v2/lib/util/InclusionProofUtils.js";
|
|
6488
|
+
import { Token as Token2 } from "state-transition-sdk-v2/lib/transaction/Token.js";
|
|
6489
|
+
import { MintTransaction } from "state-transition-sdk-v2/lib/transaction/MintTransaction.js";
|
|
6490
|
+
import { TransferTransaction } from "state-transition-sdk-v2/lib/transaction/TransferTransaction.js";
|
|
6491
|
+
import { CertifiedMintTransaction } from "state-transition-sdk-v2/lib/transaction/CertifiedMintTransaction.js";
|
|
6492
|
+
import { CertifiedTransferTransaction } from "state-transition-sdk-v2/lib/transaction/CertifiedTransferTransaction.js";
|
|
6493
|
+
import { TokenId as TokenId2 } from "state-transition-sdk-v2/lib/transaction/TokenId.js";
|
|
6494
|
+
import { TokenType } from "state-transition-sdk-v2/lib/transaction/TokenType.js";
|
|
6495
|
+
import { TokenSalt } from "state-transition-sdk-v2/lib/transaction/TokenSalt.js";
|
|
6496
|
+
import { MintJustificationVerifierService } from "state-transition-sdk-v2/lib/transaction/verification/MintJustificationVerifierService.js";
|
|
6497
|
+
import { EncodedPredicate } from "state-transition-sdk-v2/lib/predicate/EncodedPredicate.js";
|
|
6498
|
+
import { SignaturePredicate } from "state-transition-sdk-v2/lib/predicate/builtin/SignaturePredicate.js";
|
|
6499
|
+
import { SignaturePredicateUnlockScript } from "state-transition-sdk-v2/lib/predicate/builtin/SignaturePredicateUnlockScript.js";
|
|
6500
|
+
import { BurnPredicate } from "state-transition-sdk-v2/lib/predicate/builtin/BurnPredicate.js";
|
|
6501
|
+
import { PredicateVerifierService } from "state-transition-sdk-v2/lib/predicate/verification/PredicateVerifierService.js";
|
|
6502
|
+
import { SigningService } from "state-transition-sdk-v2/lib/crypto/secp256k1/SigningService.js";
|
|
6503
|
+
import { Signature } from "state-transition-sdk-v2/lib/crypto/secp256k1/Signature.js";
|
|
6504
|
+
import { MintSigningService } from "state-transition-sdk-v2/lib/crypto/MintSigningService.js";
|
|
6505
|
+
import { HashAlgorithm as HashAlgorithm2 } from "state-transition-sdk-v2/lib/crypto/hash/HashAlgorithm.js";
|
|
6506
|
+
import { DataHash } from "state-transition-sdk-v2/lib/crypto/hash/DataHash.js";
|
|
6507
|
+
import { DataHasher } from "state-transition-sdk-v2/lib/crypto/hash/DataHasher.js";
|
|
6508
|
+
import { DataHasherFactory } from "state-transition-sdk-v2/lib/crypto/hash/DataHasherFactory.js";
|
|
6509
|
+
import { CborSerializer } from "state-transition-sdk-v2/lib/serialization/cbor/CborSerializer.js";
|
|
6510
|
+
import { CborDeserializer } from "state-transition-sdk-v2/lib/serialization/cbor/CborDeserializer.js";
|
|
6511
|
+
import { CborError } from "state-transition-sdk-v2/lib/serialization/cbor/CborError.js";
|
|
6512
|
+
import { Asset } from "state-transition-sdk-v2/lib/payment/asset/Asset.js";
|
|
6513
|
+
import { AssetId } from "state-transition-sdk-v2/lib/payment/asset/AssetId.js";
|
|
6514
|
+
import { PaymentAssetCollection } from "state-transition-sdk-v2/lib/payment/asset/PaymentAssetCollection.js";
|
|
6515
|
+
import { TokenSplit } from "state-transition-sdk-v2/lib/payment/TokenSplit.js";
|
|
6516
|
+
import { SplitTokenRequest } from "state-transition-sdk-v2/lib/payment/SplitTokenRequest.js";
|
|
6517
|
+
import { SplitToken } from "state-transition-sdk-v2/lib/payment/SplitToken.js";
|
|
6518
|
+
import { SplitAssetProof } from "state-transition-sdk-v2/lib/payment/SplitAssetProof.js";
|
|
6519
|
+
import { SplitMintJustification } from "state-transition-sdk-v2/lib/payment/SplitMintJustification.js";
|
|
6520
|
+
import { SplitMintJustificationVerifier } from "state-transition-sdk-v2/lib/payment/SplitMintJustificationVerifier.js";
|
|
6521
|
+
import { VerificationStatus } from "state-transition-sdk-v2/lib/verification/VerificationStatus.js";
|
|
6522
|
+
import { VerificationResult } from "state-transition-sdk-v2/lib/verification/VerificationResult.js";
|
|
6523
|
+
import { HexConverter } from "state-transition-sdk-v2/lib/util/HexConverter.js";
|
|
6524
|
+
import { BigintConverter } from "state-transition-sdk-v2/lib/util/BigintConverter.js";
|
|
6525
|
+
import { BitString } from "state-transition-sdk-v2/lib/util/BitString.js";
|
|
6526
|
+
|
|
6527
|
+
// token-engine/token-blob.ts
|
|
6528
|
+
var TOKEN_BLOB_TAG = 39051n;
|
|
6529
|
+
var TOKEN_BLOB_VERSION = 1;
|
|
6530
|
+
function encodeTokenBlob(blob) {
|
|
6531
|
+
return CborSerializer.encodeTag(
|
|
6532
|
+
TOKEN_BLOB_TAG,
|
|
6533
|
+
CborSerializer.encodeArray(
|
|
6534
|
+
CborSerializer.encodeUnsignedInteger(BigInt(blob.v)),
|
|
6535
|
+
CborSerializer.encodeUnsignedInteger(BigInt(blob.network)),
|
|
6536
|
+
CborSerializer.encodeTextString(blob.tokenId),
|
|
6537
|
+
CborSerializer.encodeByteString(blob.token)
|
|
6538
|
+
)
|
|
6539
|
+
);
|
|
6540
|
+
}
|
|
6541
|
+
function decodeTokenBlob(bytes) {
|
|
6542
|
+
const tag = CborDeserializer.decodeTag(bytes);
|
|
6543
|
+
if (tag.tag !== TOKEN_BLOB_TAG) {
|
|
6544
|
+
throw new CborError(`Invalid TokenBlob tag: ${tag.tag}`);
|
|
6545
|
+
}
|
|
6546
|
+
const fields = CborDeserializer.decodeArray(tag.data, 4);
|
|
6547
|
+
const v = Number(CborDeserializer.decodeUnsignedInteger(fields[0]));
|
|
6548
|
+
if (v !== TOKEN_BLOB_VERSION) {
|
|
6549
|
+
throw new CborError(`Unsupported TokenBlob version: ${v}`);
|
|
6550
|
+
}
|
|
6551
|
+
return {
|
|
6552
|
+
v,
|
|
6553
|
+
network: Number(CborDeserializer.decodeUnsignedInteger(fields[1])),
|
|
6554
|
+
tokenId: CborDeserializer.decodeTextString(fields[2]),
|
|
6555
|
+
token: CborDeserializer.decodeByteString(fields[3])
|
|
6556
|
+
};
|
|
6557
|
+
}
|
|
6558
|
+
|
|
6559
|
+
// modules/payments/SpendQueue.ts
|
|
6486
6560
|
var QUEUE_TIMEOUT_MS = 3e4;
|
|
6487
6561
|
var QUEUE_MAX_SIZE = 100;
|
|
6488
6562
|
var TAG2 = "SpendQueue";
|
|
6489
6563
|
var SpendPlanner = class {
|
|
6564
|
+
/**
|
|
6565
|
+
* Token engine (path B). When injected, value reads go through the v2 engine
|
|
6566
|
+
* (sdkData = engine blob); otherwise the legacy v1 SdkToken path is used. The
|
|
6567
|
+
* engine is wired after construction via {@link setEngine} (it is bound to the
|
|
6568
|
+
* payments deps, which arrive after the planner is built).
|
|
6569
|
+
*/
|
|
6570
|
+
engine;
|
|
6571
|
+
constructor(engine) {
|
|
6572
|
+
this.engine = engine;
|
|
6573
|
+
}
|
|
6574
|
+
/** Inject (or clear) the token engine. */
|
|
6575
|
+
setEngine(engine) {
|
|
6576
|
+
this.engine = engine;
|
|
6577
|
+
}
|
|
6490
6578
|
/**
|
|
6491
6579
|
* Async pre-computation: parse all tokens for a given coinId.
|
|
6492
6580
|
* Called BEFORE the synchronous critical section.
|
|
6493
6581
|
*
|
|
6494
|
-
* Filters to confirmed tokens matching the coinId,
|
|
6495
|
-
*
|
|
6582
|
+
* Filters to confirmed tokens matching the coinId, decodes each token's
|
|
6583
|
+
* sdkData and extracts the bigint amount — via the v2 engine when injected,
|
|
6584
|
+
* else the legacy v1 SdkToken path.
|
|
6496
6585
|
*/
|
|
6497
6586
|
async buildParsedPool(tokens, coinId) {
|
|
6498
6587
|
const pool = /* @__PURE__ */ new Map();
|
|
@@ -6501,20 +6590,28 @@ var SpendPlanner = class {
|
|
|
6501
6590
|
if (t.status !== "confirmed") continue;
|
|
6502
6591
|
if (!t.sdkData) continue;
|
|
6503
6592
|
try {
|
|
6504
|
-
const
|
|
6505
|
-
|
|
6506
|
-
const realAmount = this.getTokenBalance(sdkToken, coinId);
|
|
6507
|
-
if (realAmount <= 0n) {
|
|
6593
|
+
const { sdkToken, amount } = this.engine ? await this.parseViaEngine(t.sdkData, coinId) : await this.parseViaSdk(t.sdkData, coinId);
|
|
6594
|
+
if (amount <= 0n) {
|
|
6508
6595
|
logger.warn(TAG2, `Token ${t.id} has 0 balance for coinId ${coinId}`);
|
|
6509
6596
|
continue;
|
|
6510
6597
|
}
|
|
6511
|
-
pool.set(t.id, { token: t, sdkToken, amount
|
|
6598
|
+
pool.set(t.id, { token: t, sdkToken, amount });
|
|
6512
6599
|
} catch (e) {
|
|
6513
6600
|
logger.warn(TAG2, "Failed to parse token", t.id, e);
|
|
6514
6601
|
}
|
|
6515
6602
|
}
|
|
6516
6603
|
return pool;
|
|
6517
6604
|
}
|
|
6605
|
+
/** Engine path (v2): sdkData is the engine blob (hex of CBOR(TokenBlob)). */
|
|
6606
|
+
async parseViaEngine(sdkData, coinId) {
|
|
6607
|
+
const token = await this.engine.decodeToken(decodeTokenBlob(hexToBytes2(sdkData)));
|
|
6608
|
+
return { sdkToken: token, amount: this.engine.balanceOf(token, coinId) };
|
|
6609
|
+
}
|
|
6610
|
+
/** Legacy v1 path: sdkData is TXF JSON. */
|
|
6611
|
+
async parseViaSdk(sdkData, coinId) {
|
|
6612
|
+
const sdkToken = await SdkToken.fromJSON(JSON.parse(sdkData));
|
|
6613
|
+
return { sdkToken, amount: this.getTokenBalance(sdkToken, coinId) };
|
|
6614
|
+
}
|
|
6518
6615
|
/**
|
|
6519
6616
|
* SYNCHRONOUS critical section. NO await allowed anywhere in this method.
|
|
6520
6617
|
*
|
|
@@ -6925,174 +7022,6 @@ var SpendQueue = class {
|
|
|
6925
7022
|
}
|
|
6926
7023
|
};
|
|
6927
7024
|
|
|
6928
|
-
// modules/payments/NametagMinter.ts
|
|
6929
|
-
init_logger();
|
|
6930
|
-
import { Token as Token2 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
6931
|
-
import { TokenId as TokenId2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
6932
|
-
import { TokenType } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
6933
|
-
import { TokenState as TokenState2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
6934
|
-
import { MintTransactionData } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData";
|
|
6935
|
-
import { MintCommitment } from "@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment";
|
|
6936
|
-
import { HashAlgorithm as HashAlgorithm2 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
6937
|
-
import { UnmaskedPredicate as UnmaskedPredicate2 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
6938
|
-
import { waitInclusionProof as waitInclusionProof2 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
|
|
6939
|
-
import { normalizeNametag } from "@unicitylabs/nostr-js-sdk";
|
|
6940
|
-
var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
6941
|
-
var NametagMinter = class {
|
|
6942
|
-
client;
|
|
6943
|
-
trustBase;
|
|
6944
|
-
signingService;
|
|
6945
|
-
skipVerification;
|
|
6946
|
-
debug;
|
|
6947
|
-
constructor(config) {
|
|
6948
|
-
this.client = config.stateTransitionClient;
|
|
6949
|
-
this.trustBase = config.trustBase;
|
|
6950
|
-
this.signingService = config.signingService;
|
|
6951
|
-
this.skipVerification = config.skipVerification ?? false;
|
|
6952
|
-
this.debug = config.debug ?? false;
|
|
6953
|
-
}
|
|
6954
|
-
log(message, ...args) {
|
|
6955
|
-
logger.debug("NametagMinter", message, ...args);
|
|
6956
|
-
}
|
|
6957
|
-
/**
|
|
6958
|
-
* Check if a nametag is available (not already minted)
|
|
6959
|
-
*/
|
|
6960
|
-
async isNametagAvailable(nametag) {
|
|
6961
|
-
try {
|
|
6962
|
-
const stripped = nametag.startsWith("@") ? nametag.slice(1) : nametag;
|
|
6963
|
-
const cleanNametag = normalizeNametag(stripped);
|
|
6964
|
-
const nametagTokenId = await TokenId2.fromNameTag(cleanNametag);
|
|
6965
|
-
const isMinted = await this.client.isMinted(this.trustBase, nametagTokenId);
|
|
6966
|
-
return !isMinted;
|
|
6967
|
-
} catch (error) {
|
|
6968
|
-
this.log("Error checking nametag availability:", error);
|
|
6969
|
-
return false;
|
|
6970
|
-
}
|
|
6971
|
-
}
|
|
6972
|
-
/**
|
|
6973
|
-
* Mint a nametag token on-chain
|
|
6974
|
-
*
|
|
6975
|
-
* @param nametag - The nametag to mint (e.g., "alice" or "@alice")
|
|
6976
|
-
* @param ownerAddress - The owner's direct address
|
|
6977
|
-
* @returns MintNametagResult with token if successful
|
|
6978
|
-
*/
|
|
6979
|
-
async mintNametag(nametag, ownerAddress) {
|
|
6980
|
-
const stripped = nametag.startsWith("@") ? nametag.slice(1) : nametag;
|
|
6981
|
-
const cleanNametag = normalizeNametag(stripped);
|
|
6982
|
-
this.log(`Starting mint for nametag: ${cleanNametag}`);
|
|
6983
|
-
try {
|
|
6984
|
-
const nametagTokenId = await TokenId2.fromNameTag(cleanNametag);
|
|
6985
|
-
const nametagTokenType = new TokenType(
|
|
6986
|
-
Buffer.from(UNICITY_TOKEN_TYPE_HEX, "hex")
|
|
6987
|
-
);
|
|
6988
|
-
const nametagBytes = new TextEncoder().encode(cleanNametag);
|
|
6989
|
-
const pubKey = this.signingService.publicKey;
|
|
6990
|
-
const saltInput = new Uint8Array(pubKey.length + nametagBytes.length);
|
|
6991
|
-
saltInput.set(pubKey, 0);
|
|
6992
|
-
saltInput.set(nametagBytes, pubKey.length);
|
|
6993
|
-
const saltBuffer = await crypto.subtle.digest("SHA-256", saltInput);
|
|
6994
|
-
const salt = new Uint8Array(saltBuffer);
|
|
6995
|
-
this.log("Generated deterministic salt");
|
|
6996
|
-
const mintData = await MintTransactionData.createFromNametag(
|
|
6997
|
-
cleanNametag,
|
|
6998
|
-
nametagTokenType,
|
|
6999
|
-
ownerAddress,
|
|
7000
|
-
salt,
|
|
7001
|
-
ownerAddress
|
|
7002
|
-
);
|
|
7003
|
-
this.log("Created MintTransactionData");
|
|
7004
|
-
const commitment = await MintCommitment.create(mintData);
|
|
7005
|
-
this.log("Created MintCommitment");
|
|
7006
|
-
const MAX_RETRIES = 3;
|
|
7007
|
-
let submitSuccess = false;
|
|
7008
|
-
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
7009
|
-
try {
|
|
7010
|
-
this.log(`Submitting commitment (attempt ${attempt}/${MAX_RETRIES})...`);
|
|
7011
|
-
const response = await this.client.submitMintCommitment(commitment);
|
|
7012
|
-
if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
|
|
7013
|
-
this.log(`Commitment ${response.status === "REQUEST_ID_EXISTS" ? "already exists" : "submitted successfully"}`);
|
|
7014
|
-
submitSuccess = true;
|
|
7015
|
-
break;
|
|
7016
|
-
} else {
|
|
7017
|
-
this.log(`Commitment failed: ${response.status}`);
|
|
7018
|
-
if (attempt === MAX_RETRIES) {
|
|
7019
|
-
return {
|
|
7020
|
-
success: false,
|
|
7021
|
-
error: `Failed to submit commitment after ${MAX_RETRIES} attempts: ${response.status}`
|
|
7022
|
-
};
|
|
7023
|
-
}
|
|
7024
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
7025
|
-
}
|
|
7026
|
-
} catch (error) {
|
|
7027
|
-
this.log(`Attempt ${attempt} error:`, error);
|
|
7028
|
-
if (attempt === MAX_RETRIES) {
|
|
7029
|
-
return {
|
|
7030
|
-
success: false,
|
|
7031
|
-
error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`
|
|
7032
|
-
};
|
|
7033
|
-
}
|
|
7034
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
7035
|
-
}
|
|
7036
|
-
}
|
|
7037
|
-
if (!submitSuccess) {
|
|
7038
|
-
return {
|
|
7039
|
-
success: false,
|
|
7040
|
-
error: "Failed to submit commitment after retries"
|
|
7041
|
-
};
|
|
7042
|
-
}
|
|
7043
|
-
this.log("Waiting for inclusion proof...");
|
|
7044
|
-
const inclusionProof = await waitInclusionProof2(this.trustBase, this.client, commitment);
|
|
7045
|
-
this.log("Received inclusion proof");
|
|
7046
|
-
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
7047
|
-
const nametagPredicate = await UnmaskedPredicate2.create(
|
|
7048
|
-
nametagTokenId,
|
|
7049
|
-
nametagTokenType,
|
|
7050
|
-
this.signingService,
|
|
7051
|
-
HashAlgorithm2.SHA256,
|
|
7052
|
-
salt
|
|
7053
|
-
);
|
|
7054
|
-
const tokenState = new TokenState2(nametagPredicate, null);
|
|
7055
|
-
let token;
|
|
7056
|
-
if (this.skipVerification) {
|
|
7057
|
-
this.log("Creating token WITHOUT verification (dev mode)");
|
|
7058
|
-
const tokenJson = {
|
|
7059
|
-
version: "2.0",
|
|
7060
|
-
state: tokenState.toJSON(),
|
|
7061
|
-
genesis: genesisTransaction.toJSON(),
|
|
7062
|
-
transactions: [],
|
|
7063
|
-
nametags: []
|
|
7064
|
-
};
|
|
7065
|
-
token = await Token2.fromJSON(tokenJson);
|
|
7066
|
-
} else {
|
|
7067
|
-
token = await Token2.mint(
|
|
7068
|
-
this.trustBase,
|
|
7069
|
-
tokenState,
|
|
7070
|
-
genesisTransaction
|
|
7071
|
-
);
|
|
7072
|
-
}
|
|
7073
|
-
this.log(`Nametag minted successfully: ${cleanNametag}`);
|
|
7074
|
-
const nametagData = {
|
|
7075
|
-
name: cleanNametag,
|
|
7076
|
-
token: token.toJSON(),
|
|
7077
|
-
timestamp: Date.now(),
|
|
7078
|
-
format: "txf",
|
|
7079
|
-
version: "2.0"
|
|
7080
|
-
};
|
|
7081
|
-
return {
|
|
7082
|
-
success: true,
|
|
7083
|
-
token,
|
|
7084
|
-
nametagData
|
|
7085
|
-
};
|
|
7086
|
-
} catch (error) {
|
|
7087
|
-
this.log("Minting failed:", error);
|
|
7088
|
-
return {
|
|
7089
|
-
success: false,
|
|
7090
|
-
error: error instanceof Error ? error.message : String(error)
|
|
7091
|
-
};
|
|
7092
|
-
}
|
|
7093
|
-
}
|
|
7094
|
-
};
|
|
7095
|
-
|
|
7096
7025
|
// modules/payments/PaymentsModule.ts
|
|
7097
7026
|
init_constants();
|
|
7098
7027
|
|
|
@@ -7675,6 +7604,15 @@ function txfToToken(tokenId, txf) {
|
|
|
7675
7604
|
sdkData: JSON.stringify(txf)
|
|
7676
7605
|
};
|
|
7677
7606
|
}
|
|
7607
|
+
function isV2TokenBlob(sdkData) {
|
|
7608
|
+
return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
7609
|
+
}
|
|
7610
|
+
function v2TokenId(token) {
|
|
7611
|
+
return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
|
|
7612
|
+
}
|
|
7613
|
+
function isV2TokenEntry(entry) {
|
|
7614
|
+
return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
|
|
7615
|
+
}
|
|
7678
7616
|
async function buildTxfStorageData(tokens, meta, options) {
|
|
7679
7617
|
const storageData = {
|
|
7680
7618
|
_meta: {
|
|
@@ -7705,6 +7643,8 @@ async function buildTxfStorageData(tokens, meta, options) {
|
|
|
7705
7643
|
if (txf) {
|
|
7706
7644
|
const actualTokenId = txf.genesis.data.tokenId;
|
|
7707
7645
|
storageData[keyFromTokenId(actualTokenId)] = txf;
|
|
7646
|
+
} else if (isV2TokenBlob(token.sdkData)) {
|
|
7647
|
+
storageData[keyFromTokenId(v2TokenId(token))] = token;
|
|
7708
7648
|
}
|
|
7709
7649
|
}
|
|
7710
7650
|
if (options?.archivedTokens && options.archivedTokens.size > 0) {
|
|
@@ -7802,6 +7742,8 @@ function parseTxfStorageData(data) {
|
|
|
7802
7742
|
if (txfToken?.genesis?.data?.tokenId) {
|
|
7803
7743
|
const token = txfToToken(tokenId, txfToken);
|
|
7804
7744
|
result.tokens.push(token);
|
|
7745
|
+
} else if (isV2TokenEntry(storageData[key])) {
|
|
7746
|
+
result.tokens.push(storageData[key]);
|
|
7805
7747
|
}
|
|
7806
7748
|
} catch (err) {
|
|
7807
7749
|
result.validationErrors.push(`Token ${tokenId}: ${err}`);
|
|
@@ -8026,12 +7968,12 @@ init_logger();
|
|
|
8026
7968
|
init_errors();
|
|
8027
7969
|
import { Token as Token3 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
8028
7970
|
import { TokenId as TokenId3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
8029
|
-
import { TokenState as
|
|
7971
|
+
import { TokenState as TokenState2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
8030
7972
|
import { CoinId as CoinId3 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
8031
7973
|
import { TokenCoinData as TokenCoinData2 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData";
|
|
8032
7974
|
import { TokenSplitBuilder as TokenSplitBuilder2 } from "@unicitylabs/state-transition-sdk/lib/transaction/split/TokenSplitBuilder";
|
|
8033
7975
|
import { HashAlgorithm as HashAlgorithm3 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
8034
|
-
import { UnmaskedPredicate as
|
|
7976
|
+
import { UnmaskedPredicate as UnmaskedPredicate2 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
8035
7977
|
import { UnmaskedPredicateReference as UnmaskedPredicateReference2 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference";
|
|
8036
7978
|
import { TransferCommitment as TransferCommitment2 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment";
|
|
8037
7979
|
import { waitInclusionProof as waitInclusionProof3 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
|
|
@@ -8153,14 +8095,14 @@ var InstantSplitExecutor = class {
|
|
|
8153
8095
|
options?.message
|
|
8154
8096
|
// on-chain message (invoice memo bytes, or null)
|
|
8155
8097
|
);
|
|
8156
|
-
const mintedPredicate = await
|
|
8098
|
+
const mintedPredicate = await UnmaskedPredicate2.create(
|
|
8157
8099
|
recipientTokenId,
|
|
8158
8100
|
tokenToSplit.type,
|
|
8159
8101
|
this.signingService,
|
|
8160
8102
|
HashAlgorithm3.SHA256,
|
|
8161
8103
|
recipientSalt
|
|
8162
8104
|
);
|
|
8163
|
-
const mintedState = new
|
|
8105
|
+
const mintedState = new TokenState2(mintedPredicate, null);
|
|
8164
8106
|
logger.debug("InstantSplit", "Step 5: Packaging V5 bundle...");
|
|
8165
8107
|
const senderPubkey = toHex2(this.signingService.publicKey);
|
|
8166
8108
|
let nametagTokenJson;
|
|
@@ -8275,14 +8217,14 @@ var InstantSplitExecutor = class {
|
|
|
8275
8217
|
* It does NOT need the genesis transaction or mint proof.
|
|
8276
8218
|
*/
|
|
8277
8219
|
async createTransferCommitmentFromMintData(mintData, recipientAddress, transferSalt, signingService, nametagTokens, message) {
|
|
8278
|
-
const predicate = await
|
|
8220
|
+
const predicate = await UnmaskedPredicate2.create(
|
|
8279
8221
|
mintData.tokenId,
|
|
8280
8222
|
mintData.tokenType,
|
|
8281
8223
|
signingService,
|
|
8282
8224
|
HashAlgorithm3.SHA256,
|
|
8283
8225
|
mintData.salt
|
|
8284
8226
|
);
|
|
8285
|
-
const state = new
|
|
8227
|
+
const state = new TokenState2(predicate, null);
|
|
8286
8228
|
const minimalToken = {
|
|
8287
8229
|
state,
|
|
8288
8230
|
nametagTokens: nametagTokens || [],
|
|
@@ -8343,14 +8285,14 @@ var InstantSplitExecutor = class {
|
|
|
8343
8285
|
message: `Mint proof received in ${proofDuration.toFixed(0)}ms`
|
|
8344
8286
|
});
|
|
8345
8287
|
const mintTransaction = senderMintCommitment.toTransaction(senderMintProof);
|
|
8346
|
-
const predicate = await
|
|
8288
|
+
const predicate = await UnmaskedPredicate2.create(
|
|
8347
8289
|
context.senderTokenId,
|
|
8348
8290
|
context.tokenType,
|
|
8349
8291
|
context.signingService,
|
|
8350
8292
|
HashAlgorithm3.SHA256,
|
|
8351
8293
|
context.senderSalt
|
|
8352
8294
|
);
|
|
8353
|
-
const state = new
|
|
8295
|
+
const state = new TokenState2(predicate, null);
|
|
8354
8296
|
const changeToken = await Token3.mint(this.trustBase, state, mintTransaction);
|
|
8355
8297
|
if (!this.devMode) {
|
|
8356
8298
|
const verification = await changeToken.verify(this.trustBase);
|
|
@@ -8430,14 +8372,14 @@ var InstantSplitExecutor = class {
|
|
|
8430
8372
|
init_logger();
|
|
8431
8373
|
init_errors();
|
|
8432
8374
|
import { Token as Token4 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
8433
|
-
import { TokenState as
|
|
8375
|
+
import { TokenState as TokenState3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
8434
8376
|
import { TokenType as TokenType2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
8435
8377
|
import { HashAlgorithm as HashAlgorithm4 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
8436
|
-
import { UnmaskedPredicate as
|
|
8378
|
+
import { UnmaskedPredicate as UnmaskedPredicate3 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
8437
8379
|
import { TransferCommitment as TransferCommitment3 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment";
|
|
8438
|
-
import { TransferTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction";
|
|
8439
|
-
import { MintCommitment
|
|
8440
|
-
import { MintTransactionData
|
|
8380
|
+
import { TransferTransaction as TransferTransaction2 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction";
|
|
8381
|
+
import { MintCommitment } from "@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment";
|
|
8382
|
+
import { MintTransactionData } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData";
|
|
8441
8383
|
import { waitInclusionProof as waitInclusionProof4 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
|
|
8442
8384
|
|
|
8443
8385
|
// types/instant-split.ts
|
|
@@ -8530,11 +8472,11 @@ var InstantSplitProcessor = class {
|
|
|
8530
8472
|
logger.warn("InstantSplit", "Sender pubkey mismatch (non-fatal)");
|
|
8531
8473
|
}
|
|
8532
8474
|
const burnTxJson = JSON.parse(bundle.burnTransaction);
|
|
8533
|
-
const _burnTransaction = await
|
|
8475
|
+
const _burnTransaction = await TransferTransaction2.fromJSON(burnTxJson);
|
|
8534
8476
|
logger.debug("InstantSplit", "Burn transaction validated");
|
|
8535
8477
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
8536
|
-
const mintData = await
|
|
8537
|
-
const mintCommitment = await
|
|
8478
|
+
const mintData = await MintTransactionData.fromJSON(mintDataJson);
|
|
8479
|
+
const mintCommitment = await MintCommitment.create(mintData);
|
|
8538
8480
|
logger.debug("InstantSplit", "Mint commitment recreated");
|
|
8539
8481
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
8540
8482
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
@@ -8566,14 +8508,14 @@ var InstantSplitProcessor = class {
|
|
|
8566
8508
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
8567
8509
|
logger.debug("InstantSplit", "Transfer proof received");
|
|
8568
8510
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
8569
|
-
const finalRecipientPredicate = await
|
|
8511
|
+
const finalRecipientPredicate = await UnmaskedPredicate3.create(
|
|
8570
8512
|
mintData.tokenId,
|
|
8571
8513
|
tokenType,
|
|
8572
8514
|
signingService,
|
|
8573
8515
|
HashAlgorithm4.SHA256,
|
|
8574
8516
|
transferSalt
|
|
8575
8517
|
);
|
|
8576
|
-
const finalRecipientState = new
|
|
8518
|
+
const finalRecipientState = new TokenState3(finalRecipientPredicate, null);
|
|
8577
8519
|
logger.debug("InstantSplit", "Final recipient state created");
|
|
8578
8520
|
let nametagTokens = [];
|
|
8579
8521
|
const recipientAddressStr = bundle.recipientAddressJson;
|
|
@@ -8680,8 +8622,8 @@ var InstantSplitProcessor = class {
|
|
|
8680
8622
|
await this.waitInclusionProofWithDevBypass(burnCommitment, options?.proofTimeoutMs);
|
|
8681
8623
|
logger.debug("InstantSplit", "V4: Burn proof received");
|
|
8682
8624
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
8683
|
-
const mintData = await
|
|
8684
|
-
const mintCommitment = await
|
|
8625
|
+
const mintData = await MintTransactionData.fromJSON(mintDataJson);
|
|
8626
|
+
const mintCommitment = await MintCommitment.create(mintData);
|
|
8685
8627
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
8686
8628
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
8687
8629
|
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
@@ -8694,14 +8636,14 @@ var InstantSplitProcessor = class {
|
|
|
8694
8636
|
logger.debug("InstantSplit", "V4: Mint proof received");
|
|
8695
8637
|
const tokenType = new TokenType2(fromHex3(bundle.tokenTypeHex));
|
|
8696
8638
|
const recipientSalt = fromHex3(bundle.recipientSaltHex);
|
|
8697
|
-
const recipientPredicate = await
|
|
8639
|
+
const recipientPredicate = await UnmaskedPredicate3.create(
|
|
8698
8640
|
mintData.tokenId,
|
|
8699
8641
|
tokenType,
|
|
8700
8642
|
signingService,
|
|
8701
8643
|
HashAlgorithm4.SHA256,
|
|
8702
8644
|
recipientSalt
|
|
8703
8645
|
);
|
|
8704
|
-
const recipientState = new
|
|
8646
|
+
const recipientState = new TokenState3(recipientPredicate, null);
|
|
8705
8647
|
const tokenJson = {
|
|
8706
8648
|
version: "2.0",
|
|
8707
8649
|
state: recipientState.toJSON(),
|
|
@@ -8724,14 +8666,14 @@ var InstantSplitProcessor = class {
|
|
|
8724
8666
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
8725
8667
|
logger.debug("InstantSplit", "V4: Transfer proof received");
|
|
8726
8668
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
8727
|
-
const finalPredicate = await
|
|
8669
|
+
const finalPredicate = await UnmaskedPredicate3.create(
|
|
8728
8670
|
mintData.tokenId,
|
|
8729
8671
|
tokenType,
|
|
8730
8672
|
signingService,
|
|
8731
8673
|
HashAlgorithm4.SHA256,
|
|
8732
8674
|
transferSalt
|
|
8733
8675
|
);
|
|
8734
|
-
const finalState = new
|
|
8676
|
+
const finalState = new TokenState3(finalPredicate, null);
|
|
8735
8677
|
const finalTokenJson = mintedToken.toJSON();
|
|
8736
8678
|
finalTokenJson.state = finalState.toJSON();
|
|
8737
8679
|
finalTokenJson.transactions = [transferTransaction.toJSON()];
|
|
@@ -8782,17 +8724,17 @@ var InstantSplitProcessor = class {
|
|
|
8782
8724
|
import { Token as SdkToken2 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
8783
8725
|
import { CoinId as CoinId4 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
8784
8726
|
import { TransferCommitment as TransferCommitment4 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment";
|
|
8785
|
-
import { TransferTransaction as
|
|
8786
|
-
import { SigningService } from "@unicitylabs/state-transition-sdk/lib/sign/SigningService";
|
|
8727
|
+
import { TransferTransaction as TransferTransaction3 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction";
|
|
8728
|
+
import { SigningService as SigningService2 } from "@unicitylabs/state-transition-sdk/lib/sign/SigningService";
|
|
8787
8729
|
import { AddressScheme } from "@unicitylabs/state-transition-sdk/lib/address/AddressScheme";
|
|
8788
|
-
import { UnmaskedPredicate as
|
|
8789
|
-
import { TokenState as
|
|
8730
|
+
import { UnmaskedPredicate as UnmaskedPredicate4 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
8731
|
+
import { TokenState as TokenState4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
8790
8732
|
import { HashAlgorithm as HashAlgorithm5 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
8791
8733
|
import { TokenType as TokenType3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
8792
|
-
import { MintCommitment as
|
|
8793
|
-
import { MintTransactionData as
|
|
8734
|
+
import { MintCommitment as MintCommitment2 } from "@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment";
|
|
8735
|
+
import { MintTransactionData as MintTransactionData2 } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData";
|
|
8794
8736
|
import { waitInclusionProof as waitInclusionProof5 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
|
|
8795
|
-
import { InclusionProof } from "@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof";
|
|
8737
|
+
import { InclusionProof as InclusionProof2 } from "@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof";
|
|
8796
8738
|
function computeHistoryDedupKey(type, tokenId, transferId) {
|
|
8797
8739
|
if (type === "SENT" && transferId) return `${type}_transfer_${transferId}`;
|
|
8798
8740
|
if (tokenId) return `${type}_${tokenId}`;
|
|
@@ -8813,7 +8755,7 @@ function enrichWithRegistry(info) {
|
|
|
8813
8755
|
}
|
|
8814
8756
|
return info;
|
|
8815
8757
|
}
|
|
8816
|
-
async function parseTokenInfo(tokenData) {
|
|
8758
|
+
async function parseTokenInfo(tokenData, engine) {
|
|
8817
8759
|
const defaultInfo = {
|
|
8818
8760
|
coinId: "ALPHA",
|
|
8819
8761
|
symbol: "ALPHA",
|
|
@@ -8821,6 +8763,25 @@ async function parseTokenInfo(tokenData) {
|
|
|
8821
8763
|
decimals: 0,
|
|
8822
8764
|
amount: "0"
|
|
8823
8765
|
};
|
|
8766
|
+
if (engine && typeof tokenData === "string" && looksLikeTokenBlob(tokenData)) {
|
|
8767
|
+
try {
|
|
8768
|
+
const token = await engine.decodeToken(decodeTokenBlob(hexToBytes2(tokenData)));
|
|
8769
|
+
const first = engine.readValue(token)?.assets[0];
|
|
8770
|
+
if (first) {
|
|
8771
|
+
return enrichWithRegistry({
|
|
8772
|
+
coinId: first.coinId,
|
|
8773
|
+
symbol: first.coinId.slice(0, 8),
|
|
8774
|
+
name: `Token ${first.coinId.slice(0, 8)}`,
|
|
8775
|
+
decimals: 0,
|
|
8776
|
+
amount: String(first.amount),
|
|
8777
|
+
tokenId: engine.tokenId(token)
|
|
8778
|
+
});
|
|
8779
|
+
}
|
|
8780
|
+
return { ...defaultInfo, tokenId: engine.tokenId(token) };
|
|
8781
|
+
} catch (error) {
|
|
8782
|
+
logger.warn("Payments", "Failed to parse token info via engine:", error);
|
|
8783
|
+
}
|
|
8784
|
+
}
|
|
8824
8785
|
try {
|
|
8825
8786
|
const data = typeof tokenData === "string" ? JSON.parse(tokenData) : tokenData;
|
|
8826
8787
|
try {
|
|
@@ -8959,27 +8920,41 @@ async function parseTokenInfo(tokenData) {
|
|
|
8959
8920
|
}
|
|
8960
8921
|
var sdkDataCache = /* @__PURE__ */ new Map();
|
|
8961
8922
|
var SDK_DATA_CACHE_MAX = 2e3;
|
|
8923
|
+
function looksLikeTokenBlob(sdkData) {
|
|
8924
|
+
return sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
8925
|
+
}
|
|
8926
|
+
function tryParseBlobKeys(sdkData) {
|
|
8927
|
+
try {
|
|
8928
|
+
const blob = decodeTokenBlob(hexToBytes2(sdkData));
|
|
8929
|
+
return { tokenId: blob.tokenId, stateHash: sha2562(bytesToHex3(blob.token), "hex") };
|
|
8930
|
+
} catch {
|
|
8931
|
+
return null;
|
|
8932
|
+
}
|
|
8933
|
+
}
|
|
8962
8934
|
function parseSdkDataCached(sdkData) {
|
|
8963
8935
|
const cached = sdkDataCache.get(sdkData);
|
|
8964
8936
|
if (cached) return cached;
|
|
8965
|
-
let
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8937
|
+
let entry = looksLikeTokenBlob(sdkData) ? tryParseBlobKeys(sdkData) : null;
|
|
8938
|
+
if (!entry) {
|
|
8939
|
+
let tokenId = null;
|
|
8940
|
+
let stateHash = "";
|
|
8941
|
+
try {
|
|
8942
|
+
const txf = JSON.parse(sdkData);
|
|
8943
|
+
tokenId = txf.genesis?.data?.tokenId || null;
|
|
8944
|
+
stateHash = getCurrentStateHash(txf) || "";
|
|
8945
|
+
if (!stateHash) {
|
|
8946
|
+
if (txf.state?.hash) {
|
|
8947
|
+
stateHash = txf.state.hash;
|
|
8948
|
+
} else if (txf.stateHash) {
|
|
8949
|
+
stateHash = txf.stateHash;
|
|
8950
|
+
} else if (txf.currentStateHash) {
|
|
8951
|
+
stateHash = txf.currentStateHash;
|
|
8952
|
+
}
|
|
8978
8953
|
}
|
|
8954
|
+
} catch {
|
|
8979
8955
|
}
|
|
8980
|
-
|
|
8956
|
+
entry = { tokenId, stateHash };
|
|
8981
8957
|
}
|
|
8982
|
-
const entry = { tokenId, stateHash };
|
|
8983
8958
|
if (sdkDataCache.size >= SDK_DATA_CACHE_MAX) {
|
|
8984
8959
|
sdkDataCache.clear();
|
|
8985
8960
|
}
|
|
@@ -9256,6 +9231,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
9256
9231
|
);
|
|
9257
9232
|
this.deps = deps;
|
|
9258
9233
|
this.priceProvider = deps.price ?? null;
|
|
9234
|
+
this.spendPlanner.setEngine(deps.tokenEngine);
|
|
9259
9235
|
if (this.l1) {
|
|
9260
9236
|
this.l1.initialize({
|
|
9261
9237
|
identity: deps.identity,
|
|
@@ -9480,7 +9456,59 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
9480
9456
|
request.invoiceRefundAddress,
|
|
9481
9457
|
request.invoiceContact
|
|
9482
9458
|
);
|
|
9483
|
-
if (
|
|
9459
|
+
if (this.deps?.tokenEngine && peerInfo?.chainPubkey) {
|
|
9460
|
+
const engine = this.deps.tokenEngine;
|
|
9461
|
+
const recipientChainPubkey = hexToBytes2(peerInfo.chainPubkey);
|
|
9462
|
+
const memoData = onChainMessage ?? void 0;
|
|
9463
|
+
const handToRecipient = async (finished) => {
|
|
9464
|
+
const tokenBlob = bytesToHex3(encodeTokenBlob(engine.encodeToken(finished)));
|
|
9465
|
+
await this.deps.transport.sendTokenTransfer(recipientPubkey, {
|
|
9466
|
+
type: "V2_TRANSFER",
|
|
9467
|
+
version: "2.0",
|
|
9468
|
+
tokenBlob,
|
|
9469
|
+
memo: request.memo
|
|
9470
|
+
});
|
|
9471
|
+
};
|
|
9472
|
+
for (const tw of splitPlan.tokensToTransferDirectly) {
|
|
9473
|
+
const finished = await engine.transfer({
|
|
9474
|
+
token: tw.sdkToken,
|
|
9475
|
+
recipientPubkey: recipientChainPubkey,
|
|
9476
|
+
data: memoData
|
|
9477
|
+
});
|
|
9478
|
+
await handToRecipient(finished);
|
|
9479
|
+
result.tokenTransfers.push({ sourceTokenId: tw.uiToken.id, method: "direct" });
|
|
9480
|
+
await this.removeToken(tw.uiToken.id, result.id);
|
|
9481
|
+
}
|
|
9482
|
+
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
9483
|
+
const selfChainPubkey = hexToBytes2(this.deps.identity.chainPubkey);
|
|
9484
|
+
const { outputs } = await engine.split({
|
|
9485
|
+
token: splitPlan.tokenToSplit.sdkToken,
|
|
9486
|
+
outputs: [
|
|
9487
|
+
{ recipientPubkey: recipientChainPubkey, coinId: request.coinId, amount: splitPlan.splitAmount, data: memoData },
|
|
9488
|
+
{ recipientPubkey: selfChainPubkey, coinId: request.coinId, amount: splitPlan.remainderAmount }
|
|
9489
|
+
]
|
|
9490
|
+
});
|
|
9491
|
+
await handToRecipient(outputs[0]);
|
|
9492
|
+
const changeToken = outputs[1];
|
|
9493
|
+
const changeBlob = bytesToHex3(encodeTokenBlob(engine.encodeToken(changeToken)));
|
|
9494
|
+
const changeInfo = await parseTokenInfo(changeBlob, engine);
|
|
9495
|
+
const registry = TokenRegistry.getInstance();
|
|
9496
|
+
await this.addToken({
|
|
9497
|
+
id: `v2_${engine.tokenId(changeToken)}`,
|
|
9498
|
+
coinId: changeInfo.coinId,
|
|
9499
|
+
symbol: registry.getSymbol(changeInfo.coinId) || changeInfo.symbol,
|
|
9500
|
+
name: registry.getName(changeInfo.coinId) || changeInfo.name,
|
|
9501
|
+
decimals: registry.getDecimals(changeInfo.coinId) ?? changeInfo.decimals,
|
|
9502
|
+
amount: changeInfo.amount,
|
|
9503
|
+
status: "confirmed",
|
|
9504
|
+
createdAt: Date.now(),
|
|
9505
|
+
updatedAt: Date.now(),
|
|
9506
|
+
sdkData: changeBlob
|
|
9507
|
+
});
|
|
9508
|
+
result.tokenTransfers.push({ sourceTokenId: splitPlan.tokenToSplit.uiToken.id, method: "split" });
|
|
9509
|
+
await this.removeToken(splitPlan.tokenToSplit.uiToken.id, result.id);
|
|
9510
|
+
}
|
|
9511
|
+
} else if (transferMode === "conservative") {
|
|
9484
9512
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
9485
9513
|
logger.debug("Payments", "Executing conservative split...");
|
|
9486
9514
|
const splitExecutor = new TokenSplitExecutor({
|
|
@@ -10076,7 +10104,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
10076
10104
|
* because bundle-level dedup protects against replays, and split children share genesis IDs.
|
|
10077
10105
|
*/
|
|
10078
10106
|
async saveCommitmentOnlyToken(sourceTokenInput, commitmentInput, senderPubkey, deferPersistence = false, skipGenesisDedup = false) {
|
|
10079
|
-
const tokenInfo = await parseTokenInfo(sourceTokenInput);
|
|
10107
|
+
const tokenInfo = await parseTokenInfo(sourceTokenInput, this.deps?.tokenEngine);
|
|
10080
10108
|
const sdkData = typeof sourceTokenInput === "string" ? sourceTokenInput : JSON.stringify(sourceTokenInput);
|
|
10081
10109
|
const nostrTokenId = extractTokenIdFromSdkData(sdkData);
|
|
10082
10110
|
const nostrStateHash = extractStateHashFromSdkData(sdkData);
|
|
@@ -11168,8 +11196,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11168
11196
|
if (pending2.stage === "RECEIVED") {
|
|
11169
11197
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: RECEIVED \u2192 submitting mint commitment...`);
|
|
11170
11198
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11171
|
-
const mintData = await
|
|
11172
|
-
const mintCommitment = await
|
|
11199
|
+
const mintData = await MintTransactionData2.fromJSON(mintDataJson);
|
|
11200
|
+
const mintCommitment = await MintCommitment2.create(mintData);
|
|
11173
11201
|
const mintResponse = await stClient.submitMintCommitment(mintCommitment);
|
|
11174
11202
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint response status=${mintResponse.status}`);
|
|
11175
11203
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
@@ -11181,8 +11209,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11181
11209
|
if (pending2.stage === "MINT_SUBMITTED") {
|
|
11182
11210
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_SUBMITTED \u2192 checking mint proof...`);
|
|
11183
11211
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11184
|
-
const mintData = await
|
|
11185
|
-
const mintCommitment = await
|
|
11212
|
+
const mintData = await MintTransactionData2.fromJSON(mintDataJson);
|
|
11213
|
+
const mintCommitment = await MintCommitment2.create(mintData);
|
|
11186
11214
|
const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);
|
|
11187
11215
|
if (!proof) {
|
|
11188
11216
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof not yet available, staying MINT_SUBMITTED`);
|
|
@@ -11279,10 +11307,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11279
11307
|
*/
|
|
11280
11308
|
async finalizeFromV5Bundle(bundle, pending2, signingService, stClient, trustBase) {
|
|
11281
11309
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11282
|
-
const mintData = await
|
|
11283
|
-
const mintCommitment = await
|
|
11310
|
+
const mintData = await MintTransactionData2.fromJSON(mintDataJson);
|
|
11311
|
+
const mintCommitment = await MintCommitment2.create(mintData);
|
|
11284
11312
|
const mintProofJson = JSON.parse(pending2.mintProofJson);
|
|
11285
|
-
const mintProof =
|
|
11313
|
+
const mintProof = InclusionProof2.fromJSON(mintProofJson);
|
|
11286
11314
|
const mintTransaction = mintCommitment.toTransaction(mintProof);
|
|
11287
11315
|
const tokenType = new TokenType3(fromHex4(bundle.tokenTypeHex));
|
|
11288
11316
|
const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);
|
|
@@ -11299,14 +11327,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11299
11327
|
const transferProof = await waitInclusionProof5(trustBase, stClient, transferCommitment);
|
|
11300
11328
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
11301
11329
|
const transferSalt = fromHex4(bundle.transferSaltHex);
|
|
11302
|
-
const recipientPredicate = await
|
|
11330
|
+
const recipientPredicate = await UnmaskedPredicate4.create(
|
|
11303
11331
|
mintData.tokenId,
|
|
11304
11332
|
tokenType,
|
|
11305
11333
|
signingService,
|
|
11306
11334
|
HashAlgorithm5.SHA256,
|
|
11307
11335
|
transferSalt
|
|
11308
11336
|
);
|
|
11309
|
-
const recipientState = new
|
|
11337
|
+
const recipientState = new TokenState4(recipientPredicate, null);
|
|
11310
11338
|
let nametagTokens = [];
|
|
11311
11339
|
const recipientAddressStr = bundle.recipientAddressJson;
|
|
11312
11340
|
if (recipientAddressStr.startsWith("PROXY://")) {
|
|
@@ -12043,68 +12071,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12043
12071
|
}
|
|
12044
12072
|
}
|
|
12045
12073
|
}
|
|
12046
|
-
/**
|
|
12047
|
-
* Mint a nametag token on-chain (like Sphere wallet and lottery)
|
|
12048
|
-
* This creates the nametag token required for receiving tokens via PROXY addresses
|
|
12049
|
-
*
|
|
12050
|
-
* @param nametag - The nametag to mint (e.g., "alice" or "@alice")
|
|
12051
|
-
* @returns MintNametagResult with success status and token if successful
|
|
12052
|
-
*/
|
|
12053
|
-
async mintNametag(nametag) {
|
|
12054
|
-
this.ensureInitialized();
|
|
12055
|
-
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
12056
|
-
if (!stClient) {
|
|
12057
|
-
return {
|
|
12058
|
-
success: false,
|
|
12059
|
-
error: "State transition client not available. Oracle provider must implement getStateTransitionClient()"
|
|
12060
|
-
};
|
|
12061
|
-
}
|
|
12062
|
-
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
12063
|
-
if (!trustBase) {
|
|
12064
|
-
return {
|
|
12065
|
-
success: false,
|
|
12066
|
-
error: "Trust base not available. Oracle provider must implement getTrustBase()"
|
|
12067
|
-
};
|
|
12068
|
-
}
|
|
12069
|
-
try {
|
|
12070
|
-
const signingService = await this.createSigningService();
|
|
12071
|
-
const { UnmaskedPredicateReference: UnmaskedPredicateReference4 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12072
|
-
const { TokenType: TokenType6 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
12073
|
-
const UNICITY_TOKEN_TYPE_HEX3 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
12074
|
-
const tokenType = new TokenType6(Buffer.from(UNICITY_TOKEN_TYPE_HEX3, "hex"));
|
|
12075
|
-
const addressRef = await UnmaskedPredicateReference4.create(
|
|
12076
|
-
tokenType,
|
|
12077
|
-
signingService.algorithm,
|
|
12078
|
-
signingService.publicKey,
|
|
12079
|
-
HashAlgorithm5.SHA256
|
|
12080
|
-
);
|
|
12081
|
-
const ownerAddress = await addressRef.toAddress();
|
|
12082
|
-
const minter = new NametagMinter({
|
|
12083
|
-
stateTransitionClient: stClient,
|
|
12084
|
-
trustBase,
|
|
12085
|
-
signingService,
|
|
12086
|
-
debug: this.moduleConfig.debug
|
|
12087
|
-
});
|
|
12088
|
-
const result = await minter.mintNametag(nametag, ownerAddress);
|
|
12089
|
-
if (result.success && result.nametagData) {
|
|
12090
|
-
await this.setNametag(result.nametagData);
|
|
12091
|
-
logger.debug("Payments", `Unicity ID minted and saved: ${result.nametagData.name}`);
|
|
12092
|
-
this.deps.emitEvent("nametag:registered", {
|
|
12093
|
-
nametag: result.nametagData.name,
|
|
12094
|
-
addressIndex: 0
|
|
12095
|
-
// Primary address
|
|
12096
|
-
});
|
|
12097
|
-
}
|
|
12098
|
-
return result;
|
|
12099
|
-
} catch (error) {
|
|
12100
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
12101
|
-
logger.debug("Payments", "mintNametag failed:", errorMsg);
|
|
12102
|
-
return {
|
|
12103
|
-
success: false,
|
|
12104
|
-
error: errorMsg
|
|
12105
|
-
};
|
|
12106
|
-
}
|
|
12107
|
-
}
|
|
12108
12074
|
/**
|
|
12109
12075
|
* Mint a fungible token directly to this wallet (genesis mint).
|
|
12110
12076
|
*
|
|
@@ -12145,18 +12111,18 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12145
12111
|
}
|
|
12146
12112
|
try {
|
|
12147
12113
|
const signingService = await this.createSigningService();
|
|
12148
|
-
const { TokenId:
|
|
12114
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
12149
12115
|
const { TokenCoinData: TokenCoinData3 } = await import("@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData");
|
|
12150
|
-
const { UnmaskedPredicateReference:
|
|
12116
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12151
12117
|
const tokenTypeBytes = fromHex4("f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509");
|
|
12152
12118
|
const tokenType = new TokenType3(tokenTypeBytes);
|
|
12153
12119
|
const tokenIdBytes = new Uint8Array(32);
|
|
12154
12120
|
crypto.getRandomValues(tokenIdBytes);
|
|
12155
|
-
const tokenId = new
|
|
12121
|
+
const tokenId = new TokenId4(tokenIdBytes);
|
|
12156
12122
|
const coinIdBytes = fromHex4(coinIdHex);
|
|
12157
12123
|
const coinId = new CoinId4(coinIdBytes);
|
|
12158
12124
|
const coinData = TokenCoinData3.create([[coinId, amount]]);
|
|
12159
|
-
const addressRef = await
|
|
12125
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
12160
12126
|
tokenType,
|
|
12161
12127
|
signingService.algorithm,
|
|
12162
12128
|
signingService.publicKey,
|
|
@@ -12165,7 +12131,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12165
12131
|
const ownerAddress = await addressRef.toAddress();
|
|
12166
12132
|
const salt = new Uint8Array(32);
|
|
12167
12133
|
crypto.getRandomValues(salt);
|
|
12168
|
-
const mintData = await
|
|
12134
|
+
const mintData = await MintTransactionData2.create(
|
|
12169
12135
|
tokenId,
|
|
12170
12136
|
tokenType,
|
|
12171
12137
|
null,
|
|
@@ -12180,7 +12146,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12180
12146
|
null
|
|
12181
12147
|
// reason: null (genesis, no burn predecessor)
|
|
12182
12148
|
);
|
|
12183
|
-
const commitment = await
|
|
12149
|
+
const commitment = await MintCommitment2.create(mintData);
|
|
12184
12150
|
const MAX_RETRIES = 3;
|
|
12185
12151
|
let lastStatus;
|
|
12186
12152
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
@@ -12197,14 +12163,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12197
12163
|
}
|
|
12198
12164
|
const inclusionProof = await waitInclusionProof5(trustBase, stClient, commitment);
|
|
12199
12165
|
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
12200
|
-
const predicate = await
|
|
12166
|
+
const predicate = await UnmaskedPredicate4.create(
|
|
12201
12167
|
tokenId,
|
|
12202
12168
|
tokenType,
|
|
12203
12169
|
signingService,
|
|
12204
12170
|
HashAlgorithm5.SHA256,
|
|
12205
12171
|
salt
|
|
12206
12172
|
);
|
|
12207
|
-
const tokenState = new
|
|
12173
|
+
const tokenState = new TokenState4(predicate, null);
|
|
12208
12174
|
const sdkToken = await SdkToken2.mint(trustBase, tokenState, genesisTransaction);
|
|
12209
12175
|
const tokenIdHex = tokenId.toJSON();
|
|
12210
12176
|
const symbol = this.getCoinSymbol(coinIdHex);
|
|
@@ -12231,29 +12197,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12231
12197
|
return { success: false, error: `Local mint failed: ${msg}` };
|
|
12232
12198
|
}
|
|
12233
12199
|
}
|
|
12234
|
-
/**
|
|
12235
|
-
* Check if a nametag is available for minting
|
|
12236
|
-
* @param nametag - The nametag to check (e.g., "alice" or "@alice")
|
|
12237
|
-
*/
|
|
12238
|
-
async isNametagAvailable(nametag) {
|
|
12239
|
-
this.ensureInitialized();
|
|
12240
|
-
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
12241
|
-
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
12242
|
-
if (!stClient || !trustBase) {
|
|
12243
|
-
return false;
|
|
12244
|
-
}
|
|
12245
|
-
try {
|
|
12246
|
-
const signingService = await this.createSigningService();
|
|
12247
|
-
const minter = new NametagMinter({
|
|
12248
|
-
stateTransitionClient: stClient,
|
|
12249
|
-
trustBase,
|
|
12250
|
-
signingService
|
|
12251
|
-
});
|
|
12252
|
-
return await minter.isNametagAvailable(nametag);
|
|
12253
|
-
} catch {
|
|
12254
|
-
return false;
|
|
12255
|
-
}
|
|
12256
|
-
}
|
|
12257
12200
|
// ===========================================================================
|
|
12258
12201
|
// Public API - Sync & Validate
|
|
12259
12202
|
// ===========================================================================
|
|
@@ -12569,7 +12512,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12569
12512
|
const privateKeyBytes = new Uint8Array(
|
|
12570
12513
|
privateKeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
|
12571
12514
|
);
|
|
12572
|
-
return
|
|
12515
|
+
return SigningService2.createFromSecret(privateKeyBytes);
|
|
12573
12516
|
}
|
|
12574
12517
|
/**
|
|
12575
12518
|
* Get the wallet's signing public key (used for token ownership predicates).
|
|
@@ -12584,14 +12527,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12584
12527
|
* Create DirectAddress from a public key using UnmaskedPredicateReference
|
|
12585
12528
|
*/
|
|
12586
12529
|
async createDirectAddressFromPubkey(pubkeyHex) {
|
|
12587
|
-
const { UnmaskedPredicateReference:
|
|
12588
|
-
const { TokenType:
|
|
12589
|
-
const
|
|
12590
|
-
const tokenType = new
|
|
12530
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12531
|
+
const { TokenType: TokenType4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
12532
|
+
const UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
12533
|
+
const tokenType = new TokenType4(Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex"));
|
|
12591
12534
|
const pubkeyBytes = new Uint8Array(
|
|
12592
12535
|
pubkeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
|
12593
12536
|
);
|
|
12594
|
-
const addressRef = await
|
|
12537
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
12595
12538
|
tokenType,
|
|
12596
12539
|
"secp256k1",
|
|
12597
12540
|
pubkeyBytes,
|
|
@@ -12603,10 +12546,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12603
12546
|
* Resolve recipient to IAddress for L3 transfers.
|
|
12604
12547
|
* Uses pre-resolved PeerInfo when available to avoid redundant network queries.
|
|
12605
12548
|
*/
|
|
12606
|
-
async resolveRecipientAddress(recipient,
|
|
12549
|
+
async resolveRecipientAddress(recipient, _addressMode = "auto", peerInfo) {
|
|
12607
12550
|
const { AddressFactory } = await import("@unicitylabs/state-transition-sdk/lib/address/AddressFactory");
|
|
12608
|
-
|
|
12609
|
-
if (recipient.startsWith("PROXY:") || recipient.startsWith("DIRECT:")) {
|
|
12551
|
+
if (recipient.startsWith("DIRECT:")) {
|
|
12610
12552
|
return AddressFactory.createAddress(recipient);
|
|
12611
12553
|
}
|
|
12612
12554
|
if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {
|
|
@@ -12616,28 +12558,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12616
12558
|
const info = peerInfo ?? await this.deps?.transport.resolve?.(recipient) ?? null;
|
|
12617
12559
|
if (!info) {
|
|
12618
12560
|
throw new SphereError(
|
|
12619
|
-
`Recipient "${recipient}" not found. Use @nametag, a
|
|
12561
|
+
`Recipient "${recipient}" not found. Use @nametag, a DIRECT: address, or a 33-byte hex pubkey.`,
|
|
12620
12562
|
"INVALID_RECIPIENT"
|
|
12621
12563
|
);
|
|
12622
12564
|
}
|
|
12623
12565
|
const nametag = recipient.startsWith("@") ? recipient.slice(1) : info.nametag || recipient;
|
|
12624
|
-
if (
|
|
12625
|
-
|
|
12626
|
-
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
if (!info.directAddress) {
|
|
12630
|
-
throw new SphereError(`"${nametag}" has no DirectAddress stored. It may be a legacy registration.`, "INVALID_RECIPIENT");
|
|
12631
|
-
}
|
|
12632
|
-
logger.debug("Payments", `Using DirectAddress for "${nametag}" (forced): ${info.directAddress.slice(0, 30)}...`);
|
|
12633
|
-
return AddressFactory.createAddress(info.directAddress);
|
|
12634
|
-
}
|
|
12635
|
-
if (info.directAddress) {
|
|
12636
|
-
logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
|
|
12637
|
-
return AddressFactory.createAddress(info.directAddress);
|
|
12566
|
+
if (!info.directAddress) {
|
|
12567
|
+
throw new SphereError(
|
|
12568
|
+
`"${nametag}" has no DirectAddress \u2014 the recipient must publish a key-based identity binding.`,
|
|
12569
|
+
"INVALID_RECIPIENT"
|
|
12570
|
+
);
|
|
12638
12571
|
}
|
|
12639
|
-
logger.debug("Payments", `Using
|
|
12640
|
-
return
|
|
12572
|
+
logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
|
|
12573
|
+
return AddressFactory.createAddress(info.directAddress);
|
|
12641
12574
|
}
|
|
12642
12575
|
/**
|
|
12643
12576
|
* Handle NOSTR-FIRST commitment-only transfer (recipient side)
|
|
@@ -12692,14 +12625,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12692
12625
|
const addressScheme = recipientAddress.scheme;
|
|
12693
12626
|
const signingService = await this.createSigningService();
|
|
12694
12627
|
const transferSalt = transferTx.data.salt;
|
|
12695
|
-
const recipientPredicate = await
|
|
12628
|
+
const recipientPredicate = await UnmaskedPredicate4.create(
|
|
12696
12629
|
sourceToken.id,
|
|
12697
12630
|
sourceToken.type,
|
|
12698
12631
|
signingService,
|
|
12699
12632
|
HashAlgorithm5.SHA256,
|
|
12700
12633
|
transferSalt
|
|
12701
12634
|
);
|
|
12702
|
-
const recipientState = new
|
|
12635
|
+
const recipientState = new TokenState4(recipientPredicate, null);
|
|
12703
12636
|
let nametagTokens = [];
|
|
12704
12637
|
if (addressScheme === AddressScheme.PROXY) {
|
|
12705
12638
|
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
@@ -12796,6 +12729,67 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12796
12729
|
}
|
|
12797
12730
|
}
|
|
12798
12731
|
}
|
|
12732
|
+
/**
|
|
12733
|
+
* v2 engine transfer (sender-driven): the sender handed us a FINISHED token.
|
|
12734
|
+
* Decode the blob, dedup by the genesis-stable token id, store it as a
|
|
12735
|
+
* confirmed token, and emit/record the receipt. No commitment / inclusion-proof
|
|
12736
|
+
* / finalization round-trip (contrast the v1 sourceToken+transferTx path).
|
|
12737
|
+
*/
|
|
12738
|
+
async handleV2Transfer(payload, senderPubkey) {
|
|
12739
|
+
this.ensureInitialized();
|
|
12740
|
+
if (!this.loaded && this.loadedPromise) {
|
|
12741
|
+
await this.loadedPromise;
|
|
12742
|
+
}
|
|
12743
|
+
const engine = this.deps.tokenEngine;
|
|
12744
|
+
if (!engine) return;
|
|
12745
|
+
let token;
|
|
12746
|
+
try {
|
|
12747
|
+
token = await engine.decodeToken(decodeTokenBlob(hexToBytes2(payload.tokenBlob)));
|
|
12748
|
+
} catch (err) {
|
|
12749
|
+
logger.error("Payments", "V2 transfer: failed to decode token blob:", err);
|
|
12750
|
+
return;
|
|
12751
|
+
}
|
|
12752
|
+
const id = `v2_${engine.tokenId(token)}`;
|
|
12753
|
+
if (this.tokens.has(id)) {
|
|
12754
|
+
logger.debug("Payments", `V2 transfer ${id.slice(0, 16)}... already present, skipping`);
|
|
12755
|
+
return;
|
|
12756
|
+
}
|
|
12757
|
+
const info = await parseTokenInfo(payload.tokenBlob, engine);
|
|
12758
|
+
const registry = TokenRegistry.getInstance();
|
|
12759
|
+
const uiToken = {
|
|
12760
|
+
id,
|
|
12761
|
+
coinId: info.coinId,
|
|
12762
|
+
symbol: registry.getSymbol(info.coinId) || info.symbol,
|
|
12763
|
+
name: registry.getName(info.coinId) || info.name,
|
|
12764
|
+
decimals: registry.getDecimals(info.coinId) ?? info.decimals,
|
|
12765
|
+
amount: info.amount,
|
|
12766
|
+
status: "confirmed",
|
|
12767
|
+
createdAt: Date.now(),
|
|
12768
|
+
updatedAt: Date.now(),
|
|
12769
|
+
sdkData: payload.tokenBlob
|
|
12770
|
+
};
|
|
12771
|
+
await this.addToken(uiToken);
|
|
12772
|
+
const senderInfo = await this.resolveSenderInfo(senderPubkey);
|
|
12773
|
+
this.deps.emitEvent("transfer:incoming", {
|
|
12774
|
+
id,
|
|
12775
|
+
senderPubkey,
|
|
12776
|
+
senderNametag: senderInfo.senderNametag,
|
|
12777
|
+
tokens: [uiToken],
|
|
12778
|
+
memo: payload.memo,
|
|
12779
|
+
receivedAt: Date.now()
|
|
12780
|
+
});
|
|
12781
|
+
await this.addToHistory({
|
|
12782
|
+
type: "RECEIVED",
|
|
12783
|
+
amount: info.amount,
|
|
12784
|
+
coinId: info.coinId,
|
|
12785
|
+
symbol: uiToken.symbol,
|
|
12786
|
+
timestamp: Date.now(),
|
|
12787
|
+
senderPubkey,
|
|
12788
|
+
...senderInfo,
|
|
12789
|
+
memo: payload.memo,
|
|
12790
|
+
tokenId: id
|
|
12791
|
+
});
|
|
12792
|
+
}
|
|
12799
12793
|
async handleIncomingTransfer(transfer) {
|
|
12800
12794
|
if (!this.loaded && this.loadedPromise) {
|
|
12801
12795
|
await this.loadedPromise;
|
|
@@ -12803,6 +12797,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12803
12797
|
try {
|
|
12804
12798
|
const payload = transfer.payload;
|
|
12805
12799
|
logger.debug("Payments", "handleIncomingTransfer: keys=", Object.keys(payload).join(","));
|
|
12800
|
+
if (this.deps.tokenEngine && isV2TransferPayload(transfer.payload)) {
|
|
12801
|
+
await this.handleV2Transfer(transfer.payload, transfer.senderTransportPubkey);
|
|
12802
|
+
return;
|
|
12803
|
+
}
|
|
12806
12804
|
let combinedBundle = null;
|
|
12807
12805
|
if (isCombinedTransferBundleV6(payload)) {
|
|
12808
12806
|
combinedBundle = payload;
|
|
@@ -12884,7 +12882,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12884
12882
|
const hasTransactionData = transferTxInput.transactionData !== void 0;
|
|
12885
12883
|
const hasAuthenticator = transferTxInput.authenticator !== void 0;
|
|
12886
12884
|
if (hasData && hasInclusionProof) {
|
|
12887
|
-
transferTx = await
|
|
12885
|
+
transferTx = await TransferTransaction3.fromJSON(transferTxInput);
|
|
12888
12886
|
} else if (hasTransactionData && hasAuthenticator) {
|
|
12889
12887
|
const commitment = await TransferCommitment4.fromJSON(transferTxInput);
|
|
12890
12888
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
@@ -12905,7 +12903,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12905
12903
|
transferTx = commitment.toTransaction(inclusionProof);
|
|
12906
12904
|
} else {
|
|
12907
12905
|
try {
|
|
12908
|
-
transferTx = await
|
|
12906
|
+
transferTx = await TransferTransaction3.fromJSON(transferTxInput);
|
|
12909
12907
|
} catch {
|
|
12910
12908
|
const commitment = await TransferCommitment4.fromJSON(transferTxInput);
|
|
12911
12909
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
@@ -12947,7 +12945,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12947
12945
|
logger.warn("Payments", "Received invalid token");
|
|
12948
12946
|
return;
|
|
12949
12947
|
}
|
|
12950
|
-
const tokenInfo = await parseTokenInfo(tokenData);
|
|
12948
|
+
const tokenInfo = await parseTokenInfo(tokenData, this.deps?.tokenEngine);
|
|
12951
12949
|
const token = {
|
|
12952
12950
|
id: tokenInfo.tokenId ?? crypto.randomUUID(),
|
|
12953
12951
|
coinId: tokenInfo.coinId,
|
|
@@ -13253,24 +13251,6 @@ function createPaymentsModule(config) {
|
|
|
13253
13251
|
return new PaymentsModule(config);
|
|
13254
13252
|
}
|
|
13255
13253
|
|
|
13256
|
-
// modules/payments/TokenSplitCalculator.ts
|
|
13257
|
-
init_logger();
|
|
13258
|
-
import { Token as SdkToken3 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
13259
|
-
import { CoinId as CoinId5 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
13260
|
-
|
|
13261
|
-
// modules/payments/BackgroundCommitmentService.ts
|
|
13262
|
-
init_logger();
|
|
13263
|
-
init_errors();
|
|
13264
|
-
|
|
13265
|
-
// modules/payments/TokenRecoveryService.ts
|
|
13266
|
-
init_logger();
|
|
13267
|
-
import { TokenId as TokenId4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
13268
|
-
import { TokenState as TokenState6 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
13269
|
-
import { TokenType as TokenType4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
13270
|
-
import { CoinId as CoinId6 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
13271
|
-
import { HashAlgorithm as HashAlgorithm6 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
13272
|
-
import { UnmaskedPredicate as UnmaskedPredicate6 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
13273
|
-
|
|
13274
13254
|
// modules/communications/CommunicationsModule.ts
|
|
13275
13255
|
init_logger();
|
|
13276
13256
|
init_errors();
|
|
@@ -16912,7 +16892,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
16912
16892
|
const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : void 0;
|
|
16913
16893
|
return abytes(bytes, sizer);
|
|
16914
16894
|
}
|
|
16915
|
-
class
|
|
16895
|
+
class Signature2 {
|
|
16916
16896
|
r;
|
|
16917
16897
|
s;
|
|
16918
16898
|
recovery;
|
|
@@ -16932,7 +16912,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
16932
16912
|
let recid;
|
|
16933
16913
|
if (format === "der") {
|
|
16934
16914
|
const { r: r2, s: s2 } = DER.toSig(abytes(bytes));
|
|
16935
|
-
return new
|
|
16915
|
+
return new Signature2(r2, s2);
|
|
16936
16916
|
}
|
|
16937
16917
|
if (format === "recovered") {
|
|
16938
16918
|
recid = bytes[0];
|
|
@@ -16942,7 +16922,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
16942
16922
|
const L = lengths.signature / 2;
|
|
16943
16923
|
const r = bytes.subarray(0, L);
|
|
16944
16924
|
const s = bytes.subarray(L, L * 2);
|
|
16945
|
-
return new
|
|
16925
|
+
return new Signature2(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
16946
16926
|
}
|
|
16947
16927
|
static fromHex(hex, format) {
|
|
16948
16928
|
return this.fromBytes(hexToBytes(hex), format);
|
|
@@ -16954,7 +16934,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
16954
16934
|
return recovery;
|
|
16955
16935
|
}
|
|
16956
16936
|
addRecoveryBit(recovery) {
|
|
16957
|
-
return new
|
|
16937
|
+
return new Signature2(this.r, this.s, recovery);
|
|
16958
16938
|
}
|
|
16959
16939
|
recoverPublicKey(messageHash) {
|
|
16960
16940
|
const { r, s } = this;
|
|
@@ -17046,7 +17026,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17046
17026
|
normS = Fn.neg(s);
|
|
17047
17027
|
recovery ^= 1;
|
|
17048
17028
|
}
|
|
17049
|
-
return new
|
|
17029
|
+
return new Signature2(r, normS, hasLargeCofactor ? void 0 : recovery);
|
|
17050
17030
|
}
|
|
17051
17031
|
return { seed, k2sig };
|
|
17052
17032
|
}
|
|
@@ -17061,12 +17041,12 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17061
17041
|
publicKey = abytes(publicKey, void 0, "publicKey");
|
|
17062
17042
|
message = validateMsgAndHash(message, prehash);
|
|
17063
17043
|
if (!isBytes(signature)) {
|
|
17064
|
-
const end = signature instanceof
|
|
17044
|
+
const end = signature instanceof Signature2 ? ", use sig.toBytes()" : "";
|
|
17065
17045
|
throw new Error("verify expects Uint8Array signature" + end);
|
|
17066
17046
|
}
|
|
17067
17047
|
validateSigLength(signature, format);
|
|
17068
17048
|
try {
|
|
17069
|
-
const sig =
|
|
17049
|
+
const sig = Signature2.fromBytes(signature, format);
|
|
17070
17050
|
const P = Point.fromBytes(publicKey);
|
|
17071
17051
|
if (lowS && sig.hasHighS())
|
|
17072
17052
|
return false;
|
|
@@ -17087,7 +17067,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17087
17067
|
function recoverPublicKey(signature, message, opts = {}) {
|
|
17088
17068
|
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|
17089
17069
|
message = validateMsgAndHash(message, prehash);
|
|
17090
|
-
return
|
|
17070
|
+
return Signature2.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes();
|
|
17091
17071
|
}
|
|
17092
17072
|
return Object.freeze({
|
|
17093
17073
|
keygen,
|
|
@@ -17099,7 +17079,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17099
17079
|
sign,
|
|
17100
17080
|
verify,
|
|
17101
17081
|
recoverPublicKey,
|
|
17102
|
-
Signature,
|
|
17082
|
+
Signature: Signature2,
|
|
17103
17083
|
hash
|
|
17104
17084
|
});
|
|
17105
17085
|
}
|
|
@@ -18371,7 +18351,7 @@ function freezeCoinAsset(coinAsset, state, latestSender) {
|
|
|
18371
18351
|
}
|
|
18372
18352
|
|
|
18373
18353
|
// modules/accounting/AccountingModule.ts
|
|
18374
|
-
import { Token as
|
|
18354
|
+
import { Token as SdkToken3 } from "@unicitylabs/state-transition-sdk/lib/token/Token.js";
|
|
18375
18355
|
var LOG_TAG2 = "Accounting";
|
|
18376
18356
|
var INV_LEDGER_PREFIX = "inv_ledger:";
|
|
18377
18357
|
var AccountingModule = class _AccountingModule {
|
|
@@ -19024,129 +19004,146 @@ var AccountingModule = class _AccountingModule {
|
|
|
19024
19004
|
);
|
|
19025
19005
|
}
|
|
19026
19006
|
try {
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
const
|
|
19030
|
-
|
|
19031
|
-
|
|
19032
|
-
|
|
19033
|
-
|
|
19034
|
-
|
|
19035
|
-
|
|
19036
|
-
|
|
19037
|
-
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
|
|
19041
|
-
|
|
19042
|
-
|
|
19043
|
-
|
|
19044
|
-
|
|
19045
|
-
|
|
19046
|
-
);
|
|
19047
|
-
|
|
19048
|
-
|
|
19049
|
-
|
|
19050
|
-
|
|
19051
|
-
|
|
19052
|
-
|
|
19053
|
-
|
|
19054
|
-
|
|
19055
|
-
|
|
19056
|
-
|
|
19057
|
-
|
|
19058
|
-
|
|
19059
|
-
|
|
19060
|
-
|
|
19061
|
-
|
|
19062
|
-
|
|
19063
|
-
|
|
19064
|
-
|
|
19065
|
-
|
|
19066
|
-
|
|
19067
|
-
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
|
|
19071
|
-
|
|
19072
|
-
|
|
19073
|
-
|
|
19074
|
-
|
|
19075
|
-
|
|
19076
|
-
|
|
19077
|
-
|
|
19078
|
-
|
|
19079
|
-
|
|
19080
|
-
|
|
19081
|
-
|
|
19082
|
-
|
|
19083
|
-
|
|
19084
|
-
|
|
19085
|
-
|
|
19086
|
-
|
|
19087
|
-
|
|
19088
|
-
|
|
19089
|
-
|
|
19090
|
-
|
|
19091
|
-
|
|
19007
|
+
let invoiceId;
|
|
19008
|
+
let sdkData;
|
|
19009
|
+
const engine = deps.tokenEngine;
|
|
19010
|
+
if (engine) {
|
|
19011
|
+
const invoiceToken = await engine.mintDataToken({
|
|
19012
|
+
recipientPubkey: hexToBytes(deps.identity.chainPubkey),
|
|
19013
|
+
data: invoiceBytesEncoded,
|
|
19014
|
+
tokenType: hexToBytes(INVOICE_TOKEN_TYPE_HEX),
|
|
19015
|
+
salt
|
|
19016
|
+
});
|
|
19017
|
+
invoiceId = engine.tokenId(invoiceToken);
|
|
19018
|
+
if (this.invoiceTermsCache.has(invoiceId)) {
|
|
19019
|
+
throw new SphereError(`Invoice already exists locally: ${invoiceId}`, "INVOICE_ALREADY_EXISTS");
|
|
19020
|
+
}
|
|
19021
|
+
sdkData = bytesToHex(encodeTokenBlob(engine.encodeToken(invoiceToken)));
|
|
19022
|
+
} else {
|
|
19023
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
|
|
19024
|
+
const { TokenType: TokenType4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType.js");
|
|
19025
|
+
const { MintTransactionData: MintTransactionData3 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData.js");
|
|
19026
|
+
const { MintCommitment: MintCommitment3 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment.js");
|
|
19027
|
+
const { SigningService: SigningService3 } = await import("@unicitylabs/state-transition-sdk/lib/sign/SigningService.js");
|
|
19028
|
+
const { HashAlgorithm: HashAlgorithm6 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
|
|
19029
|
+
const { DataHasher: DataHasher2 } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19030
|
+
const { UnmaskedPredicate: UnmaskedPredicate5 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate.js");
|
|
19031
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference.js");
|
|
19032
|
+
const { TokenState: TokenState5 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenState.js");
|
|
19033
|
+
const { Token: SdkToken4 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token.js");
|
|
19034
|
+
const { waitInclusionProof: waitInclusionProof6 } = await import("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js");
|
|
19035
|
+
const hash = await new DataHasher2(HashAlgorithm6.SHA256).update(invoiceBytesEncoded).digest();
|
|
19036
|
+
const invoiceTokenId = new TokenId4(hash.imprint);
|
|
19037
|
+
invoiceId = invoiceTokenId.toJSON();
|
|
19038
|
+
if (this.invoiceTermsCache.has(invoiceId)) {
|
|
19039
|
+
throw new SphereError(
|
|
19040
|
+
`Invoice already exists locally: ${invoiceId}`,
|
|
19041
|
+
"INVOICE_ALREADY_EXISTS"
|
|
19042
|
+
);
|
|
19043
|
+
}
|
|
19044
|
+
const invoiceTokenType = new TokenType4(
|
|
19045
|
+
Buffer.from(INVOICE_TOKEN_TYPE_HEX, "hex")
|
|
19046
|
+
);
|
|
19047
|
+
const signingService = await SigningService3.createFromSecret(signingKeyBytes);
|
|
19048
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
19049
|
+
invoiceTokenType,
|
|
19050
|
+
signingService.algorithm,
|
|
19051
|
+
signingService.publicKey,
|
|
19052
|
+
HashAlgorithm6.SHA256
|
|
19053
|
+
);
|
|
19054
|
+
const ownerAddress = await addressRef.toAddress();
|
|
19055
|
+
const mintData = await MintTransactionData3.create(
|
|
19056
|
+
invoiceTokenId,
|
|
19057
|
+
invoiceTokenType,
|
|
19058
|
+
invoiceBytesEncoded,
|
|
19059
|
+
// tokenData: serialized InvoiceTerms (UTF-8 JSON)
|
|
19060
|
+
null,
|
|
19061
|
+
// coinData: null (non-fungible invoice token)
|
|
19062
|
+
ownerAddress,
|
|
19063
|
+
salt,
|
|
19064
|
+
null,
|
|
19065
|
+
// recipientDataHash: null
|
|
19066
|
+
null
|
|
19067
|
+
// reason: null
|
|
19068
|
+
);
|
|
19069
|
+
if (this.config.debug) {
|
|
19070
|
+
logger.debug(LOG_TAG2, `Created MintTransactionData for invoice ${invoiceId}`);
|
|
19071
|
+
}
|
|
19072
|
+
const commitment = await MintCommitment3.create(mintData);
|
|
19073
|
+
if (this.config.debug) {
|
|
19074
|
+
logger.debug(LOG_TAG2, "Created MintCommitment for invoice");
|
|
19075
|
+
}
|
|
19076
|
+
const MAX_RETRIES = 3;
|
|
19077
|
+
let submitSuccess = false;
|
|
19078
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
19079
|
+
try {
|
|
19092
19080
|
if (this.config.debug) {
|
|
19093
19081
|
logger.debug(
|
|
19094
19082
|
LOG_TAG2,
|
|
19095
|
-
|
|
19083
|
+
`Submitting invoice commitment (attempt ${attempt}/${MAX_RETRIES})...`
|
|
19096
19084
|
);
|
|
19097
19085
|
}
|
|
19098
|
-
|
|
19099
|
-
|
|
19100
|
-
|
|
19101
|
-
|
|
19086
|
+
const response = await stClient.submitMintCommitment(commitment);
|
|
19087
|
+
if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
|
|
19088
|
+
if (this.config.debug) {
|
|
19089
|
+
logger.debug(
|
|
19090
|
+
LOG_TAG2,
|
|
19091
|
+
response.status === "REQUEST_ID_EXISTS" ? "Invoice commitment already exists (idempotent re-mint)" : "Invoice commitment submitted successfully"
|
|
19092
|
+
);
|
|
19093
|
+
}
|
|
19094
|
+
submitSuccess = true;
|
|
19095
|
+
break;
|
|
19096
|
+
} else {
|
|
19097
|
+
logger.warn(LOG_TAG2, `Invoice commitment submission failed: ${response.status}`);
|
|
19098
|
+
if (attempt === MAX_RETRIES) {
|
|
19099
|
+
throw new SphereError(
|
|
19100
|
+
`Failed to mint invoice token: commitment rejected after ${MAX_RETRIES} attempts: ${response.status}`,
|
|
19101
|
+
"INVOICE_MINT_FAILED"
|
|
19102
|
+
);
|
|
19103
|
+
}
|
|
19104
|
+
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19105
|
+
}
|
|
19106
|
+
} catch (retryErr) {
|
|
19107
|
+
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;
|
|
19108
|
+
logger.warn(LOG_TAG2, `Invoice commitment attempt ${attempt} error:`, retryErr);
|
|
19102
19109
|
if (attempt === MAX_RETRIES) {
|
|
19103
19110
|
throw new SphereError(
|
|
19104
|
-
`Failed to mint invoice token:
|
|
19105
|
-
"INVOICE_MINT_FAILED"
|
|
19111
|
+
`Failed to mint invoice token: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
|
|
19112
|
+
"INVOICE_MINT_FAILED",
|
|
19113
|
+
retryErr
|
|
19106
19114
|
);
|
|
19107
19115
|
}
|
|
19108
19116
|
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19109
19117
|
}
|
|
19110
|
-
} catch (retryErr) {
|
|
19111
|
-
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;
|
|
19112
|
-
logger.warn(LOG_TAG2, `Invoice commitment attempt ${attempt} error:`, retryErr);
|
|
19113
|
-
if (attempt === MAX_RETRIES) {
|
|
19114
|
-
throw new SphereError(
|
|
19115
|
-
`Failed to mint invoice token: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
|
|
19116
|
-
"INVOICE_MINT_FAILED",
|
|
19117
|
-
retryErr
|
|
19118
|
-
);
|
|
19119
|
-
}
|
|
19120
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19121
19118
|
}
|
|
19122
|
-
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
|
|
19126
|
-
|
|
19119
|
+
if (!submitSuccess) {
|
|
19120
|
+
throw new SphereError(
|
|
19121
|
+
"Failed to mint invoice token: commitment submission failed after retries",
|
|
19122
|
+
"INVOICE_MINT_FAILED"
|
|
19123
|
+
);
|
|
19124
|
+
}
|
|
19125
|
+
if (this.config.debug) {
|
|
19126
|
+
logger.debug(LOG_TAG2, "Waiting for invoice inclusion proof...");
|
|
19127
|
+
}
|
|
19128
|
+
const inclusionProof = await waitInclusionProof6(trustBase, stClient, commitment);
|
|
19129
|
+
if (this.config.debug) {
|
|
19130
|
+
logger.debug(LOG_TAG2, "Invoice inclusion proof received");
|
|
19131
|
+
}
|
|
19132
|
+
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
19133
|
+
const invoicePredicate = await UnmaskedPredicate5.create(
|
|
19134
|
+
invoiceTokenId,
|
|
19135
|
+
invoiceTokenType,
|
|
19136
|
+
signingService,
|
|
19137
|
+
HashAlgorithm6.SHA256,
|
|
19138
|
+
salt
|
|
19127
19139
|
);
|
|
19140
|
+
const tokenState = new TokenState5(invoicePredicate, null);
|
|
19141
|
+
const sdkToken = await SdkToken4.mint(trustBase, tokenState, genesisTransaction);
|
|
19142
|
+
if (this.config.debug) {
|
|
19143
|
+
logger.debug(LOG_TAG2, "Invoice token minted successfully");
|
|
19144
|
+
}
|
|
19145
|
+
sdkData = JSON.stringify(sdkToken.toJSON());
|
|
19128
19146
|
}
|
|
19129
|
-
if (this.config.debug) {
|
|
19130
|
-
logger.debug(LOG_TAG2, "Waiting for invoice inclusion proof...");
|
|
19131
|
-
}
|
|
19132
|
-
const inclusionProof = await waitInclusionProof6(trustBase, stClient, commitment);
|
|
19133
|
-
if (this.config.debug) {
|
|
19134
|
-
logger.debug(LOG_TAG2, "Invoice inclusion proof received");
|
|
19135
|
-
}
|
|
19136
|
-
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
19137
|
-
const invoicePredicate = await UnmaskedPredicate7.create(
|
|
19138
|
-
invoiceTokenId,
|
|
19139
|
-
invoiceTokenType,
|
|
19140
|
-
signingService,
|
|
19141
|
-
HashAlgorithm8.SHA256,
|
|
19142
|
-
salt
|
|
19143
|
-
);
|
|
19144
|
-
const tokenState = new TokenState7(invoicePredicate, null);
|
|
19145
|
-
const sdkToken = await SdkToken5.mint(trustBase, tokenState, genesisTransaction);
|
|
19146
|
-
if (this.config.debug) {
|
|
19147
|
-
logger.debug(LOG_TAG2, "Invoice token minted successfully");
|
|
19148
|
-
}
|
|
19149
|
-
const sdkTokenJson = sdkToken.toJSON();
|
|
19150
19147
|
const uiToken = {
|
|
19151
19148
|
id: invoiceId,
|
|
19152
19149
|
coinId: INVOICE_TOKEN_TYPE_HEX,
|
|
@@ -19157,7 +19154,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19157
19154
|
status: "confirmed",
|
|
19158
19155
|
createdAt: terms.createdAt,
|
|
19159
19156
|
updatedAt: terms.createdAt,
|
|
19160
|
-
sdkData
|
|
19157
|
+
sdkData
|
|
19161
19158
|
};
|
|
19162
19159
|
await deps.payments.addToken(uiToken);
|
|
19163
19160
|
this.invoiceTermsCache.set(invoiceId, this._normalizeInvoiceTerms(terms));
|
|
@@ -19168,17 +19165,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19168
19165
|
const allTokens = deps.payments.getTokens();
|
|
19169
19166
|
let anyScanDirty = false;
|
|
19170
19167
|
for (const token of allTokens) {
|
|
19171
|
-
if (
|
|
19172
|
-
let txf;
|
|
19173
|
-
try {
|
|
19174
|
-
txf = JSON.parse(token.sdkData);
|
|
19175
|
-
} catch {
|
|
19176
|
-
continue;
|
|
19177
|
-
}
|
|
19178
|
-
const txCount = txf.transactions?.length ?? 0;
|
|
19179
|
-
if (txCount === 0) continue;
|
|
19180
|
-
this._processTokenTransactions(token.id, txf, 0);
|
|
19181
|
-
anyScanDirty = true;
|
|
19168
|
+
if (await this._scanTokenForAttribution(token, 0)) anyScanDirty = true;
|
|
19182
19169
|
}
|
|
19183
19170
|
const archivedTokensForScan = deps.payments.getArchivedTokens();
|
|
19184
19171
|
for (const [archivedId, txf] of archivedTokensForScan) {
|
|
@@ -19194,7 +19181,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19194
19181
|
if (this.config.debug) {
|
|
19195
19182
|
logger.debug(LOG_TAG2, `Invoice created and stored: ${invoiceId}`);
|
|
19196
19183
|
}
|
|
19197
|
-
const txfToken =
|
|
19184
|
+
const txfToken = engine ? sdkData : JSON.parse(sdkData);
|
|
19198
19185
|
return {
|
|
19199
19186
|
success: true,
|
|
19200
19187
|
invoiceId,
|
|
@@ -19235,36 +19222,60 @@ var AccountingModule = class _AccountingModule {
|
|
|
19235
19222
|
this.ensureNotDestroyed();
|
|
19236
19223
|
this.ensureInitialized();
|
|
19237
19224
|
const deps = this.deps;
|
|
19238
|
-
|
|
19239
|
-
|
|
19240
|
-
|
|
19241
|
-
|
|
19242
|
-
|
|
19243
|
-
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
|
|
19248
|
-
|
|
19249
|
-
|
|
19250
|
-
);
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19225
|
+
let terms;
|
|
19226
|
+
let tokenId;
|
|
19227
|
+
let sdkDataForStore;
|
|
19228
|
+
const engine = deps.tokenEngine;
|
|
19229
|
+
const isV2 = !!engine && typeof token === "string";
|
|
19230
|
+
if (isV2) {
|
|
19231
|
+
const sphereToken = await engine.decodeToken(decodeTokenBlob(hexToBytes(token)));
|
|
19232
|
+
const verifyResult = await engine.verify(sphereToken);
|
|
19233
|
+
if (!verifyResult.ok) {
|
|
19234
|
+
throw new SphereError("Invoice import failed: inclusion proof is invalid.", "INVOICE_INVALID_PROOF");
|
|
19235
|
+
}
|
|
19236
|
+
tokenId = engine.tokenId(sphereToken);
|
|
19237
|
+
const data = engine.readTokenData(sphereToken);
|
|
19238
|
+
if (!data) {
|
|
19239
|
+
throw new SphereError("Invoice import failed: missing or invalid tokenData field.", "INVOICE_INVALID_DATA");
|
|
19240
|
+
}
|
|
19254
19241
|
try {
|
|
19255
|
-
|
|
19256
|
-
jsonString = new TextDecoder().decode(bytes);
|
|
19242
|
+
terms = JSON.parse(new TextDecoder().decode(data));
|
|
19257
19243
|
} catch {
|
|
19244
|
+
throw new SphereError("Invoice import failed: tokenData is not valid JSON.", "INVOICE_INVALID_DATA");
|
|
19258
19245
|
}
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
|
|
19262
|
-
|
|
19263
|
-
|
|
19264
|
-
|
|
19265
|
-
|
|
19266
|
-
|
|
19267
|
-
|
|
19246
|
+
sdkDataForStore = token;
|
|
19247
|
+
} else {
|
|
19248
|
+
const tokenType = token.genesis?.data?.tokenType;
|
|
19249
|
+
if (tokenType !== INVOICE_TOKEN_TYPE_HEX) {
|
|
19250
|
+
throw new SphereError(
|
|
19251
|
+
`Invoice import failed: token type "${tokenType}" is not the expected invoice type.`,
|
|
19252
|
+
"INVOICE_WRONG_TOKEN_TYPE"
|
|
19253
|
+
);
|
|
19254
|
+
}
|
|
19255
|
+
const tokenData = token.genesis?.data?.tokenData;
|
|
19256
|
+
if (!tokenData || typeof tokenData !== "string") {
|
|
19257
|
+
throw new SphereError(
|
|
19258
|
+
"Invoice import failed: missing or invalid tokenData field.",
|
|
19259
|
+
"INVOICE_INVALID_DATA"
|
|
19260
|
+
);
|
|
19261
|
+
}
|
|
19262
|
+
let jsonString = tokenData;
|
|
19263
|
+
if (!/^\s*[[{"]/.test(tokenData)) {
|
|
19264
|
+
try {
|
|
19265
|
+
const bytes = hexToBytes(tokenData);
|
|
19266
|
+
jsonString = new TextDecoder().decode(bytes);
|
|
19267
|
+
} catch {
|
|
19268
|
+
}
|
|
19269
|
+
}
|
|
19270
|
+
try {
|
|
19271
|
+
terms = JSON.parse(jsonString);
|
|
19272
|
+
} catch {
|
|
19273
|
+
throw new SphereError(
|
|
19274
|
+
"Invoice import failed: tokenData is not valid JSON.",
|
|
19275
|
+
"INVOICE_INVALID_DATA"
|
|
19276
|
+
);
|
|
19277
|
+
}
|
|
19278
|
+
sdkDataForStore = JSON.stringify(token);
|
|
19268
19279
|
}
|
|
19269
19280
|
if (!terms || typeof terms !== "object") {
|
|
19270
19281
|
throw new SphereError(
|
|
@@ -19351,7 +19362,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19351
19362
|
}
|
|
19352
19363
|
}
|
|
19353
19364
|
}
|
|
19354
|
-
|
|
19365
|
+
if (!isV2) tokenId = token.genesis?.data?.tokenId;
|
|
19355
19366
|
if (!tokenId || typeof tokenId !== "string") {
|
|
19356
19367
|
throw new SphereError(
|
|
19357
19368
|
"Invoice import failed: missing tokenId in genesis data.",
|
|
@@ -19364,13 +19375,13 @@ var AccountingModule = class _AccountingModule {
|
|
|
19364
19375
|
"INVOICE_ALREADY_EXISTS"
|
|
19365
19376
|
);
|
|
19366
19377
|
}
|
|
19367
|
-
{
|
|
19368
|
-
const { DataHasher } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19369
|
-
const { HashAlgorithm:
|
|
19370
|
-
const { TokenId:
|
|
19378
|
+
if (!isV2) {
|
|
19379
|
+
const { DataHasher: DataHasher2 } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19380
|
+
const { HashAlgorithm: HashAlgorithm6 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
|
|
19381
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
|
|
19371
19382
|
const reSerializedBytes = new TextEncoder().encode(canonicalSerialize(terms));
|
|
19372
|
-
const hash = await new
|
|
19373
|
-
const reTokenId = new
|
|
19383
|
+
const hash = await new DataHasher2(HashAlgorithm6.SHA256).update(reSerializedBytes).digest();
|
|
19384
|
+
const reTokenId = new TokenId4(hash.imprint).toJSON();
|
|
19374
19385
|
if (reTokenId !== tokenId) {
|
|
19375
19386
|
throw new SphereError(
|
|
19376
19387
|
"Invoice import failed: parsed terms do not match on-chain token ID (canonical hash mismatch).",
|
|
@@ -19378,35 +19389,37 @@ var AccountingModule = class _AccountingModule {
|
|
|
19378
19389
|
);
|
|
19379
19390
|
}
|
|
19380
19391
|
}
|
|
19381
|
-
if (!
|
|
19382
|
-
|
|
19383
|
-
"Trust base unavailable \u2014 cannot verify invoice proof. Ensure oracle supports getTrustBase().",
|
|
19384
|
-
"INVOICE_INVALID_PROOF"
|
|
19385
|
-
);
|
|
19386
|
-
}
|
|
19387
|
-
try {
|
|
19388
|
-
const sdkToken = await SdkToken4.fromJSON(token);
|
|
19389
|
-
const verifyResult = await sdkToken.verify(deps.trustBase);
|
|
19390
|
-
const verifyOk = verifyResult.isSuccessful === true;
|
|
19391
|
-
if (!verifyOk) {
|
|
19392
|
+
if (!isV2) {
|
|
19393
|
+
if (!deps.trustBase || deps.trustBase instanceof Uint8Array && deps.trustBase.length === 0) {
|
|
19392
19394
|
throw new SphereError(
|
|
19393
|
-
"
|
|
19395
|
+
"Trust base unavailable \u2014 cannot verify invoice proof. Ensure oracle supports getTrustBase().",
|
|
19394
19396
|
"INVOICE_INVALID_PROOF"
|
|
19395
19397
|
);
|
|
19396
19398
|
}
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
+
try {
|
|
19400
|
+
const sdkToken = await SdkToken3.fromJSON(token);
|
|
19401
|
+
const verifyResult = await sdkToken.verify(deps.trustBase);
|
|
19402
|
+
const verifyOk = verifyResult.isSuccessful === true;
|
|
19403
|
+
if (!verifyOk) {
|
|
19404
|
+
throw new SphereError(
|
|
19405
|
+
"Invoice import failed: inclusion proof is invalid.",
|
|
19406
|
+
"INVOICE_INVALID_PROOF"
|
|
19407
|
+
);
|
|
19408
|
+
}
|
|
19409
|
+
const canonicalTokenId = sdkToken.id?.toJSON?.() ?? null;
|
|
19410
|
+
if (!canonicalTokenId || canonicalTokenId !== tokenId) {
|
|
19411
|
+
throw new SphereError(
|
|
19412
|
+
`Invoice import failed: tokenId mismatch or unverifiable \u2014 JSON claims ${tokenId}, cryptographic identity is ${canonicalTokenId ?? "unknown"}`,
|
|
19413
|
+
"INVOICE_INVALID_DATA"
|
|
19414
|
+
);
|
|
19415
|
+
}
|
|
19416
|
+
} catch (err) {
|
|
19417
|
+
if (err instanceof SphereError) throw err;
|
|
19399
19418
|
throw new SphereError(
|
|
19400
|
-
`Invoice import failed:
|
|
19401
|
-
"
|
|
19419
|
+
`Invoice import failed: proof verification error \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
19420
|
+
"INVOICE_INVALID_PROOF"
|
|
19402
19421
|
);
|
|
19403
19422
|
}
|
|
19404
|
-
} catch (err) {
|
|
19405
|
-
if (err instanceof SphereError) throw err;
|
|
19406
|
-
throw new SphereError(
|
|
19407
|
-
`Invoice import failed: proof verification error \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
19408
|
-
"INVOICE_INVALID_PROOF"
|
|
19409
|
-
);
|
|
19410
19423
|
}
|
|
19411
19424
|
try {
|
|
19412
19425
|
const uiToken = {
|
|
@@ -19419,7 +19432,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19419
19432
|
status: "confirmed",
|
|
19420
19433
|
createdAt: terms.createdAt,
|
|
19421
19434
|
updatedAt: terms.createdAt,
|
|
19422
|
-
sdkData:
|
|
19435
|
+
sdkData: sdkDataForStore
|
|
19423
19436
|
};
|
|
19424
19437
|
await deps.payments.addToken(uiToken);
|
|
19425
19438
|
} catch (err) {
|
|
@@ -19474,17 +19487,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
19474
19487
|
const allTokens = deps.payments.getTokens();
|
|
19475
19488
|
let anyDirty = false;
|
|
19476
19489
|
for (const existingToken of allTokens) {
|
|
19477
|
-
if (!existingToken.sdkData) continue;
|
|
19478
|
-
let txf;
|
|
19479
|
-
try {
|
|
19480
|
-
txf = JSON.parse(existingToken.sdkData);
|
|
19481
|
-
} catch {
|
|
19482
|
-
continue;
|
|
19483
|
-
}
|
|
19484
|
-
const transactions = txf.transactions ?? [];
|
|
19485
19490
|
const startIndex = this.tokenScanState.get(existingToken.id) ?? 0;
|
|
19486
|
-
if (
|
|
19487
|
-
this._processTokenTransactions(existingToken.id, txf, startIndex);
|
|
19491
|
+
if (await this._scanTokenForAttribution(existingToken, startIndex)) {
|
|
19488
19492
|
anyDirty = true;
|
|
19489
19493
|
}
|
|
19490
19494
|
}
|
|
@@ -21374,17 +21378,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
21374
21378
|
const allTokens = deps.payments.getTokens();
|
|
21375
21379
|
let anyDirty = false;
|
|
21376
21380
|
for (const token of allTokens) {
|
|
21377
|
-
if (!token.sdkData) continue;
|
|
21378
|
-
let txf;
|
|
21379
|
-
try {
|
|
21380
|
-
txf = JSON.parse(token.sdkData);
|
|
21381
|
-
} catch {
|
|
21382
|
-
continue;
|
|
21383
|
-
}
|
|
21384
|
-
const transactions = txf.transactions ?? [];
|
|
21385
21381
|
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
21386
|
-
if (
|
|
21387
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21382
|
+
if (await this._scanTokenForAttribution(token, startIndex)) {
|
|
21388
21383
|
anyDirty = true;
|
|
21389
21384
|
}
|
|
21390
21385
|
}
|
|
@@ -21439,6 +21434,53 @@ var AccountingModule = class _AccountingModule {
|
|
|
21439
21434
|
* @param txf - Parsed TxfToken.
|
|
21440
21435
|
* @param startIndex - First unprocessed transaction index.
|
|
21441
21436
|
*/
|
|
21437
|
+
/**
|
|
21438
|
+
* Attribute one payment token's invoice memo(s) to the ledger.
|
|
21439
|
+
*
|
|
21440
|
+
* v2 (engine blob): the token carries a single on-chain memo. We decode it and
|
|
21441
|
+
* shim the token into a v1-shaped `txf` (coinData ← engine.readValue, the memo
|
|
21442
|
+
* ← engine.readMemo) so the battle-hardened `_processTokenTransactions` runs
|
|
21443
|
+
* UNCHANGED — same dedup, direction, provisional/synthetic/orphan handling.
|
|
21444
|
+
* v1 (TXF JSON): parse and scan transactions directly.
|
|
21445
|
+
*
|
|
21446
|
+
* `startIndex` is the per-token watermark (0 for a full retroactive scan).
|
|
21447
|
+
* Returns true when the token was scanned. Async because engine.decodeToken is.
|
|
21448
|
+
*/
|
|
21449
|
+
async _scanTokenForAttribution(token, startIndex) {
|
|
21450
|
+
if (!token.sdkData) return false;
|
|
21451
|
+
const engine = this.deps?.tokenEngine;
|
|
21452
|
+
const isBlob = token.sdkData.length >= 2 && token.sdkData.length % 2 === 0 && token.sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(token.sdkData);
|
|
21453
|
+
if (engine && isBlob) {
|
|
21454
|
+
let syntheticTxf;
|
|
21455
|
+
try {
|
|
21456
|
+
const sphereToken = await engine.decodeToken(decodeTokenBlob(hexToBytes(token.sdkData)));
|
|
21457
|
+
const memo = engine.readMemo(sphereToken);
|
|
21458
|
+
if (!memo) return false;
|
|
21459
|
+
const coinData = (engine.readValue(sphereToken)?.assets ?? []).map(
|
|
21460
|
+
(a) => [a.coinId, a.amount.toString()]
|
|
21461
|
+
);
|
|
21462
|
+
syntheticTxf = {
|
|
21463
|
+
genesis: { data: { coinData } },
|
|
21464
|
+
transactions: [{ data: { message: bytesToHex(memo) }, inclusionProof: {} }]
|
|
21465
|
+
};
|
|
21466
|
+
} catch {
|
|
21467
|
+
return false;
|
|
21468
|
+
}
|
|
21469
|
+
this._processTokenTransactions(token.id, syntheticTxf, startIndex);
|
|
21470
|
+
return true;
|
|
21471
|
+
}
|
|
21472
|
+
let txf;
|
|
21473
|
+
try {
|
|
21474
|
+
txf = JSON.parse(token.sdkData);
|
|
21475
|
+
} catch {
|
|
21476
|
+
return false;
|
|
21477
|
+
}
|
|
21478
|
+
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
21479
|
+
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21480
|
+
return true;
|
|
21481
|
+
}
|
|
21482
|
+
return false;
|
|
21483
|
+
}
|
|
21442
21484
|
_processTokenTransactions(tokenId, txf, startIndex) {
|
|
21443
21485
|
const transactions = txf.transactions ?? [];
|
|
21444
21486
|
let lastSuccessIdx = startIndex;
|
|
@@ -21718,17 +21760,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
21718
21760
|
async _handleIncomingTransfer(transfer) {
|
|
21719
21761
|
if (this.destroyed) return;
|
|
21720
21762
|
for (const token of transfer.tokens) {
|
|
21721
|
-
|
|
21722
|
-
let txf;
|
|
21723
|
-
try {
|
|
21724
|
-
txf = JSON.parse(token.sdkData);
|
|
21725
|
-
} catch {
|
|
21726
|
-
continue;
|
|
21727
|
-
}
|
|
21728
|
-
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
21729
|
-
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
21730
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21731
|
-
}
|
|
21763
|
+
await this._scanTokenForAttribution(token, this.tokenScanState.get(token.id) ?? 0);
|
|
21732
21764
|
}
|
|
21733
21765
|
if (this.destroyed) return;
|
|
21734
21766
|
await this._flushDirtyLedgerEntries();
|
|
@@ -21866,16 +21898,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
21866
21898
|
if (this.destroyed) return;
|
|
21867
21899
|
for (const token of result.tokens) {
|
|
21868
21900
|
if (!token.sdkData) continue;
|
|
21869
|
-
|
|
21870
|
-
try {
|
|
21871
|
-
txf = JSON.parse(token.sdkData);
|
|
21872
|
-
} catch {
|
|
21873
|
-
continue;
|
|
21874
|
-
}
|
|
21875
|
-
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
21876
|
-
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
21877
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21878
|
-
}
|
|
21901
|
+
await this._scanTokenForAttribution(token, this.tokenScanState.get(token.id) ?? 0);
|
|
21879
21902
|
const relatedInvoices = this.tokenInvoiceMap.get(token.id);
|
|
21880
21903
|
if (relatedInvoices) {
|
|
21881
21904
|
for (const invoiceId of relatedInvoices) {
|
|
@@ -21946,15 +21969,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
21946
21969
|
const tokens = this.deps.payments.getTokens();
|
|
21947
21970
|
const token = tokens.find((t) => t.id === tokenId);
|
|
21948
21971
|
if (!token?.sdkData) return;
|
|
21949
|
-
let txf;
|
|
21950
|
-
try {
|
|
21951
|
-
txf = JSON.parse(token.sdkData);
|
|
21952
|
-
} catch {
|
|
21953
|
-
return;
|
|
21954
|
-
}
|
|
21955
21972
|
const startIndex = this.tokenScanState.get(tokenId) ?? 0;
|
|
21956
|
-
if ((
|
|
21957
|
-
this._processTokenTransactions(tokenId, txf, startIndex);
|
|
21973
|
+
if (await this._scanTokenForAttribution(token, startIndex)) {
|
|
21958
21974
|
if (this.destroyed) return;
|
|
21959
21975
|
await this._flushDirtyLedgerEntries();
|
|
21960
21976
|
}
|
|
@@ -27392,29 +27408,421 @@ async function parseAndDecryptWalletDat(data, password, onProgress) {
|
|
|
27392
27408
|
};
|
|
27393
27409
|
}
|
|
27394
27410
|
|
|
27411
|
+
// token-engine/identity.ts
|
|
27412
|
+
var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
27413
|
+
var SIGNING_ALGORITHM = "secp256k1";
|
|
27414
|
+
var EMBEDDED_PREDICATE_UNMASKED = 0;
|
|
27415
|
+
var HASH_ALGORITHM_SHA256 = 0n;
|
|
27416
|
+
var SHA256_IMPRINT_PREFIX = new Uint8Array([0, 0]);
|
|
27417
|
+
function hexToBytes4(hex) {
|
|
27418
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
27419
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
27420
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
27421
|
+
}
|
|
27422
|
+
return bytes;
|
|
27423
|
+
}
|
|
27424
|
+
function sha2565(data) {
|
|
27425
|
+
return new DataHasher(HashAlgorithm2.SHA256).update(data).digest().then((h) => h.data);
|
|
27426
|
+
}
|
|
27427
|
+
async function deriveDirectAddress(publicKey) {
|
|
27428
|
+
const tokenTypeCbor = CborSerializer.encodeByteString(hexToBytes4(UNICITY_TOKEN_TYPE_HEX));
|
|
27429
|
+
const reference = CborSerializer.encodeArray(
|
|
27430
|
+
CborSerializer.encodeByteString(new Uint8Array([EMBEDDED_PREDICATE_UNMASKED])),
|
|
27431
|
+
CborSerializer.encodeByteString(tokenTypeCbor),
|
|
27432
|
+
CborSerializer.encodeTextString(SIGNING_ALGORITHM),
|
|
27433
|
+
CborSerializer.encodeUnsignedInteger(HASH_ALGORITHM_SHA256),
|
|
27434
|
+
CborSerializer.encodeByteString(publicKey)
|
|
27435
|
+
);
|
|
27436
|
+
const refHash = await sha2565(reference);
|
|
27437
|
+
const imprint = new Uint8Array([...SHA256_IMPRINT_PREFIX, ...refHash]);
|
|
27438
|
+
const checksum = (await sha2565(imprint)).slice(0, 4);
|
|
27439
|
+
return `DIRECT://${HexConverter.encode(imprint)}${HexConverter.encode(checksum)}`;
|
|
27440
|
+
}
|
|
27441
|
+
|
|
27442
|
+
// token-engine/factory.ts
|
|
27443
|
+
init_errors();
|
|
27444
|
+
|
|
27445
|
+
// token-engine/SpherePaymentData.ts
|
|
27446
|
+
init_errors();
|
|
27447
|
+
var COIN_ID_PATTERN = /^([0-9a-f]{2})+$/;
|
|
27448
|
+
function assertAsset(coinId, amount) {
|
|
27449
|
+
if (!COIN_ID_PATTERN.test(coinId)) {
|
|
27450
|
+
throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
|
|
27451
|
+
}
|
|
27452
|
+
if (amount < 0n) {
|
|
27453
|
+
throw new SphereError(`Asset amount must be non-negative: ${amount.toString()}`, "VALIDATION_ERROR");
|
|
27454
|
+
}
|
|
27455
|
+
}
|
|
27456
|
+
function sphereAssetToSdk(coinId, amount) {
|
|
27457
|
+
assertAsset(coinId, amount);
|
|
27458
|
+
return new Asset(new AssetId(HexConverter.decode(coinId)), amount);
|
|
27459
|
+
}
|
|
27460
|
+
var SpherePaymentData = class _SpherePaymentData {
|
|
27461
|
+
constructor(assets, _memo = null) {
|
|
27462
|
+
this.assets = assets;
|
|
27463
|
+
this._memo = _memo;
|
|
27464
|
+
}
|
|
27465
|
+
/** Sphere-private CBOR tag (verified free in the v2 SDK tag space). */
|
|
27466
|
+
static CBOR_TAG = 39050n;
|
|
27467
|
+
/** Envelope version; bump when the structure changes. */
|
|
27468
|
+
static VERSION = 1n;
|
|
27469
|
+
/** Opaque, app-defined memo carried alongside the value (e.g. invoice attribution). */
|
|
27470
|
+
get memo() {
|
|
27471
|
+
return this._memo ? new Uint8Array(this._memo) : null;
|
|
27472
|
+
}
|
|
27473
|
+
/** Wrap an existing SDK asset collection (+ optional opaque memo). */
|
|
27474
|
+
static create(assets, memo = null) {
|
|
27475
|
+
return new _SpherePaymentData(assets, memo);
|
|
27476
|
+
}
|
|
27477
|
+
/** Build from a sphere-domain value (hex coin id → bigint amount) + optional opaque memo. */
|
|
27478
|
+
static fromValue(value, memo = null) {
|
|
27479
|
+
const assets = value.assets.map((a) => sphereAssetToSdk(a.coinId, a.amount));
|
|
27480
|
+
return new _SpherePaymentData(PaymentAssetCollection.create(...assets), memo);
|
|
27481
|
+
}
|
|
27482
|
+
/** Decode from the CBOR envelope produced by {@link encode}. */
|
|
27483
|
+
static fromCBOR(bytes) {
|
|
27484
|
+
const tag = CborDeserializer.decodeTag(bytes);
|
|
27485
|
+
if (tag.tag !== _SpherePaymentData.CBOR_TAG) {
|
|
27486
|
+
throw new CborError(`Invalid SpherePaymentData tag: ${tag.tag}`);
|
|
27487
|
+
}
|
|
27488
|
+
const fields = CborDeserializer.decodeArray(tag.data, 3);
|
|
27489
|
+
const version = CborDeserializer.decodeUnsignedInteger(fields[0]);
|
|
27490
|
+
if (version !== _SpherePaymentData.VERSION) {
|
|
27491
|
+
throw new CborError(`Unsupported SpherePaymentData version: ${version}`);
|
|
27492
|
+
}
|
|
27493
|
+
const memo = CborDeserializer.decodeNullable(fields[2], CborDeserializer.decodeByteString);
|
|
27494
|
+
return new _SpherePaymentData(PaymentAssetCollection.fromCBOR(fields[1]), memo);
|
|
27495
|
+
}
|
|
27496
|
+
/** Deterministic, versioned, tagged CBOR: `tag(39050)[ version, assets, memo? ]`. */
|
|
27497
|
+
encode() {
|
|
27498
|
+
return Promise.resolve(
|
|
27499
|
+
CborSerializer.encodeTag(
|
|
27500
|
+
_SpherePaymentData.CBOR_TAG,
|
|
27501
|
+
CborSerializer.encodeArray(
|
|
27502
|
+
CborSerializer.encodeUnsignedInteger(_SpherePaymentData.VERSION),
|
|
27503
|
+
this.assets.toCBOR(),
|
|
27504
|
+
CborSerializer.encodeNullable(this._memo, CborSerializer.encodeByteString)
|
|
27505
|
+
)
|
|
27506
|
+
)
|
|
27507
|
+
);
|
|
27508
|
+
}
|
|
27509
|
+
/** Project to a sphere-domain value (hex coin id + bigint amount), preserving order. */
|
|
27510
|
+
toValue() {
|
|
27511
|
+
return {
|
|
27512
|
+
assets: this.assets.toArray().map((a) => ({
|
|
27513
|
+
coinId: HexConverter.encode(a.id.bytes),
|
|
27514
|
+
amount: a.value
|
|
27515
|
+
}))
|
|
27516
|
+
};
|
|
27517
|
+
}
|
|
27518
|
+
/** Balance of a single coin within this payload (0n when absent). */
|
|
27519
|
+
balanceOf(coinId) {
|
|
27520
|
+
if (!COIN_ID_PATTERN.test(coinId)) {
|
|
27521
|
+
throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
|
|
27522
|
+
}
|
|
27523
|
+
const asset = this.assets.get(new AssetId(HexConverter.decode(coinId)));
|
|
27524
|
+
return asset ? asset.value : 0n;
|
|
27525
|
+
}
|
|
27526
|
+
};
|
|
27527
|
+
function decodeSpherePaymentData(bytes) {
|
|
27528
|
+
return Promise.resolve(SpherePaymentData.fromCBOR(bytes));
|
|
27529
|
+
}
|
|
27530
|
+
|
|
27531
|
+
// token-engine/SphereTokenEngine.ts
|
|
27532
|
+
init_errors();
|
|
27533
|
+
var SphereTokenEngine = class {
|
|
27534
|
+
constructor(deps) {
|
|
27535
|
+
this.deps = deps;
|
|
27536
|
+
}
|
|
27537
|
+
// ── identity ────────────────────────────────────────────────────────────────
|
|
27538
|
+
getIdentity() {
|
|
27539
|
+
return { chainPubkey: new Uint8Array(this.deps.signingService.publicKey) };
|
|
27540
|
+
}
|
|
27541
|
+
/** Legacy DIRECT:// address (Path A). Async: the derivation hashes via the SDK. */
|
|
27542
|
+
deriveIdentityAddress(pubkey) {
|
|
27543
|
+
return deriveDirectAddress(pubkey ?? this.deps.signingService.publicKey);
|
|
27544
|
+
}
|
|
27545
|
+
// ── value (read) ─────────────────────────────────────────────────────────────
|
|
27546
|
+
readValue(token) {
|
|
27547
|
+
return token.value;
|
|
27548
|
+
}
|
|
27549
|
+
balanceOf(token, coinId) {
|
|
27550
|
+
let sum = 0n;
|
|
27551
|
+
for (const asset of token.value?.assets ?? []) {
|
|
27552
|
+
if (asset.coinId === coinId) sum += asset.amount;
|
|
27553
|
+
}
|
|
27554
|
+
return sum;
|
|
27555
|
+
}
|
|
27556
|
+
tokenId(token) {
|
|
27557
|
+
return token.blob.tokenId;
|
|
27558
|
+
}
|
|
27559
|
+
readMemo(token) {
|
|
27560
|
+
const sdkToken = token.sdkToken;
|
|
27561
|
+
if (sdkToken.transactions.length > 0) {
|
|
27562
|
+
return sdkToken.latestTransaction.data;
|
|
27563
|
+
}
|
|
27564
|
+
const data = sdkToken.genesis.data;
|
|
27565
|
+
if (data && this.isSpherePaymentData(data)) {
|
|
27566
|
+
return SpherePaymentData.fromCBOR(data).memo;
|
|
27567
|
+
}
|
|
27568
|
+
return null;
|
|
27569
|
+
}
|
|
27570
|
+
readTokenData(token) {
|
|
27571
|
+
const data = token.sdkToken.genesis.data;
|
|
27572
|
+
return data ? new Uint8Array(data) : null;
|
|
27573
|
+
}
|
|
27574
|
+
// ── lifecycle ────────────────────────────────────────────────────────────────
|
|
27575
|
+
async mint(params, options) {
|
|
27576
|
+
const recipient = SignaturePredicate.create(params.recipientPubkey);
|
|
27577
|
+
const data = params.value ? await SpherePaymentData.fromValue(params.value).encode() : null;
|
|
27578
|
+
const mintTx = await MintTransaction.create(this.deps.networkId, recipient, data);
|
|
27579
|
+
const certificationData = await CertificationData.fromMintTransaction(mintTx);
|
|
27580
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
27581
|
+
if (response.status !== CertificationStatus.SUCCESS) {
|
|
27582
|
+
throw new SphereError(`Mint certification failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
27583
|
+
}
|
|
27584
|
+
const proof = await waitInclusionProof2(
|
|
27585
|
+
this.deps.client,
|
|
27586
|
+
this.deps.trustBase,
|
|
27587
|
+
this.deps.predicateVerifier,
|
|
27588
|
+
mintTx,
|
|
27589
|
+
options?.signal
|
|
27590
|
+
);
|
|
27591
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
27592
|
+
const token = await Token2.mint(
|
|
27593
|
+
this.deps.trustBase,
|
|
27594
|
+
this.deps.predicateVerifier,
|
|
27595
|
+
this.deps.mintJustificationVerifier,
|
|
27596
|
+
certified
|
|
27597
|
+
);
|
|
27598
|
+
return this.wrapToken(token);
|
|
27599
|
+
}
|
|
27600
|
+
async mintDataToken(params, options) {
|
|
27601
|
+
const recipient = SignaturePredicate.create(params.recipientPubkey);
|
|
27602
|
+
const tokenType = params.tokenType ? new TokenType(params.tokenType) : TokenType.generate();
|
|
27603
|
+
const salt = params.salt ? TokenSalt.fromBytes(params.salt) : TokenSalt.generate();
|
|
27604
|
+
const mintTx = await MintTransaction.create(this.deps.networkId, recipient, params.data, tokenType, salt);
|
|
27605
|
+
const certificationData = await CertificationData.fromMintTransaction(mintTx);
|
|
27606
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
27607
|
+
if (response.status !== CertificationStatus.SUCCESS) {
|
|
27608
|
+
throw new SphereError(`Data-token mint failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
27609
|
+
}
|
|
27610
|
+
const proof = await waitInclusionProof2(
|
|
27611
|
+
this.deps.client,
|
|
27612
|
+
this.deps.trustBase,
|
|
27613
|
+
this.deps.predicateVerifier,
|
|
27614
|
+
mintTx,
|
|
27615
|
+
options?.signal
|
|
27616
|
+
);
|
|
27617
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
27618
|
+
const token = await Token2.mint(
|
|
27619
|
+
this.deps.trustBase,
|
|
27620
|
+
this.deps.predicateVerifier,
|
|
27621
|
+
this.deps.mintJustificationVerifier,
|
|
27622
|
+
certified
|
|
27623
|
+
);
|
|
27624
|
+
return this.wrapToken(token);
|
|
27625
|
+
}
|
|
27626
|
+
async transfer(params, options) {
|
|
27627
|
+
this.assertOwned(params.token);
|
|
27628
|
+
const recipient = SignaturePredicate.create(params.recipientPubkey);
|
|
27629
|
+
const stateMask = crypto.getRandomValues(new Uint8Array(32));
|
|
27630
|
+
const transferTx = await TransferTransaction.create(params.token.sdkToken, recipient, stateMask, params.data ?? null);
|
|
27631
|
+
const unlockScript = await SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);
|
|
27632
|
+
const certificationData = await CertificationData.fromTransaction(transferTx, unlockScript);
|
|
27633
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
27634
|
+
if (response.status !== CertificationStatus.SUCCESS) {
|
|
27635
|
+
throw new SphereError(`Transfer certification failed: ${response.status}`, "TRANSFER_FAILED");
|
|
27636
|
+
}
|
|
27637
|
+
const proof = await waitInclusionProof2(
|
|
27638
|
+
this.deps.client,
|
|
27639
|
+
this.deps.trustBase,
|
|
27640
|
+
this.deps.predicateVerifier,
|
|
27641
|
+
transferTx,
|
|
27642
|
+
options?.signal
|
|
27643
|
+
);
|
|
27644
|
+
const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
27645
|
+
const transferred = await params.token.sdkToken.transfer(this.deps.trustBase, this.deps.predicateVerifier, certified);
|
|
27646
|
+
return this.wrapToken(transferred);
|
|
27647
|
+
}
|
|
27648
|
+
async split(params, options) {
|
|
27649
|
+
this.assertOwned(params.token);
|
|
27650
|
+
if (params.outputs.length === 0) {
|
|
27651
|
+
throw new SphereError("Split requires at least one output", "VALIDATION_ERROR");
|
|
27652
|
+
}
|
|
27653
|
+
const requests = params.outputs.map(
|
|
27654
|
+
(o) => SplitTokenRequest.create(
|
|
27655
|
+
SignaturePredicate.create(o.recipientPubkey),
|
|
27656
|
+
PaymentAssetCollection.create(sphereAssetToSdk(o.coinId, o.amount))
|
|
27657
|
+
)
|
|
27658
|
+
);
|
|
27659
|
+
const split = await TokenSplit.split(params.token.sdkToken, decodeSpherePaymentData, requests);
|
|
27660
|
+
const burnUnlock = await SignaturePredicateUnlockScript.create(split.burn.transaction, this.deps.signingService);
|
|
27661
|
+
const burnCert = await CertificationData.fromTransaction(split.burn.transaction, burnUnlock);
|
|
27662
|
+
const burnResponse = await this.deps.client.submitCertificationRequest(burnCert);
|
|
27663
|
+
if (burnResponse.status !== CertificationStatus.SUCCESS) {
|
|
27664
|
+
throw new SphereError(`Split burn failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
27665
|
+
}
|
|
27666
|
+
const burnProof = await waitInclusionProof2(
|
|
27667
|
+
this.deps.client,
|
|
27668
|
+
this.deps.trustBase,
|
|
27669
|
+
this.deps.predicateVerifier,
|
|
27670
|
+
split.burn.transaction,
|
|
27671
|
+
options?.signal
|
|
27672
|
+
);
|
|
27673
|
+
const burnCertified = await split.burn.transaction.toCertifiedTransaction(
|
|
27674
|
+
this.deps.trustBase,
|
|
27675
|
+
this.deps.predicateVerifier,
|
|
27676
|
+
burnProof
|
|
27677
|
+
);
|
|
27678
|
+
const burntToken = await params.token.sdkToken.transfer(
|
|
27679
|
+
this.deps.trustBase,
|
|
27680
|
+
this.deps.predicateVerifier,
|
|
27681
|
+
burnCertified
|
|
27682
|
+
);
|
|
27683
|
+
const outputs = [];
|
|
27684
|
+
for (let i = 0; i < split.tokens.length; i++) {
|
|
27685
|
+
const splitToken = split.tokens[i];
|
|
27686
|
+
const data = await SpherePaymentData.create(splitToken.assets, params.outputs[i].data ?? null).encode();
|
|
27687
|
+
const justification = SplitMintJustification.create(burntToken, splitToken.proofs).toCBOR();
|
|
27688
|
+
const mintTx = await MintTransaction.create(
|
|
27689
|
+
splitToken.networkId,
|
|
27690
|
+
splitToken.recipient,
|
|
27691
|
+
data,
|
|
27692
|
+
splitToken.tokenType,
|
|
27693
|
+
splitToken.salt,
|
|
27694
|
+
justification
|
|
27695
|
+
);
|
|
27696
|
+
const certData = await CertificationData.fromMintTransaction(mintTx);
|
|
27697
|
+
const response = await this.deps.client.submitCertificationRequest(certData);
|
|
27698
|
+
if (response.status !== CertificationStatus.SUCCESS) {
|
|
27699
|
+
throw new SphereError(`Split mint failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
27700
|
+
}
|
|
27701
|
+
const proof = await waitInclusionProof2(
|
|
27702
|
+
this.deps.client,
|
|
27703
|
+
this.deps.trustBase,
|
|
27704
|
+
this.deps.predicateVerifier,
|
|
27705
|
+
mintTx,
|
|
27706
|
+
options?.signal
|
|
27707
|
+
);
|
|
27708
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
27709
|
+
const token = await Token2.mint(
|
|
27710
|
+
this.deps.trustBase,
|
|
27711
|
+
this.deps.predicateVerifier,
|
|
27712
|
+
this.deps.mintJustificationVerifier,
|
|
27713
|
+
certified
|
|
27714
|
+
);
|
|
27715
|
+
outputs.push(this.wrapToken(token));
|
|
27716
|
+
}
|
|
27717
|
+
return { outputs };
|
|
27718
|
+
}
|
|
27719
|
+
// ── verification ─────────────────────────────────────────────────────────────
|
|
27720
|
+
async verify(token, _options) {
|
|
27721
|
+
const result = await token.sdkToken.verify(
|
|
27722
|
+
this.deps.trustBase,
|
|
27723
|
+
this.deps.predicateVerifier,
|
|
27724
|
+
this.deps.mintJustificationVerifier
|
|
27725
|
+
);
|
|
27726
|
+
return result.status === VerificationStatus.OK ? { ok: true } : { ok: false, reason: String(result.status) };
|
|
27727
|
+
}
|
|
27728
|
+
async isSpent(token, _options) {
|
|
27729
|
+
const probe = await TransferTransaction.create(
|
|
27730
|
+
token.sdkToken,
|
|
27731
|
+
SignaturePredicate.create(this.deps.signingService.publicKey),
|
|
27732
|
+
new Uint8Array(32)
|
|
27733
|
+
);
|
|
27734
|
+
const stateId = await StateId.fromTransaction(probe);
|
|
27735
|
+
const response = await this.deps.client.getInclusionProof(stateId);
|
|
27736
|
+
return response.inclusionProof.inclusionCertificate !== null;
|
|
27737
|
+
}
|
|
27738
|
+
// ── serialization ────────────────────────────────────────────────────────────
|
|
27739
|
+
encodeToken(token) {
|
|
27740
|
+
return token.blob;
|
|
27741
|
+
}
|
|
27742
|
+
async decodeToken(blob) {
|
|
27743
|
+
const sdkToken = await Token2.fromCBOR(blob.token);
|
|
27744
|
+
if (sdkToken.genesis.networkId.id !== this.deps.networkId.id) {
|
|
27745
|
+
throw new SphereError(
|
|
27746
|
+
`Token network mismatch: token is on network ${sdkToken.genesis.networkId.id}, engine on ${this.deps.networkId.id}`,
|
|
27747
|
+
"VALIDATION_ERROR"
|
|
27748
|
+
);
|
|
27749
|
+
}
|
|
27750
|
+
return this.wrapToken(sdkToken);
|
|
27751
|
+
}
|
|
27752
|
+
// ── internals ────────────────────────────────────────────────────────────────
|
|
27753
|
+
/** Fail fast if this engine's key does not own the token's current state. */
|
|
27754
|
+
assertOwned(token) {
|
|
27755
|
+
const owner = token.sdkToken.latestTransaction.recipient;
|
|
27756
|
+
const mine = EncodedPredicate.fromPredicate(SignaturePredicate.create(this.deps.signingService.publicKey));
|
|
27757
|
+
if (!EncodedPredicate.equals(owner, mine)) {
|
|
27758
|
+
throw new SphereError("Cannot transfer a token not owned by this engine identity", "VALIDATION_ERROR");
|
|
27759
|
+
}
|
|
27760
|
+
}
|
|
27761
|
+
/** Wrap an SDK token into a SphereToken: cache its blob (incl. stable tokenId) + decoded value. */
|
|
27762
|
+
wrapToken(sdkToken) {
|
|
27763
|
+
const data = sdkToken.genesis.data;
|
|
27764
|
+
let value = null;
|
|
27765
|
+
if (data && this.isSpherePaymentData(data)) {
|
|
27766
|
+
try {
|
|
27767
|
+
value = SpherePaymentData.fromCBOR(data).toValue();
|
|
27768
|
+
} catch (err) {
|
|
27769
|
+
throw new SphereError(
|
|
27770
|
+
`Failed to decode token payment data: ${err instanceof Error ? err.message : String(err)}`,
|
|
27771
|
+
"VALIDATION_ERROR"
|
|
27772
|
+
);
|
|
27773
|
+
}
|
|
27774
|
+
}
|
|
27775
|
+
const blob = {
|
|
27776
|
+
v: TOKEN_BLOB_VERSION,
|
|
27777
|
+
network: sdkToken.genesis.networkId.id,
|
|
27778
|
+
tokenId: HexConverter.encode(sdkToken.id.bytes),
|
|
27779
|
+
token: sdkToken.toCBOR()
|
|
27780
|
+
};
|
|
27781
|
+
return { sdkToken, blob, value };
|
|
27782
|
+
}
|
|
27783
|
+
/** True if the bytes are a SpherePaymentData envelope (value token) vs a raw data token. */
|
|
27784
|
+
isSpherePaymentData(data) {
|
|
27785
|
+
try {
|
|
27786
|
+
return CborDeserializer.decodeTag(data).tag === SpherePaymentData.CBOR_TAG;
|
|
27787
|
+
} catch {
|
|
27788
|
+
return false;
|
|
27789
|
+
}
|
|
27790
|
+
}
|
|
27791
|
+
};
|
|
27792
|
+
|
|
27793
|
+
// token-engine/factory.ts
|
|
27794
|
+
async function createSphereTokenEngine(config) {
|
|
27795
|
+
if (config.trustBaseJson == null) {
|
|
27796
|
+
throw new SphereError("Engine config requires a trust base (trustBaseJson)", "INVALID_CONFIG");
|
|
27797
|
+
}
|
|
27798
|
+
const trustBase = RootTrustBase.fromJSON(config.trustBaseJson);
|
|
27799
|
+
const predicateVerifier = PredicateVerifierService.create();
|
|
27800
|
+
const mintJustificationVerifier = new MintJustificationVerifierService();
|
|
27801
|
+
mintJustificationVerifier.register(
|
|
27802
|
+
new SplitMintJustificationVerifier(trustBase, predicateVerifier, decodeSpherePaymentData)
|
|
27803
|
+
);
|
|
27804
|
+
const deps = {
|
|
27805
|
+
client: new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
|
|
27806
|
+
trustBase,
|
|
27807
|
+
predicateVerifier,
|
|
27808
|
+
mintJustificationVerifier,
|
|
27809
|
+
signingService: new SigningService(config.privateKey),
|
|
27810
|
+
// The trust base is the single source of truth for the network id (it carries
|
|
27811
|
+
// NetworkId.fromId, so any id works — e.g. testnet2 = 4 — with no enum entry).
|
|
27812
|
+
networkId: trustBase.networkId
|
|
27813
|
+
};
|
|
27814
|
+
return new SphereTokenEngine(deps);
|
|
27815
|
+
}
|
|
27816
|
+
|
|
27395
27817
|
// core/Sphere.ts
|
|
27396
|
-
import {
|
|
27397
|
-
import { TokenType as TokenType5 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
27398
|
-
import { HashAlgorithm as HashAlgorithm7 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
27399
|
-
import { UnmaskedPredicateReference as UnmaskedPredicateReference3 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference";
|
|
27400
|
-
import { normalizeNametag as normalizeNametag2, isPhoneNumber } from "@unicitylabs/nostr-js-sdk";
|
|
27818
|
+
import { normalizeNametag, isPhoneNumber } from "@unicitylabs/nostr-js-sdk";
|
|
27401
27819
|
function isValidNametag2(nametag) {
|
|
27402
27820
|
if (isPhoneNumber(nametag)) return true;
|
|
27403
27821
|
return /^[a-z0-9_-]{3,20}$/.test(nametag);
|
|
27404
27822
|
}
|
|
27405
|
-
var UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
27406
27823
|
async function deriveL3PredicateAddress(privateKey) {
|
|
27407
|
-
const
|
|
27408
|
-
|
|
27409
|
-
const tokenTypeBytes = Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex");
|
|
27410
|
-
const tokenType = new TokenType5(tokenTypeBytes);
|
|
27411
|
-
const predicateRef = UnmaskedPredicateReference3.create(
|
|
27412
|
-
tokenType,
|
|
27413
|
-
signingService.algorithm,
|
|
27414
|
-
signingService.publicKey,
|
|
27415
|
-
HashAlgorithm7.SHA256
|
|
27416
|
-
);
|
|
27417
|
-
return (await (await predicateRef).toAddress()).toString();
|
|
27824
|
+
const prehashedPublicKey = getPublicKey(sha2562(privateKey, "hex"));
|
|
27825
|
+
return deriveDirectAddress(hexToBytes2(prehashedPublicKey));
|
|
27418
27826
|
}
|
|
27419
27827
|
var Sphere = class _Sphere {
|
|
27420
27828
|
// Singleton
|
|
@@ -27436,14 +27844,14 @@ var Sphere = class _Sphere {
|
|
|
27436
27844
|
_addressIdToIndex = /* @__PURE__ */ new Map();
|
|
27437
27845
|
/** Nametag cache: addressId -> (nametagIndex -> nametag). Separate from tracked addresses. */
|
|
27438
27846
|
_addressNametags = /* @__PURE__ */ new Map();
|
|
27439
|
-
/** Cached PROXY address (computed once when nametag is set) */
|
|
27440
|
-
_cachedProxyAddress = void 0;
|
|
27441
27847
|
// Providers
|
|
27442
27848
|
_storage;
|
|
27443
27849
|
_tokenStorageProviders = /* @__PURE__ */ new Map();
|
|
27444
27850
|
_transport;
|
|
27445
27851
|
_oracle;
|
|
27446
27852
|
_priceProvider;
|
|
27853
|
+
/** v2 token engine (built per active address from the oracle); injected into modules. */
|
|
27854
|
+
_tokenEngine;
|
|
27447
27855
|
// Modules (single-instance — backward compat, delegates to active address)
|
|
27448
27856
|
_payments;
|
|
27449
27857
|
_communications;
|
|
@@ -27784,20 +28192,6 @@ var Sphere = class _Sphere {
|
|
|
27784
28192
|
await sphere.syncIdentityWithTransport();
|
|
27785
28193
|
sphere._initialized = true;
|
|
27786
28194
|
_Sphere.instance = sphere;
|
|
27787
|
-
if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {
|
|
27788
|
-
progress?.({ step: "registering_nametag", message: "Restoring nametag token..." });
|
|
27789
|
-
logger.debug("Sphere", `Unicity ID @${sphere._identity.nametag} has no token, attempting to mint...`);
|
|
27790
|
-
try {
|
|
27791
|
-
const result = await sphere.mintNametag(sphere._identity.nametag);
|
|
27792
|
-
if (result.success) {
|
|
27793
|
-
logger.debug("Sphere", `Nametag token minted successfully on load`);
|
|
27794
|
-
} else {
|
|
27795
|
-
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
27796
|
-
}
|
|
27797
|
-
} catch (err) {
|
|
27798
|
-
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
27799
|
-
}
|
|
27800
|
-
}
|
|
27801
28195
|
if (options.discoverAddresses !== false && sphere._transport.discoverAddresses && sphere._masterKey) {
|
|
27802
28196
|
progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
|
|
27803
28197
|
try {
|
|
@@ -28849,13 +29243,13 @@ var Sphere = class _Sphere {
|
|
|
28849
29243
|
oracle: this._oracle,
|
|
28850
29244
|
emitEvent: this.emitEvent.bind(this),
|
|
28851
29245
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
28852
|
-
price: this._priceProvider ?? void 0
|
|
29246
|
+
price: this._priceProvider ?? void 0,
|
|
29247
|
+
tokenEngine: moduleSet.tokenEngine
|
|
28853
29248
|
});
|
|
28854
29249
|
}
|
|
28855
29250
|
}
|
|
28856
29251
|
this._identity = newIdentity;
|
|
28857
29252
|
this._currentAddressIndex = index;
|
|
28858
|
-
await this._updateCachedProxyAddress();
|
|
28859
29253
|
const activeModules = this._addressModules.get(index);
|
|
28860
29254
|
this._payments = activeModules.payments;
|
|
28861
29255
|
this._communications = activeModules.communications;
|
|
@@ -28893,35 +29287,10 @@ var Sphere = class _Sphere {
|
|
|
28893
29287
|
}
|
|
28894
29288
|
if (newNametag) {
|
|
28895
29289
|
await this.persistAddressNametags();
|
|
28896
|
-
if (!this._payments.hasNametag()) {
|
|
28897
|
-
logger.debug("Sphere", `Minting nametag token for @${newNametag}...`);
|
|
28898
|
-
try {
|
|
28899
|
-
const result = await this.mintNametag(newNametag);
|
|
28900
|
-
if (result.success) {
|
|
28901
|
-
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
28902
|
-
} else {
|
|
28903
|
-
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
28904
|
-
}
|
|
28905
|
-
} catch (err) {
|
|
28906
|
-
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
28907
|
-
}
|
|
28908
|
-
}
|
|
28909
29290
|
this.emitEvent("nametag:registered", {
|
|
28910
29291
|
nametag: newNametag,
|
|
28911
29292
|
addressIndex: index
|
|
28912
29293
|
});
|
|
28913
|
-
} else if (this._identity?.nametag && !this._payments.hasNametag()) {
|
|
28914
|
-
logger.debug("Sphere", `Unicity ID @${this._identity.nametag} has no token after switch, minting...`);
|
|
28915
|
-
try {
|
|
28916
|
-
const result = await this.mintNametag(this._identity.nametag);
|
|
28917
|
-
if (result.success) {
|
|
28918
|
-
logger.debug("Sphere", `Nametag token minted successfully after switch`);
|
|
28919
|
-
} else {
|
|
28920
|
-
logger.warn("Sphere", `Could not mint nametag token after switch: ${result.error}`);
|
|
28921
|
-
}
|
|
28922
|
-
} catch (err) {
|
|
28923
|
-
logger.warn("Sphere", `Nametag token mint failed after switch:`, err);
|
|
28924
|
-
}
|
|
28925
29294
|
}
|
|
28926
29295
|
}
|
|
28927
29296
|
/**
|
|
@@ -28951,6 +29320,7 @@ var Sphere = class _Sphere {
|
|
|
28951
29320
|
const communications = createCommunicationsModule(this._communicationsConfig);
|
|
28952
29321
|
const groupChat = this._groupChatConfig ? createGroupChatModule(this._groupChatConfig) : null;
|
|
28953
29322
|
const market = this._marketConfig ? createMarketModule(this._marketConfig) : null;
|
|
29323
|
+
const tokenEngine = await this.buildTokenEngine(identity);
|
|
28954
29324
|
payments.initialize({
|
|
28955
29325
|
identity,
|
|
28956
29326
|
storage: this._storage,
|
|
@@ -28959,7 +29329,8 @@ var Sphere = class _Sphere {
|
|
|
28959
29329
|
oracle: this._oracle,
|
|
28960
29330
|
emitEvent,
|
|
28961
29331
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
28962
|
-
price: this._priceProvider ?? void 0
|
|
29332
|
+
price: this._priceProvider ?? void 0,
|
|
29333
|
+
tokenEngine
|
|
28963
29334
|
});
|
|
28964
29335
|
communications.initialize({
|
|
28965
29336
|
identity,
|
|
@@ -28995,7 +29366,8 @@ var Sphere = class _Sphere {
|
|
|
28995
29366
|
emitEvent,
|
|
28996
29367
|
on: this.on.bind(this),
|
|
28997
29368
|
storage: this._storage,
|
|
28998
|
-
communications
|
|
29369
|
+
communications,
|
|
29370
|
+
tokenEngine
|
|
28999
29371
|
});
|
|
29000
29372
|
} else {
|
|
29001
29373
|
logger.warn("Sphere", "Accounting module enabled but no token storage available \u2014 disabling");
|
|
@@ -29055,6 +29427,7 @@ var Sphere = class _Sphere {
|
|
|
29055
29427
|
market,
|
|
29056
29428
|
transportAdapter: adapter,
|
|
29057
29429
|
tokenStorageProviders: new Map(tokenStorageProviders),
|
|
29430
|
+
tokenEngine,
|
|
29058
29431
|
initialized: true
|
|
29059
29432
|
};
|
|
29060
29433
|
this._addressModules.set(index, moduleSet);
|
|
@@ -29610,15 +29983,6 @@ var Sphere = class _Sphere {
|
|
|
29610
29983
|
hasNametag() {
|
|
29611
29984
|
return !!this._identity?.nametag;
|
|
29612
29985
|
}
|
|
29613
|
-
/**
|
|
29614
|
-
* Get the PROXY address for the current nametag
|
|
29615
|
-
* PROXY addresses are derived from the nametag hash and require
|
|
29616
|
-
* the nametag token to claim funds sent to them
|
|
29617
|
-
* @returns PROXY address string or undefined if no nametag
|
|
29618
|
-
*/
|
|
29619
|
-
getProxyAddress() {
|
|
29620
|
-
return this._cachedProxyAddress;
|
|
29621
|
-
}
|
|
29622
29986
|
/**
|
|
29623
29987
|
* Resolve any identifier to full peer information.
|
|
29624
29988
|
* Accepts @nametag, bare nametag, DIRECT://, PROXY://, L1 address, or transport pubkey.
|
|
@@ -29653,17 +30017,6 @@ var Sphere = class _Sphere {
|
|
|
29653
30017
|
throw new SphereError(`Cannot resolve address: ${address.slice(0, 30)}`, "INVALID_RECIPIENT");
|
|
29654
30018
|
}
|
|
29655
30019
|
}
|
|
29656
|
-
/** Compute and cache the PROXY address from the current nametag */
|
|
29657
|
-
async _updateCachedProxyAddress() {
|
|
29658
|
-
const nametag = this._identity?.nametag;
|
|
29659
|
-
if (!nametag) {
|
|
29660
|
-
this._cachedProxyAddress = void 0;
|
|
29661
|
-
return;
|
|
29662
|
-
}
|
|
29663
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
29664
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
29665
|
-
this._cachedProxyAddress = proxyAddr.toString();
|
|
29666
|
-
}
|
|
29667
30020
|
/**
|
|
29668
30021
|
* Register a nametag for the current active address
|
|
29669
30022
|
* Each address can have its own independent nametag
|
|
@@ -29691,17 +30044,6 @@ var Sphere = class _Sphere {
|
|
|
29691
30044
|
if (this._identity?.nametag) {
|
|
29692
30045
|
throw new SphereError(`Unicity ID already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`, "ALREADY_INITIALIZED");
|
|
29693
30046
|
}
|
|
29694
|
-
if (!this._payments.hasNametag()) {
|
|
29695
|
-
logger.debug("Sphere", `Minting nametag token for @${cleanNametag}...`);
|
|
29696
|
-
const result = await this.mintNametag(cleanNametag);
|
|
29697
|
-
if (!result.success) {
|
|
29698
|
-
throw new SphereError(
|
|
29699
|
-
`Failed to mint nametag token: ${result.error}`,
|
|
29700
|
-
"AGGREGATOR_ERROR"
|
|
29701
|
-
);
|
|
29702
|
-
}
|
|
29703
|
-
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
29704
|
-
}
|
|
29705
30047
|
if (this._transport.publishIdentityBinding) {
|
|
29706
30048
|
const success = await this._transport.publishIdentityBinding(
|
|
29707
30049
|
this._identity.chainPubkey,
|
|
@@ -29714,7 +30056,6 @@ var Sphere = class _Sphere {
|
|
|
29714
30056
|
}
|
|
29715
30057
|
}
|
|
29716
30058
|
this._identity.nametag = cleanNametag;
|
|
29717
|
-
await this._updateCachedProxyAddress();
|
|
29718
30059
|
const currentAddressId = this._trackedAddresses.get(this._currentAddressIndex)?.addressId;
|
|
29719
30060
|
if (currentAddressId) {
|
|
29720
30061
|
let nametags = this._addressNametags.get(currentAddressId);
|
|
@@ -29747,35 +30088,19 @@ var Sphere = class _Sphere {
|
|
|
29747
30088
|
await this._storage.saveTrackedAddresses(entries);
|
|
29748
30089
|
}
|
|
29749
30090
|
/**
|
|
29750
|
-
*
|
|
29751
|
-
* This creates the nametag token required for receiving tokens via PROXY addresses (@nametag)
|
|
30091
|
+
* Check whether a nametag is available to register.
|
|
29752
30092
|
*
|
|
29753
|
-
*
|
|
29754
|
-
*
|
|
30093
|
+
* D5: nametags are Nostr bindings (name ↔ chainPubkey), not on-chain tokens. Availability is
|
|
30094
|
+
* first-seen-wins — a name is available iff no binding resolves for it.
|
|
29755
30095
|
*
|
|
29756
|
-
* @example
|
|
29757
|
-
* ```typescript
|
|
29758
|
-
* // Mint nametag token for receiving via @alice
|
|
29759
|
-
* const result = await sphere.mintNametag('alice');
|
|
29760
|
-
* if (result.success) {
|
|
29761
|
-
* console.log('Nametag minted:', result.nametagData?.name);
|
|
29762
|
-
* } else {
|
|
29763
|
-
* console.error('Mint failed:', result.error);
|
|
29764
|
-
* }
|
|
29765
|
-
* ```
|
|
29766
|
-
*/
|
|
29767
|
-
async mintNametag(nametag) {
|
|
29768
|
-
this.ensureReady();
|
|
29769
|
-
return this._payments.mintNametag(nametag);
|
|
29770
|
-
}
|
|
29771
|
-
/**
|
|
29772
|
-
* Check if a nametag is available for minting
|
|
29773
30096
|
* @param nametag - The nametag to check (e.g., "alice" or "@alice")
|
|
29774
|
-
* @returns true if available, false if taken
|
|
30097
|
+
* @returns true if available, false if already taken
|
|
29775
30098
|
*/
|
|
29776
30099
|
async isNametagAvailable(nametag) {
|
|
29777
30100
|
this.ensureReady();
|
|
29778
|
-
|
|
30101
|
+
if (!this._transport.resolveNametag) return true;
|
|
30102
|
+
const bound = await this._transport.resolveNametag(this.cleanNametag(nametag));
|
|
30103
|
+
return bound == null;
|
|
29779
30104
|
}
|
|
29780
30105
|
/**
|
|
29781
30106
|
* Load tracked addresses from storage.
|
|
@@ -29950,7 +30275,6 @@ var Sphere = class _Sphere {
|
|
|
29950
30275
|
}
|
|
29951
30276
|
if (recoveredNametag && !this._identity?.nametag) {
|
|
29952
30277
|
this._identity.nametag = recoveredNametag;
|
|
29953
|
-
await this._updateCachedProxyAddress();
|
|
29954
30278
|
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
29955
30279
|
let nametags = this._addressNametags.get(entry.addressId);
|
|
29956
30280
|
if (!nametags) {
|
|
@@ -30039,7 +30363,6 @@ var Sphere = class _Sphere {
|
|
|
30039
30363
|
try {
|
|
30040
30364
|
if (this._identity) {
|
|
30041
30365
|
this._identity.nametag = recoveredNametag;
|
|
30042
|
-
await this._updateCachedProxyAddress();
|
|
30043
30366
|
}
|
|
30044
30367
|
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
30045
30368
|
let nametags = this._addressNametags.get(entry.addressId);
|
|
@@ -30059,7 +30382,7 @@ var Sphere = class _Sphere {
|
|
|
30059
30382
|
*/
|
|
30060
30383
|
cleanNametag(raw) {
|
|
30061
30384
|
const stripped = raw.startsWith("@") ? raw.slice(1) : raw;
|
|
30062
|
-
return
|
|
30385
|
+
return normalizeNametag(stripped);
|
|
30063
30386
|
}
|
|
30064
30387
|
// ===========================================================================
|
|
30065
30388
|
// Public Methods - Lifecycle
|
|
@@ -30239,7 +30562,6 @@ var Sphere = class _Sphere {
|
|
|
30239
30562
|
} else if (this._identity && nametag) {
|
|
30240
30563
|
this._identity.nametag = nametag;
|
|
30241
30564
|
}
|
|
30242
|
-
await this._updateCachedProxyAddress();
|
|
30243
30565
|
}
|
|
30244
30566
|
async initializeIdentityFromMnemonic(mnemonic, derivationPath) {
|
|
30245
30567
|
const basePath = derivationPath ?? DEFAULT_BASE_PATH;
|
|
@@ -30390,10 +30712,45 @@ var Sphere = class _Sphere {
|
|
|
30390
30712
|
this._providerEventCleanups = [];
|
|
30391
30713
|
this._lastProviderConnected.clear();
|
|
30392
30714
|
}
|
|
30715
|
+
/**
|
|
30716
|
+
* Construct the v2 token engine for a given address identity (defaults to the
|
|
30717
|
+
* active one) from the oracle's gateway URL + trust base and that address's
|
|
30718
|
+
* signing key. The engine is per-address — each address signs with its own key.
|
|
30719
|
+
* The trust base is the single source of truth for the network id (so any id
|
|
30720
|
+
* works — e.g. testnet2 = 4 — with no enum entry). Returns undefined (modules
|
|
30721
|
+
* keep their legacy path) when the oracle can't supply a trust base / url, or
|
|
30722
|
+
* construction fails — a misconfigured oracle never breaks initialization.
|
|
30723
|
+
*/
|
|
30724
|
+
async buildTokenEngine(identity) {
|
|
30725
|
+
const oracle = this._oracle;
|
|
30726
|
+
const privateKey = (identity ?? this._identity)?.privateKey;
|
|
30727
|
+
const trustBaseJson = oracle.getTrustBaseJson?.() ?? null;
|
|
30728
|
+
const aggregatorUrl = oracle.getAggregatorUrl?.();
|
|
30729
|
+
if (!trustBaseJson || !aggregatorUrl || !privateKey) {
|
|
30730
|
+
logger.warn("Sphere", "v2 token engine not constructed (oracle has no trust base / url, or no identity) \u2014 legacy path");
|
|
30731
|
+
return void 0;
|
|
30732
|
+
}
|
|
30733
|
+
try {
|
|
30734
|
+
return await createSphereTokenEngine({
|
|
30735
|
+
aggregatorUrl,
|
|
30736
|
+
apiKey: oracle.getApiKey?.(),
|
|
30737
|
+
privateKey: hexToBytes2(privateKey),
|
|
30738
|
+
trustBaseJson
|
|
30739
|
+
});
|
|
30740
|
+
} catch (err) {
|
|
30741
|
+
logger.warn(
|
|
30742
|
+
"Sphere",
|
|
30743
|
+
`Failed to construct v2 token engine \u2014 modules use the legacy path: ${err instanceof Error ? err.message : String(err)}`
|
|
30744
|
+
);
|
|
30745
|
+
return void 0;
|
|
30746
|
+
}
|
|
30747
|
+
}
|
|
30393
30748
|
async initializeModules() {
|
|
30394
30749
|
const emitEvent = this.emitEvent.bind(this);
|
|
30395
30750
|
const adapter = await this.ensureTransportMux(this._currentAddressIndex, this._identity);
|
|
30396
30751
|
const moduleTransport = adapter ?? this._transport;
|
|
30752
|
+
this._tokenEngine = await this.buildTokenEngine();
|
|
30753
|
+
const tokenEngine = this._tokenEngine;
|
|
30397
30754
|
this._payments.initialize({
|
|
30398
30755
|
identity: this._identity,
|
|
30399
30756
|
storage: this._storage,
|
|
@@ -30404,7 +30761,8 @@ var Sphere = class _Sphere {
|
|
|
30404
30761
|
// Pass chain code for L1 HD derivation
|
|
30405
30762
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
30406
30763
|
price: this._priceProvider ?? void 0,
|
|
30407
|
-
disabledProviderIds: this._disabledProviders
|
|
30764
|
+
disabledProviderIds: this._disabledProviders,
|
|
30765
|
+
tokenEngine
|
|
30408
30766
|
});
|
|
30409
30767
|
this._communications.initialize({
|
|
30410
30768
|
identity: this._identity,
|
|
@@ -30440,7 +30798,8 @@ var Sphere = class _Sphere {
|
|
|
30440
30798
|
emitEvent,
|
|
30441
30799
|
on: this.on.bind(this),
|
|
30442
30800
|
storage: this._storage,
|
|
30443
|
-
communications: this._communications
|
|
30801
|
+
communications: this._communications,
|
|
30802
|
+
tokenEngine
|
|
30444
30803
|
});
|
|
30445
30804
|
} else {
|
|
30446
30805
|
logger.warn("Sphere", "Accounting module enabled but no token storage available \u2014 disabling");
|
|
@@ -30502,6 +30861,7 @@ var Sphere = class _Sphere {
|
|
|
30502
30861
|
market: this._market,
|
|
30503
30862
|
transportAdapter: adapter,
|
|
30504
30863
|
tokenStorageProviders: new Map(this._tokenStorageProviders),
|
|
30864
|
+
tokenEngine: this._tokenEngine,
|
|
30505
30865
|
initialized: true
|
|
30506
30866
|
});
|
|
30507
30867
|
}
|