@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/index.js
CHANGED
|
@@ -1914,26 +1914,14 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
1914
1914
|
}
|
|
1915
1915
|
/**
|
|
1916
1916
|
* Convert a BindingInfo (from nostr-js-sdk) to PeerInfo (sphere-sdk type).
|
|
1917
|
-
* Computes PROXY address from nametag if available.
|
|
1918
1917
|
*/
|
|
1919
1918
|
async bindingInfoToPeerInfo(binding, nametag) {
|
|
1920
|
-
const nametagValue = nametag || binding.nametag;
|
|
1921
|
-
let proxyAddress = binding.proxyAddress;
|
|
1922
|
-
if (nametagValue && !proxyAddress) {
|
|
1923
|
-
try {
|
|
1924
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
1925
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametagValue);
|
|
1926
|
-
proxyAddress = proxyAddr.toString();
|
|
1927
|
-
} catch {
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
1919
|
return {
|
|
1931
|
-
nametag:
|
|
1920
|
+
nametag: nametag || binding.nametag,
|
|
1932
1921
|
transportPubkey: binding.transportPubkey,
|
|
1933
1922
|
chainPubkey: binding.publicKey || "",
|
|
1934
1923
|
l1Address: binding.l1Address || "",
|
|
1935
1924
|
directAddress: binding.directAddress || "",
|
|
1936
|
-
proxyAddress,
|
|
1937
1925
|
timestamp: binding.timestamp
|
|
1938
1926
|
};
|
|
1939
1927
|
}
|
|
@@ -1959,7 +1947,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
1959
1947
|
chainPubkey: content.public_key || "",
|
|
1960
1948
|
l1Address: content.l1_address || "",
|
|
1961
1949
|
directAddress: content.direct_address || "",
|
|
1962
|
-
proxyAddress: content.proxy_address || void 0,
|
|
1963
1950
|
timestamp: bindingEvent.created_at * 1e3
|
|
1964
1951
|
};
|
|
1965
1952
|
} catch {
|
|
@@ -2002,7 +1989,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2002
1989
|
chainPubkey: content.public_key || "",
|
|
2003
1990
|
l1Address: content.l1_address || "",
|
|
2004
1991
|
directAddress: content.direct_address || "",
|
|
2005
|
-
proxyAddress: content.proxy_address || void 0,
|
|
2006
1992
|
timestamp: event.created_at * 1e3
|
|
2007
1993
|
});
|
|
2008
1994
|
} catch {
|
|
@@ -2072,8 +2058,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2072
2058
|
}
|
|
2073
2059
|
const nostrPubkey = this.getNostrPubkey();
|
|
2074
2060
|
if (nametag) {
|
|
2075
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
2076
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
2077
2061
|
try {
|
|
2078
2062
|
const success2 = await this.nostrClient.publishNametagBinding(
|
|
2079
2063
|
nametag,
|
|
@@ -2081,8 +2065,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2081
2065
|
{
|
|
2082
2066
|
publicKey: chainPubkey,
|
|
2083
2067
|
l1Address,
|
|
2084
|
-
directAddress
|
|
2085
|
-
proxyAddress: proxyAddr.toString()
|
|
2068
|
+
directAddress
|
|
2086
2069
|
}
|
|
2087
2070
|
);
|
|
2088
2071
|
if (success2) {
|
|
@@ -6286,6 +6269,13 @@ function createL1PaymentsModule(config) {
|
|
|
6286
6269
|
return new L1PaymentsModule(config);
|
|
6287
6270
|
}
|
|
6288
6271
|
|
|
6272
|
+
// types/v2-transfer.ts
|
|
6273
|
+
function isV2TransferPayload(obj) {
|
|
6274
|
+
if (!obj || typeof obj !== "object") return false;
|
|
6275
|
+
const p = obj;
|
|
6276
|
+
return p.type === "V2_TRANSFER" && typeof p.tokenBlob === "string" && p.tokenBlob.length > 0;
|
|
6277
|
+
}
|
|
6278
|
+
|
|
6289
6279
|
// modules/payments/TokenSplitExecutor.ts
|
|
6290
6280
|
init_logger();
|
|
6291
6281
|
init_errors();
|
|
@@ -6661,16 +6651,115 @@ init_logger();
|
|
|
6661
6651
|
init_errors();
|
|
6662
6652
|
import { Token as SdkToken } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
6663
6653
|
import { CoinId as CoinId2 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
6654
|
+
|
|
6655
|
+
// token-engine/sdk.ts
|
|
6656
|
+
import { StateTransitionClient } from "state-transition-sdk-v2/lib/StateTransitionClient.js";
|
|
6657
|
+
import { AggregatorClient } from "state-transition-sdk-v2/lib/api/AggregatorClient.js";
|
|
6658
|
+
import { NetworkId } from "state-transition-sdk-v2/lib/api/NetworkId.js";
|
|
6659
|
+
import { CertificationData } from "state-transition-sdk-v2/lib/api/CertificationData.js";
|
|
6660
|
+
import { CertificationResponse, CertificationStatus } from "state-transition-sdk-v2/lib/api/CertificationResponse.js";
|
|
6661
|
+
import { StateId } from "state-transition-sdk-v2/lib/api/StateId.js";
|
|
6662
|
+
import { InclusionProof } from "state-transition-sdk-v2/lib/api/InclusionProof.js";
|
|
6663
|
+
import { InclusionProofResponse } from "state-transition-sdk-v2/lib/api/InclusionProofResponse.js";
|
|
6664
|
+
import { RootTrustBase } from "state-transition-sdk-v2/lib/api/bft/RootTrustBase.js";
|
|
6665
|
+
import { waitInclusionProof as waitInclusionProof2 } from "state-transition-sdk-v2/lib/util/InclusionProofUtils.js";
|
|
6666
|
+
import { Token as Token2 } from "state-transition-sdk-v2/lib/transaction/Token.js";
|
|
6667
|
+
import { MintTransaction } from "state-transition-sdk-v2/lib/transaction/MintTransaction.js";
|
|
6668
|
+
import { TransferTransaction } from "state-transition-sdk-v2/lib/transaction/TransferTransaction.js";
|
|
6669
|
+
import { CertifiedMintTransaction } from "state-transition-sdk-v2/lib/transaction/CertifiedMintTransaction.js";
|
|
6670
|
+
import { CertifiedTransferTransaction } from "state-transition-sdk-v2/lib/transaction/CertifiedTransferTransaction.js";
|
|
6671
|
+
import { TokenId as TokenId2 } from "state-transition-sdk-v2/lib/transaction/TokenId.js";
|
|
6672
|
+
import { TokenType } from "state-transition-sdk-v2/lib/transaction/TokenType.js";
|
|
6673
|
+
import { TokenSalt } from "state-transition-sdk-v2/lib/transaction/TokenSalt.js";
|
|
6674
|
+
import { MintJustificationVerifierService } from "state-transition-sdk-v2/lib/transaction/verification/MintJustificationVerifierService.js";
|
|
6675
|
+
import { EncodedPredicate } from "state-transition-sdk-v2/lib/predicate/EncodedPredicate.js";
|
|
6676
|
+
import { SignaturePredicate } from "state-transition-sdk-v2/lib/predicate/builtin/SignaturePredicate.js";
|
|
6677
|
+
import { SignaturePredicateUnlockScript } from "state-transition-sdk-v2/lib/predicate/builtin/SignaturePredicateUnlockScript.js";
|
|
6678
|
+
import { BurnPredicate } from "state-transition-sdk-v2/lib/predicate/builtin/BurnPredicate.js";
|
|
6679
|
+
import { PredicateVerifierService } from "state-transition-sdk-v2/lib/predicate/verification/PredicateVerifierService.js";
|
|
6680
|
+
import { SigningService } from "state-transition-sdk-v2/lib/crypto/secp256k1/SigningService.js";
|
|
6681
|
+
import { Signature } from "state-transition-sdk-v2/lib/crypto/secp256k1/Signature.js";
|
|
6682
|
+
import { MintSigningService } from "state-transition-sdk-v2/lib/crypto/MintSigningService.js";
|
|
6683
|
+
import { HashAlgorithm as HashAlgorithm2 } from "state-transition-sdk-v2/lib/crypto/hash/HashAlgorithm.js";
|
|
6684
|
+
import { DataHash } from "state-transition-sdk-v2/lib/crypto/hash/DataHash.js";
|
|
6685
|
+
import { DataHasher } from "state-transition-sdk-v2/lib/crypto/hash/DataHasher.js";
|
|
6686
|
+
import { DataHasherFactory } from "state-transition-sdk-v2/lib/crypto/hash/DataHasherFactory.js";
|
|
6687
|
+
import { CborSerializer } from "state-transition-sdk-v2/lib/serialization/cbor/CborSerializer.js";
|
|
6688
|
+
import { CborDeserializer } from "state-transition-sdk-v2/lib/serialization/cbor/CborDeserializer.js";
|
|
6689
|
+
import { CborError } from "state-transition-sdk-v2/lib/serialization/cbor/CborError.js";
|
|
6690
|
+
import { Asset } from "state-transition-sdk-v2/lib/payment/asset/Asset.js";
|
|
6691
|
+
import { AssetId } from "state-transition-sdk-v2/lib/payment/asset/AssetId.js";
|
|
6692
|
+
import { PaymentAssetCollection } from "state-transition-sdk-v2/lib/payment/asset/PaymentAssetCollection.js";
|
|
6693
|
+
import { TokenSplit } from "state-transition-sdk-v2/lib/payment/TokenSplit.js";
|
|
6694
|
+
import { SplitTokenRequest } from "state-transition-sdk-v2/lib/payment/SplitTokenRequest.js";
|
|
6695
|
+
import { SplitToken } from "state-transition-sdk-v2/lib/payment/SplitToken.js";
|
|
6696
|
+
import { SplitAssetProof } from "state-transition-sdk-v2/lib/payment/SplitAssetProof.js";
|
|
6697
|
+
import { SplitMintJustification } from "state-transition-sdk-v2/lib/payment/SplitMintJustification.js";
|
|
6698
|
+
import { SplitMintJustificationVerifier } from "state-transition-sdk-v2/lib/payment/SplitMintJustificationVerifier.js";
|
|
6699
|
+
import { VerificationStatus } from "state-transition-sdk-v2/lib/verification/VerificationStatus.js";
|
|
6700
|
+
import { VerificationResult } from "state-transition-sdk-v2/lib/verification/VerificationResult.js";
|
|
6701
|
+
import { HexConverter } from "state-transition-sdk-v2/lib/util/HexConverter.js";
|
|
6702
|
+
import { BigintConverter } from "state-transition-sdk-v2/lib/util/BigintConverter.js";
|
|
6703
|
+
import { BitString } from "state-transition-sdk-v2/lib/util/BitString.js";
|
|
6704
|
+
|
|
6705
|
+
// token-engine/token-blob.ts
|
|
6706
|
+
var TOKEN_BLOB_TAG = 39051n;
|
|
6707
|
+
var TOKEN_BLOB_VERSION = 1;
|
|
6708
|
+
function encodeTokenBlob(blob) {
|
|
6709
|
+
return CborSerializer.encodeTag(
|
|
6710
|
+
TOKEN_BLOB_TAG,
|
|
6711
|
+
CborSerializer.encodeArray(
|
|
6712
|
+
CborSerializer.encodeUnsignedInteger(BigInt(blob.v)),
|
|
6713
|
+
CborSerializer.encodeUnsignedInteger(BigInt(blob.network)),
|
|
6714
|
+
CborSerializer.encodeTextString(blob.tokenId),
|
|
6715
|
+
CborSerializer.encodeByteString(blob.token)
|
|
6716
|
+
)
|
|
6717
|
+
);
|
|
6718
|
+
}
|
|
6719
|
+
function decodeTokenBlob(bytes) {
|
|
6720
|
+
const tag = CborDeserializer.decodeTag(bytes);
|
|
6721
|
+
if (tag.tag !== TOKEN_BLOB_TAG) {
|
|
6722
|
+
throw new CborError(`Invalid TokenBlob tag: ${tag.tag}`);
|
|
6723
|
+
}
|
|
6724
|
+
const fields = CborDeserializer.decodeArray(tag.data, 4);
|
|
6725
|
+
const v = Number(CborDeserializer.decodeUnsignedInteger(fields[0]));
|
|
6726
|
+
if (v !== TOKEN_BLOB_VERSION) {
|
|
6727
|
+
throw new CborError(`Unsupported TokenBlob version: ${v}`);
|
|
6728
|
+
}
|
|
6729
|
+
return {
|
|
6730
|
+
v,
|
|
6731
|
+
network: Number(CborDeserializer.decodeUnsignedInteger(fields[1])),
|
|
6732
|
+
tokenId: CborDeserializer.decodeTextString(fields[2]),
|
|
6733
|
+
token: CborDeserializer.decodeByteString(fields[3])
|
|
6734
|
+
};
|
|
6735
|
+
}
|
|
6736
|
+
|
|
6737
|
+
// modules/payments/SpendQueue.ts
|
|
6664
6738
|
var QUEUE_TIMEOUT_MS = 3e4;
|
|
6665
6739
|
var QUEUE_MAX_SIZE = 100;
|
|
6666
6740
|
var TAG2 = "SpendQueue";
|
|
6667
6741
|
var SpendPlanner = class {
|
|
6742
|
+
/**
|
|
6743
|
+
* Token engine (path B). When injected, value reads go through the v2 engine
|
|
6744
|
+
* (sdkData = engine blob); otherwise the legacy v1 SdkToken path is used. The
|
|
6745
|
+
* engine is wired after construction via {@link setEngine} (it is bound to the
|
|
6746
|
+
* payments deps, which arrive after the planner is built).
|
|
6747
|
+
*/
|
|
6748
|
+
engine;
|
|
6749
|
+
constructor(engine) {
|
|
6750
|
+
this.engine = engine;
|
|
6751
|
+
}
|
|
6752
|
+
/** Inject (or clear) the token engine. */
|
|
6753
|
+
setEngine(engine) {
|
|
6754
|
+
this.engine = engine;
|
|
6755
|
+
}
|
|
6668
6756
|
/**
|
|
6669
6757
|
* Async pre-computation: parse all tokens for a given coinId.
|
|
6670
6758
|
* Called BEFORE the synchronous critical section.
|
|
6671
6759
|
*
|
|
6672
|
-
* Filters to confirmed tokens matching the coinId,
|
|
6673
|
-
*
|
|
6760
|
+
* Filters to confirmed tokens matching the coinId, decodes each token's
|
|
6761
|
+
* sdkData and extracts the bigint amount — via the v2 engine when injected,
|
|
6762
|
+
* else the legacy v1 SdkToken path.
|
|
6674
6763
|
*/
|
|
6675
6764
|
async buildParsedPool(tokens, coinId) {
|
|
6676
6765
|
const pool = /* @__PURE__ */ new Map();
|
|
@@ -6679,20 +6768,28 @@ var SpendPlanner = class {
|
|
|
6679
6768
|
if (t.status !== "confirmed") continue;
|
|
6680
6769
|
if (!t.sdkData) continue;
|
|
6681
6770
|
try {
|
|
6682
|
-
const
|
|
6683
|
-
|
|
6684
|
-
const realAmount = this.getTokenBalance(sdkToken, coinId);
|
|
6685
|
-
if (realAmount <= 0n) {
|
|
6771
|
+
const { sdkToken, amount } = this.engine ? await this.parseViaEngine(t.sdkData, coinId) : await this.parseViaSdk(t.sdkData, coinId);
|
|
6772
|
+
if (amount <= 0n) {
|
|
6686
6773
|
logger.warn(TAG2, `Token ${t.id} has 0 balance for coinId ${coinId}`);
|
|
6687
6774
|
continue;
|
|
6688
6775
|
}
|
|
6689
|
-
pool.set(t.id, { token: t, sdkToken, amount
|
|
6776
|
+
pool.set(t.id, { token: t, sdkToken, amount });
|
|
6690
6777
|
} catch (e) {
|
|
6691
6778
|
logger.warn(TAG2, "Failed to parse token", t.id, e);
|
|
6692
6779
|
}
|
|
6693
6780
|
}
|
|
6694
6781
|
return pool;
|
|
6695
6782
|
}
|
|
6783
|
+
/** Engine path (v2): sdkData is the engine blob (hex of CBOR(TokenBlob)). */
|
|
6784
|
+
async parseViaEngine(sdkData, coinId) {
|
|
6785
|
+
const token = await this.engine.decodeToken(decodeTokenBlob(hexToBytes2(sdkData)));
|
|
6786
|
+
return { sdkToken: token, amount: this.engine.balanceOf(token, coinId) };
|
|
6787
|
+
}
|
|
6788
|
+
/** Legacy v1 path: sdkData is TXF JSON. */
|
|
6789
|
+
async parseViaSdk(sdkData, coinId) {
|
|
6790
|
+
const sdkToken = await SdkToken.fromJSON(JSON.parse(sdkData));
|
|
6791
|
+
return { sdkToken, amount: this.getTokenBalance(sdkToken, coinId) };
|
|
6792
|
+
}
|
|
6696
6793
|
/**
|
|
6697
6794
|
* SYNCHRONOUS critical section. NO await allowed anywhere in this method.
|
|
6698
6795
|
*
|
|
@@ -7103,174 +7200,6 @@ var SpendQueue = class {
|
|
|
7103
7200
|
}
|
|
7104
7201
|
};
|
|
7105
7202
|
|
|
7106
|
-
// modules/payments/NametagMinter.ts
|
|
7107
|
-
init_logger();
|
|
7108
|
-
import { Token as Token2 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
7109
|
-
import { TokenId as TokenId2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
7110
|
-
import { TokenType } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
7111
|
-
import { TokenState as TokenState2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
7112
|
-
import { MintTransactionData } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData";
|
|
7113
|
-
import { MintCommitment } from "@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment";
|
|
7114
|
-
import { HashAlgorithm as HashAlgorithm2 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
7115
|
-
import { UnmaskedPredicate as UnmaskedPredicate2 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
7116
|
-
import { waitInclusionProof as waitInclusionProof2 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
|
|
7117
|
-
import { normalizeNametag } from "@unicitylabs/nostr-js-sdk";
|
|
7118
|
-
var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
7119
|
-
var NametagMinter = class {
|
|
7120
|
-
client;
|
|
7121
|
-
trustBase;
|
|
7122
|
-
signingService;
|
|
7123
|
-
skipVerification;
|
|
7124
|
-
debug;
|
|
7125
|
-
constructor(config) {
|
|
7126
|
-
this.client = config.stateTransitionClient;
|
|
7127
|
-
this.trustBase = config.trustBase;
|
|
7128
|
-
this.signingService = config.signingService;
|
|
7129
|
-
this.skipVerification = config.skipVerification ?? false;
|
|
7130
|
-
this.debug = config.debug ?? false;
|
|
7131
|
-
}
|
|
7132
|
-
log(message, ...args) {
|
|
7133
|
-
logger.debug("NametagMinter", message, ...args);
|
|
7134
|
-
}
|
|
7135
|
-
/**
|
|
7136
|
-
* Check if a nametag is available (not already minted)
|
|
7137
|
-
*/
|
|
7138
|
-
async isNametagAvailable(nametag) {
|
|
7139
|
-
try {
|
|
7140
|
-
const stripped = nametag.startsWith("@") ? nametag.slice(1) : nametag;
|
|
7141
|
-
const cleanNametag = normalizeNametag(stripped);
|
|
7142
|
-
const nametagTokenId = await TokenId2.fromNameTag(cleanNametag);
|
|
7143
|
-
const isMinted = await this.client.isMinted(this.trustBase, nametagTokenId);
|
|
7144
|
-
return !isMinted;
|
|
7145
|
-
} catch (error) {
|
|
7146
|
-
this.log("Error checking nametag availability:", error);
|
|
7147
|
-
return false;
|
|
7148
|
-
}
|
|
7149
|
-
}
|
|
7150
|
-
/**
|
|
7151
|
-
* Mint a nametag token on-chain
|
|
7152
|
-
*
|
|
7153
|
-
* @param nametag - The nametag to mint (e.g., "alice" or "@alice")
|
|
7154
|
-
* @param ownerAddress - The owner's direct address
|
|
7155
|
-
* @returns MintNametagResult with token if successful
|
|
7156
|
-
*/
|
|
7157
|
-
async mintNametag(nametag, ownerAddress) {
|
|
7158
|
-
const stripped = nametag.startsWith("@") ? nametag.slice(1) : nametag;
|
|
7159
|
-
const cleanNametag = normalizeNametag(stripped);
|
|
7160
|
-
this.log(`Starting mint for nametag: ${cleanNametag}`);
|
|
7161
|
-
try {
|
|
7162
|
-
const nametagTokenId = await TokenId2.fromNameTag(cleanNametag);
|
|
7163
|
-
const nametagTokenType = new TokenType(
|
|
7164
|
-
Buffer.from(UNICITY_TOKEN_TYPE_HEX, "hex")
|
|
7165
|
-
);
|
|
7166
|
-
const nametagBytes = new TextEncoder().encode(cleanNametag);
|
|
7167
|
-
const pubKey = this.signingService.publicKey;
|
|
7168
|
-
const saltInput = new Uint8Array(pubKey.length + nametagBytes.length);
|
|
7169
|
-
saltInput.set(pubKey, 0);
|
|
7170
|
-
saltInput.set(nametagBytes, pubKey.length);
|
|
7171
|
-
const saltBuffer = await crypto.subtle.digest("SHA-256", saltInput);
|
|
7172
|
-
const salt = new Uint8Array(saltBuffer);
|
|
7173
|
-
this.log("Generated deterministic salt");
|
|
7174
|
-
const mintData = await MintTransactionData.createFromNametag(
|
|
7175
|
-
cleanNametag,
|
|
7176
|
-
nametagTokenType,
|
|
7177
|
-
ownerAddress,
|
|
7178
|
-
salt,
|
|
7179
|
-
ownerAddress
|
|
7180
|
-
);
|
|
7181
|
-
this.log("Created MintTransactionData");
|
|
7182
|
-
const commitment = await MintCommitment.create(mintData);
|
|
7183
|
-
this.log("Created MintCommitment");
|
|
7184
|
-
const MAX_RETRIES = 3;
|
|
7185
|
-
let submitSuccess = false;
|
|
7186
|
-
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
7187
|
-
try {
|
|
7188
|
-
this.log(`Submitting commitment (attempt ${attempt}/${MAX_RETRIES})...`);
|
|
7189
|
-
const response = await this.client.submitMintCommitment(commitment);
|
|
7190
|
-
if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
|
|
7191
|
-
this.log(`Commitment ${response.status === "REQUEST_ID_EXISTS" ? "already exists" : "submitted successfully"}`);
|
|
7192
|
-
submitSuccess = true;
|
|
7193
|
-
break;
|
|
7194
|
-
} else {
|
|
7195
|
-
this.log(`Commitment failed: ${response.status}`);
|
|
7196
|
-
if (attempt === MAX_RETRIES) {
|
|
7197
|
-
return {
|
|
7198
|
-
success: false,
|
|
7199
|
-
error: `Failed to submit commitment after ${MAX_RETRIES} attempts: ${response.status}`
|
|
7200
|
-
};
|
|
7201
|
-
}
|
|
7202
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
7203
|
-
}
|
|
7204
|
-
} catch (error) {
|
|
7205
|
-
this.log(`Attempt ${attempt} error:`, error);
|
|
7206
|
-
if (attempt === MAX_RETRIES) {
|
|
7207
|
-
return {
|
|
7208
|
-
success: false,
|
|
7209
|
-
error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`
|
|
7210
|
-
};
|
|
7211
|
-
}
|
|
7212
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
7213
|
-
}
|
|
7214
|
-
}
|
|
7215
|
-
if (!submitSuccess) {
|
|
7216
|
-
return {
|
|
7217
|
-
success: false,
|
|
7218
|
-
error: "Failed to submit commitment after retries"
|
|
7219
|
-
};
|
|
7220
|
-
}
|
|
7221
|
-
this.log("Waiting for inclusion proof...");
|
|
7222
|
-
const inclusionProof = await waitInclusionProof2(this.trustBase, this.client, commitment);
|
|
7223
|
-
this.log("Received inclusion proof");
|
|
7224
|
-
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
7225
|
-
const nametagPredicate = await UnmaskedPredicate2.create(
|
|
7226
|
-
nametagTokenId,
|
|
7227
|
-
nametagTokenType,
|
|
7228
|
-
this.signingService,
|
|
7229
|
-
HashAlgorithm2.SHA256,
|
|
7230
|
-
salt
|
|
7231
|
-
);
|
|
7232
|
-
const tokenState = new TokenState2(nametagPredicate, null);
|
|
7233
|
-
let token;
|
|
7234
|
-
if (this.skipVerification) {
|
|
7235
|
-
this.log("Creating token WITHOUT verification (dev mode)");
|
|
7236
|
-
const tokenJson = {
|
|
7237
|
-
version: "2.0",
|
|
7238
|
-
state: tokenState.toJSON(),
|
|
7239
|
-
genesis: genesisTransaction.toJSON(),
|
|
7240
|
-
transactions: [],
|
|
7241
|
-
nametags: []
|
|
7242
|
-
};
|
|
7243
|
-
token = await Token2.fromJSON(tokenJson);
|
|
7244
|
-
} else {
|
|
7245
|
-
token = await Token2.mint(
|
|
7246
|
-
this.trustBase,
|
|
7247
|
-
tokenState,
|
|
7248
|
-
genesisTransaction
|
|
7249
|
-
);
|
|
7250
|
-
}
|
|
7251
|
-
this.log(`Nametag minted successfully: ${cleanNametag}`);
|
|
7252
|
-
const nametagData = {
|
|
7253
|
-
name: cleanNametag,
|
|
7254
|
-
token: token.toJSON(),
|
|
7255
|
-
timestamp: Date.now(),
|
|
7256
|
-
format: "txf",
|
|
7257
|
-
version: "2.0"
|
|
7258
|
-
};
|
|
7259
|
-
return {
|
|
7260
|
-
success: true,
|
|
7261
|
-
token,
|
|
7262
|
-
nametagData
|
|
7263
|
-
};
|
|
7264
|
-
} catch (error) {
|
|
7265
|
-
this.log("Minting failed:", error);
|
|
7266
|
-
return {
|
|
7267
|
-
success: false,
|
|
7268
|
-
error: error instanceof Error ? error.message : String(error)
|
|
7269
|
-
};
|
|
7270
|
-
}
|
|
7271
|
-
}
|
|
7272
|
-
};
|
|
7273
|
-
|
|
7274
7203
|
// modules/payments/PaymentsModule.ts
|
|
7275
7204
|
init_constants();
|
|
7276
7205
|
|
|
@@ -7887,6 +7816,15 @@ function txfToToken(tokenId, txf) {
|
|
|
7887
7816
|
sdkData: JSON.stringify(txf)
|
|
7888
7817
|
};
|
|
7889
7818
|
}
|
|
7819
|
+
function isV2TokenBlob(sdkData) {
|
|
7820
|
+
return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
7821
|
+
}
|
|
7822
|
+
function v2TokenId(token) {
|
|
7823
|
+
return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
|
|
7824
|
+
}
|
|
7825
|
+
function isV2TokenEntry(entry) {
|
|
7826
|
+
return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
|
|
7827
|
+
}
|
|
7890
7828
|
async function buildTxfStorageData(tokens, meta, options) {
|
|
7891
7829
|
const storageData = {
|
|
7892
7830
|
_meta: {
|
|
@@ -7917,6 +7855,8 @@ async function buildTxfStorageData(tokens, meta, options) {
|
|
|
7917
7855
|
if (txf) {
|
|
7918
7856
|
const actualTokenId = txf.genesis.data.tokenId;
|
|
7919
7857
|
storageData[keyFromTokenId(actualTokenId)] = txf;
|
|
7858
|
+
} else if (isV2TokenBlob(token.sdkData)) {
|
|
7859
|
+
storageData[keyFromTokenId(v2TokenId(token))] = token;
|
|
7920
7860
|
}
|
|
7921
7861
|
}
|
|
7922
7862
|
if (options?.archivedTokens && options.archivedTokens.size > 0) {
|
|
@@ -8014,6 +7954,8 @@ function parseTxfStorageData(data) {
|
|
|
8014
7954
|
if (txfToken?.genesis?.data?.tokenId) {
|
|
8015
7955
|
const token = txfToToken(tokenId, txfToken);
|
|
8016
7956
|
result.tokens.push(token);
|
|
7957
|
+
} else if (isV2TokenEntry(storageData[key])) {
|
|
7958
|
+
result.tokens.push(storageData[key]);
|
|
8017
7959
|
}
|
|
8018
7960
|
} catch (err) {
|
|
8019
7961
|
result.validationErrors.push(`Token ${tokenId}: ${err}`);
|
|
@@ -8289,12 +8231,12 @@ init_logger();
|
|
|
8289
8231
|
init_errors();
|
|
8290
8232
|
import { Token as Token3 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
8291
8233
|
import { TokenId as TokenId3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
8292
|
-
import { TokenState as
|
|
8234
|
+
import { TokenState as TokenState2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
8293
8235
|
import { CoinId as CoinId3 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
8294
8236
|
import { TokenCoinData as TokenCoinData2 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData";
|
|
8295
8237
|
import { TokenSplitBuilder as TokenSplitBuilder2 } from "@unicitylabs/state-transition-sdk/lib/transaction/split/TokenSplitBuilder";
|
|
8296
8238
|
import { HashAlgorithm as HashAlgorithm3 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
8297
|
-
import { UnmaskedPredicate as
|
|
8239
|
+
import { UnmaskedPredicate as UnmaskedPredicate2 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
8298
8240
|
import { UnmaskedPredicateReference as UnmaskedPredicateReference2 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference";
|
|
8299
8241
|
import { TransferCommitment as TransferCommitment2 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment";
|
|
8300
8242
|
import { waitInclusionProof as waitInclusionProof3 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
|
|
@@ -8416,14 +8358,14 @@ var InstantSplitExecutor = class {
|
|
|
8416
8358
|
options?.message
|
|
8417
8359
|
// on-chain message (invoice memo bytes, or null)
|
|
8418
8360
|
);
|
|
8419
|
-
const mintedPredicate = await
|
|
8361
|
+
const mintedPredicate = await UnmaskedPredicate2.create(
|
|
8420
8362
|
recipientTokenId,
|
|
8421
8363
|
tokenToSplit.type,
|
|
8422
8364
|
this.signingService,
|
|
8423
8365
|
HashAlgorithm3.SHA256,
|
|
8424
8366
|
recipientSalt
|
|
8425
8367
|
);
|
|
8426
|
-
const mintedState = new
|
|
8368
|
+
const mintedState = new TokenState2(mintedPredicate, null);
|
|
8427
8369
|
logger.debug("InstantSplit", "Step 5: Packaging V5 bundle...");
|
|
8428
8370
|
const senderPubkey = toHex2(this.signingService.publicKey);
|
|
8429
8371
|
let nametagTokenJson;
|
|
@@ -8538,14 +8480,14 @@ var InstantSplitExecutor = class {
|
|
|
8538
8480
|
* It does NOT need the genesis transaction or mint proof.
|
|
8539
8481
|
*/
|
|
8540
8482
|
async createTransferCommitmentFromMintData(mintData, recipientAddress, transferSalt, signingService, nametagTokens, message) {
|
|
8541
|
-
const predicate = await
|
|
8483
|
+
const predicate = await UnmaskedPredicate2.create(
|
|
8542
8484
|
mintData.tokenId,
|
|
8543
8485
|
mintData.tokenType,
|
|
8544
8486
|
signingService,
|
|
8545
8487
|
HashAlgorithm3.SHA256,
|
|
8546
8488
|
mintData.salt
|
|
8547
8489
|
);
|
|
8548
|
-
const state = new
|
|
8490
|
+
const state = new TokenState2(predicate, null);
|
|
8549
8491
|
const minimalToken = {
|
|
8550
8492
|
state,
|
|
8551
8493
|
nametagTokens: nametagTokens || [],
|
|
@@ -8606,14 +8548,14 @@ var InstantSplitExecutor = class {
|
|
|
8606
8548
|
message: `Mint proof received in ${proofDuration.toFixed(0)}ms`
|
|
8607
8549
|
});
|
|
8608
8550
|
const mintTransaction = senderMintCommitment.toTransaction(senderMintProof);
|
|
8609
|
-
const predicate = await
|
|
8551
|
+
const predicate = await UnmaskedPredicate2.create(
|
|
8610
8552
|
context.senderTokenId,
|
|
8611
8553
|
context.tokenType,
|
|
8612
8554
|
context.signingService,
|
|
8613
8555
|
HashAlgorithm3.SHA256,
|
|
8614
8556
|
context.senderSalt
|
|
8615
8557
|
);
|
|
8616
|
-
const state = new
|
|
8558
|
+
const state = new TokenState2(predicate, null);
|
|
8617
8559
|
const changeToken = await Token3.mint(this.trustBase, state, mintTransaction);
|
|
8618
8560
|
if (!this.devMode) {
|
|
8619
8561
|
const verification = await changeToken.verify(this.trustBase);
|
|
@@ -8693,14 +8635,14 @@ var InstantSplitExecutor = class {
|
|
|
8693
8635
|
init_logger();
|
|
8694
8636
|
init_errors();
|
|
8695
8637
|
import { Token as Token4 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
8696
|
-
import { TokenState as
|
|
8638
|
+
import { TokenState as TokenState3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
8697
8639
|
import { TokenType as TokenType2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
8698
8640
|
import { HashAlgorithm as HashAlgorithm4 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
8699
|
-
import { UnmaskedPredicate as
|
|
8641
|
+
import { UnmaskedPredicate as UnmaskedPredicate3 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
8700
8642
|
import { TransferCommitment as TransferCommitment3 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment";
|
|
8701
|
-
import { TransferTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction";
|
|
8702
|
-
import { MintCommitment
|
|
8703
|
-
import { MintTransactionData
|
|
8643
|
+
import { TransferTransaction as TransferTransaction2 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction";
|
|
8644
|
+
import { MintCommitment } from "@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment";
|
|
8645
|
+
import { MintTransactionData } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData";
|
|
8704
8646
|
import { waitInclusionProof as waitInclusionProof4 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
|
|
8705
8647
|
|
|
8706
8648
|
// types/instant-split.ts
|
|
@@ -8793,11 +8735,11 @@ var InstantSplitProcessor = class {
|
|
|
8793
8735
|
logger.warn("InstantSplit", "Sender pubkey mismatch (non-fatal)");
|
|
8794
8736
|
}
|
|
8795
8737
|
const burnTxJson = JSON.parse(bundle.burnTransaction);
|
|
8796
|
-
const _burnTransaction = await
|
|
8738
|
+
const _burnTransaction = await TransferTransaction2.fromJSON(burnTxJson);
|
|
8797
8739
|
logger.debug("InstantSplit", "Burn transaction validated");
|
|
8798
8740
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
8799
|
-
const mintData = await
|
|
8800
|
-
const mintCommitment = await
|
|
8741
|
+
const mintData = await MintTransactionData.fromJSON(mintDataJson);
|
|
8742
|
+
const mintCommitment = await MintCommitment.create(mintData);
|
|
8801
8743
|
logger.debug("InstantSplit", "Mint commitment recreated");
|
|
8802
8744
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
8803
8745
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
@@ -8829,14 +8771,14 @@ var InstantSplitProcessor = class {
|
|
|
8829
8771
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
8830
8772
|
logger.debug("InstantSplit", "Transfer proof received");
|
|
8831
8773
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
8832
|
-
const finalRecipientPredicate = await
|
|
8774
|
+
const finalRecipientPredicate = await UnmaskedPredicate3.create(
|
|
8833
8775
|
mintData.tokenId,
|
|
8834
8776
|
tokenType,
|
|
8835
8777
|
signingService,
|
|
8836
8778
|
HashAlgorithm4.SHA256,
|
|
8837
8779
|
transferSalt
|
|
8838
8780
|
);
|
|
8839
|
-
const finalRecipientState = new
|
|
8781
|
+
const finalRecipientState = new TokenState3(finalRecipientPredicate, null);
|
|
8840
8782
|
logger.debug("InstantSplit", "Final recipient state created");
|
|
8841
8783
|
let nametagTokens = [];
|
|
8842
8784
|
const recipientAddressStr = bundle.recipientAddressJson;
|
|
@@ -8943,8 +8885,8 @@ var InstantSplitProcessor = class {
|
|
|
8943
8885
|
await this.waitInclusionProofWithDevBypass(burnCommitment, options?.proofTimeoutMs);
|
|
8944
8886
|
logger.debug("InstantSplit", "V4: Burn proof received");
|
|
8945
8887
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
8946
|
-
const mintData = await
|
|
8947
|
-
const mintCommitment = await
|
|
8888
|
+
const mintData = await MintTransactionData.fromJSON(mintDataJson);
|
|
8889
|
+
const mintCommitment = await MintCommitment.create(mintData);
|
|
8948
8890
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
8949
8891
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
8950
8892
|
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
@@ -8957,14 +8899,14 @@ var InstantSplitProcessor = class {
|
|
|
8957
8899
|
logger.debug("InstantSplit", "V4: Mint proof received");
|
|
8958
8900
|
const tokenType = new TokenType2(fromHex3(bundle.tokenTypeHex));
|
|
8959
8901
|
const recipientSalt = fromHex3(bundle.recipientSaltHex);
|
|
8960
|
-
const recipientPredicate = await
|
|
8902
|
+
const recipientPredicate = await UnmaskedPredicate3.create(
|
|
8961
8903
|
mintData.tokenId,
|
|
8962
8904
|
tokenType,
|
|
8963
8905
|
signingService,
|
|
8964
8906
|
HashAlgorithm4.SHA256,
|
|
8965
8907
|
recipientSalt
|
|
8966
8908
|
);
|
|
8967
|
-
const recipientState = new
|
|
8909
|
+
const recipientState = new TokenState3(recipientPredicate, null);
|
|
8968
8910
|
const tokenJson = {
|
|
8969
8911
|
version: "2.0",
|
|
8970
8912
|
state: recipientState.toJSON(),
|
|
@@ -8987,14 +8929,14 @@ var InstantSplitProcessor = class {
|
|
|
8987
8929
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
8988
8930
|
logger.debug("InstantSplit", "V4: Transfer proof received");
|
|
8989
8931
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
8990
|
-
const finalPredicate = await
|
|
8932
|
+
const finalPredicate = await UnmaskedPredicate3.create(
|
|
8991
8933
|
mintData.tokenId,
|
|
8992
8934
|
tokenType,
|
|
8993
8935
|
signingService,
|
|
8994
8936
|
HashAlgorithm4.SHA256,
|
|
8995
8937
|
transferSalt
|
|
8996
8938
|
);
|
|
8997
|
-
const finalState = new
|
|
8939
|
+
const finalState = new TokenState3(finalPredicate, null);
|
|
8998
8940
|
const finalTokenJson = mintedToken.toJSON();
|
|
8999
8941
|
finalTokenJson.state = finalState.toJSON();
|
|
9000
8942
|
finalTokenJson.transactions = [transferTransaction.toJSON()];
|
|
@@ -9045,17 +8987,17 @@ var InstantSplitProcessor = class {
|
|
|
9045
8987
|
import { Token as SdkToken2 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
9046
8988
|
import { CoinId as CoinId4 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
9047
8989
|
import { TransferCommitment as TransferCommitment4 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment";
|
|
9048
|
-
import { TransferTransaction as
|
|
9049
|
-
import { SigningService } from "@unicitylabs/state-transition-sdk/lib/sign/SigningService";
|
|
8990
|
+
import { TransferTransaction as TransferTransaction3 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction";
|
|
8991
|
+
import { SigningService as SigningService2 } from "@unicitylabs/state-transition-sdk/lib/sign/SigningService";
|
|
9050
8992
|
import { AddressScheme } from "@unicitylabs/state-transition-sdk/lib/address/AddressScheme";
|
|
9051
|
-
import { UnmaskedPredicate as
|
|
9052
|
-
import { TokenState as
|
|
8993
|
+
import { UnmaskedPredicate as UnmaskedPredicate4 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
8994
|
+
import { TokenState as TokenState4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
9053
8995
|
import { HashAlgorithm as HashAlgorithm5 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
9054
8996
|
import { TokenType as TokenType3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
9055
|
-
import { MintCommitment as
|
|
9056
|
-
import { MintTransactionData as
|
|
8997
|
+
import { MintCommitment as MintCommitment2 } from "@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment";
|
|
8998
|
+
import { MintTransactionData as MintTransactionData2 } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData";
|
|
9057
8999
|
import { waitInclusionProof as waitInclusionProof5 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
|
|
9058
|
-
import { InclusionProof } from "@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof";
|
|
9000
|
+
import { InclusionProof as InclusionProof2 } from "@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof";
|
|
9059
9001
|
function computeHistoryDedupKey(type, tokenId, transferId) {
|
|
9060
9002
|
if (type === "SENT" && transferId) return `${type}_transfer_${transferId}`;
|
|
9061
9003
|
if (tokenId) return `${type}_${tokenId}`;
|
|
@@ -9076,7 +9018,7 @@ function enrichWithRegistry(info) {
|
|
|
9076
9018
|
}
|
|
9077
9019
|
return info;
|
|
9078
9020
|
}
|
|
9079
|
-
async function parseTokenInfo(tokenData) {
|
|
9021
|
+
async function parseTokenInfo(tokenData, engine) {
|
|
9080
9022
|
const defaultInfo = {
|
|
9081
9023
|
coinId: "ALPHA",
|
|
9082
9024
|
symbol: "ALPHA",
|
|
@@ -9084,6 +9026,25 @@ async function parseTokenInfo(tokenData) {
|
|
|
9084
9026
|
decimals: 0,
|
|
9085
9027
|
amount: "0"
|
|
9086
9028
|
};
|
|
9029
|
+
if (engine && typeof tokenData === "string" && looksLikeTokenBlob(tokenData)) {
|
|
9030
|
+
try {
|
|
9031
|
+
const token = await engine.decodeToken(decodeTokenBlob(hexToBytes2(tokenData)));
|
|
9032
|
+
const first = engine.readValue(token)?.assets[0];
|
|
9033
|
+
if (first) {
|
|
9034
|
+
return enrichWithRegistry({
|
|
9035
|
+
coinId: first.coinId,
|
|
9036
|
+
symbol: first.coinId.slice(0, 8),
|
|
9037
|
+
name: `Token ${first.coinId.slice(0, 8)}`,
|
|
9038
|
+
decimals: 0,
|
|
9039
|
+
amount: String(first.amount),
|
|
9040
|
+
tokenId: engine.tokenId(token)
|
|
9041
|
+
});
|
|
9042
|
+
}
|
|
9043
|
+
return { ...defaultInfo, tokenId: engine.tokenId(token) };
|
|
9044
|
+
} catch (error) {
|
|
9045
|
+
logger.warn("Payments", "Failed to parse token info via engine:", error);
|
|
9046
|
+
}
|
|
9047
|
+
}
|
|
9087
9048
|
try {
|
|
9088
9049
|
const data = typeof tokenData === "string" ? JSON.parse(tokenData) : tokenData;
|
|
9089
9050
|
try {
|
|
@@ -9222,27 +9183,41 @@ async function parseTokenInfo(tokenData) {
|
|
|
9222
9183
|
}
|
|
9223
9184
|
var sdkDataCache = /* @__PURE__ */ new Map();
|
|
9224
9185
|
var SDK_DATA_CACHE_MAX = 2e3;
|
|
9186
|
+
function looksLikeTokenBlob(sdkData) {
|
|
9187
|
+
return sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
9188
|
+
}
|
|
9189
|
+
function tryParseBlobKeys(sdkData) {
|
|
9190
|
+
try {
|
|
9191
|
+
const blob = decodeTokenBlob(hexToBytes2(sdkData));
|
|
9192
|
+
return { tokenId: blob.tokenId, stateHash: sha2562(bytesToHex3(blob.token), "hex") };
|
|
9193
|
+
} catch {
|
|
9194
|
+
return null;
|
|
9195
|
+
}
|
|
9196
|
+
}
|
|
9225
9197
|
function parseSdkDataCached(sdkData) {
|
|
9226
9198
|
const cached = sdkDataCache.get(sdkData);
|
|
9227
9199
|
if (cached) return cached;
|
|
9228
|
-
let
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9200
|
+
let entry = looksLikeTokenBlob(sdkData) ? tryParseBlobKeys(sdkData) : null;
|
|
9201
|
+
if (!entry) {
|
|
9202
|
+
let tokenId = null;
|
|
9203
|
+
let stateHash = "";
|
|
9204
|
+
try {
|
|
9205
|
+
const txf = JSON.parse(sdkData);
|
|
9206
|
+
tokenId = txf.genesis?.data?.tokenId || null;
|
|
9207
|
+
stateHash = getCurrentStateHash(txf) || "";
|
|
9208
|
+
if (!stateHash) {
|
|
9209
|
+
if (txf.state?.hash) {
|
|
9210
|
+
stateHash = txf.state.hash;
|
|
9211
|
+
} else if (txf.stateHash) {
|
|
9212
|
+
stateHash = txf.stateHash;
|
|
9213
|
+
} else if (txf.currentStateHash) {
|
|
9214
|
+
stateHash = txf.currentStateHash;
|
|
9215
|
+
}
|
|
9241
9216
|
}
|
|
9217
|
+
} catch {
|
|
9242
9218
|
}
|
|
9243
|
-
|
|
9219
|
+
entry = { tokenId, stateHash };
|
|
9244
9220
|
}
|
|
9245
|
-
const entry = { tokenId, stateHash };
|
|
9246
9221
|
if (sdkDataCache.size >= SDK_DATA_CACHE_MAX) {
|
|
9247
9222
|
sdkDataCache.clear();
|
|
9248
9223
|
}
|
|
@@ -9519,6 +9494,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
9519
9494
|
);
|
|
9520
9495
|
this.deps = deps;
|
|
9521
9496
|
this.priceProvider = deps.price ?? null;
|
|
9497
|
+
this.spendPlanner.setEngine(deps.tokenEngine);
|
|
9522
9498
|
if (this.l1) {
|
|
9523
9499
|
this.l1.initialize({
|
|
9524
9500
|
identity: deps.identity,
|
|
@@ -9743,7 +9719,59 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
9743
9719
|
request.invoiceRefundAddress,
|
|
9744
9720
|
request.invoiceContact
|
|
9745
9721
|
);
|
|
9746
|
-
if (
|
|
9722
|
+
if (this.deps?.tokenEngine && peerInfo?.chainPubkey) {
|
|
9723
|
+
const engine = this.deps.tokenEngine;
|
|
9724
|
+
const recipientChainPubkey = hexToBytes2(peerInfo.chainPubkey);
|
|
9725
|
+
const memoData = onChainMessage ?? void 0;
|
|
9726
|
+
const handToRecipient = async (finished) => {
|
|
9727
|
+
const tokenBlob = bytesToHex3(encodeTokenBlob(engine.encodeToken(finished)));
|
|
9728
|
+
await this.deps.transport.sendTokenTransfer(recipientPubkey, {
|
|
9729
|
+
type: "V2_TRANSFER",
|
|
9730
|
+
version: "2.0",
|
|
9731
|
+
tokenBlob,
|
|
9732
|
+
memo: request.memo
|
|
9733
|
+
});
|
|
9734
|
+
};
|
|
9735
|
+
for (const tw of splitPlan.tokensToTransferDirectly) {
|
|
9736
|
+
const finished = await engine.transfer({
|
|
9737
|
+
token: tw.sdkToken,
|
|
9738
|
+
recipientPubkey: recipientChainPubkey,
|
|
9739
|
+
data: memoData
|
|
9740
|
+
});
|
|
9741
|
+
await handToRecipient(finished);
|
|
9742
|
+
result.tokenTransfers.push({ sourceTokenId: tw.uiToken.id, method: "direct" });
|
|
9743
|
+
await this.removeToken(tw.uiToken.id, result.id);
|
|
9744
|
+
}
|
|
9745
|
+
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
9746
|
+
const selfChainPubkey = hexToBytes2(this.deps.identity.chainPubkey);
|
|
9747
|
+
const { outputs } = await engine.split({
|
|
9748
|
+
token: splitPlan.tokenToSplit.sdkToken,
|
|
9749
|
+
outputs: [
|
|
9750
|
+
{ recipientPubkey: recipientChainPubkey, coinId: request.coinId, amount: splitPlan.splitAmount, data: memoData },
|
|
9751
|
+
{ recipientPubkey: selfChainPubkey, coinId: request.coinId, amount: splitPlan.remainderAmount }
|
|
9752
|
+
]
|
|
9753
|
+
});
|
|
9754
|
+
await handToRecipient(outputs[0]);
|
|
9755
|
+
const changeToken = outputs[1];
|
|
9756
|
+
const changeBlob = bytesToHex3(encodeTokenBlob(engine.encodeToken(changeToken)));
|
|
9757
|
+
const changeInfo = await parseTokenInfo(changeBlob, engine);
|
|
9758
|
+
const registry = TokenRegistry.getInstance();
|
|
9759
|
+
await this.addToken({
|
|
9760
|
+
id: `v2_${engine.tokenId(changeToken)}`,
|
|
9761
|
+
coinId: changeInfo.coinId,
|
|
9762
|
+
symbol: registry.getSymbol(changeInfo.coinId) || changeInfo.symbol,
|
|
9763
|
+
name: registry.getName(changeInfo.coinId) || changeInfo.name,
|
|
9764
|
+
decimals: registry.getDecimals(changeInfo.coinId) ?? changeInfo.decimals,
|
|
9765
|
+
amount: changeInfo.amount,
|
|
9766
|
+
status: "confirmed",
|
|
9767
|
+
createdAt: Date.now(),
|
|
9768
|
+
updatedAt: Date.now(),
|
|
9769
|
+
sdkData: changeBlob
|
|
9770
|
+
});
|
|
9771
|
+
result.tokenTransfers.push({ sourceTokenId: splitPlan.tokenToSplit.uiToken.id, method: "split" });
|
|
9772
|
+
await this.removeToken(splitPlan.tokenToSplit.uiToken.id, result.id);
|
|
9773
|
+
}
|
|
9774
|
+
} else if (transferMode === "conservative") {
|
|
9747
9775
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
9748
9776
|
logger.debug("Payments", "Executing conservative split...");
|
|
9749
9777
|
const splitExecutor = new TokenSplitExecutor({
|
|
@@ -10339,7 +10367,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
10339
10367
|
* because bundle-level dedup protects against replays, and split children share genesis IDs.
|
|
10340
10368
|
*/
|
|
10341
10369
|
async saveCommitmentOnlyToken(sourceTokenInput, commitmentInput, senderPubkey, deferPersistence = false, skipGenesisDedup = false) {
|
|
10342
|
-
const tokenInfo = await parseTokenInfo(sourceTokenInput);
|
|
10370
|
+
const tokenInfo = await parseTokenInfo(sourceTokenInput, this.deps?.tokenEngine);
|
|
10343
10371
|
const sdkData = typeof sourceTokenInput === "string" ? sourceTokenInput : JSON.stringify(sourceTokenInput);
|
|
10344
10372
|
const nostrTokenId = extractTokenIdFromSdkData(sdkData);
|
|
10345
10373
|
const nostrStateHash = extractStateHashFromSdkData(sdkData);
|
|
@@ -11431,8 +11459,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11431
11459
|
if (pending2.stage === "RECEIVED") {
|
|
11432
11460
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: RECEIVED \u2192 submitting mint commitment...`);
|
|
11433
11461
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11434
|
-
const mintData = await
|
|
11435
|
-
const mintCommitment = await
|
|
11462
|
+
const mintData = await MintTransactionData2.fromJSON(mintDataJson);
|
|
11463
|
+
const mintCommitment = await MintCommitment2.create(mintData);
|
|
11436
11464
|
const mintResponse = await stClient.submitMintCommitment(mintCommitment);
|
|
11437
11465
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint response status=${mintResponse.status}`);
|
|
11438
11466
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
@@ -11444,8 +11472,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11444
11472
|
if (pending2.stage === "MINT_SUBMITTED") {
|
|
11445
11473
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_SUBMITTED \u2192 checking mint proof...`);
|
|
11446
11474
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11447
|
-
const mintData = await
|
|
11448
|
-
const mintCommitment = await
|
|
11475
|
+
const mintData = await MintTransactionData2.fromJSON(mintDataJson);
|
|
11476
|
+
const mintCommitment = await MintCommitment2.create(mintData);
|
|
11449
11477
|
const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);
|
|
11450
11478
|
if (!proof) {
|
|
11451
11479
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof not yet available, staying MINT_SUBMITTED`);
|
|
@@ -11542,10 +11570,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11542
11570
|
*/
|
|
11543
11571
|
async finalizeFromV5Bundle(bundle, pending2, signingService, stClient, trustBase) {
|
|
11544
11572
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11545
|
-
const mintData = await
|
|
11546
|
-
const mintCommitment = await
|
|
11573
|
+
const mintData = await MintTransactionData2.fromJSON(mintDataJson);
|
|
11574
|
+
const mintCommitment = await MintCommitment2.create(mintData);
|
|
11547
11575
|
const mintProofJson = JSON.parse(pending2.mintProofJson);
|
|
11548
|
-
const mintProof =
|
|
11576
|
+
const mintProof = InclusionProof2.fromJSON(mintProofJson);
|
|
11549
11577
|
const mintTransaction = mintCommitment.toTransaction(mintProof);
|
|
11550
11578
|
const tokenType = new TokenType3(fromHex4(bundle.tokenTypeHex));
|
|
11551
11579
|
const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);
|
|
@@ -11562,14 +11590,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11562
11590
|
const transferProof = await waitInclusionProof5(trustBase, stClient, transferCommitment);
|
|
11563
11591
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
11564
11592
|
const transferSalt = fromHex4(bundle.transferSaltHex);
|
|
11565
|
-
const recipientPredicate = await
|
|
11593
|
+
const recipientPredicate = await UnmaskedPredicate4.create(
|
|
11566
11594
|
mintData.tokenId,
|
|
11567
11595
|
tokenType,
|
|
11568
11596
|
signingService,
|
|
11569
11597
|
HashAlgorithm5.SHA256,
|
|
11570
11598
|
transferSalt
|
|
11571
11599
|
);
|
|
11572
|
-
const recipientState = new
|
|
11600
|
+
const recipientState = new TokenState4(recipientPredicate, null);
|
|
11573
11601
|
let nametagTokens = [];
|
|
11574
11602
|
const recipientAddressStr = bundle.recipientAddressJson;
|
|
11575
11603
|
if (recipientAddressStr.startsWith("PROXY://")) {
|
|
@@ -12306,68 +12334,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12306
12334
|
}
|
|
12307
12335
|
}
|
|
12308
12336
|
}
|
|
12309
|
-
/**
|
|
12310
|
-
* Mint a nametag token on-chain (like Sphere wallet and lottery)
|
|
12311
|
-
* This creates the nametag token required for receiving tokens via PROXY addresses
|
|
12312
|
-
*
|
|
12313
|
-
* @param nametag - The nametag to mint (e.g., "alice" or "@alice")
|
|
12314
|
-
* @returns MintNametagResult with success status and token if successful
|
|
12315
|
-
*/
|
|
12316
|
-
async mintNametag(nametag) {
|
|
12317
|
-
this.ensureInitialized();
|
|
12318
|
-
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
12319
|
-
if (!stClient) {
|
|
12320
|
-
return {
|
|
12321
|
-
success: false,
|
|
12322
|
-
error: "State transition client not available. Oracle provider must implement getStateTransitionClient()"
|
|
12323
|
-
};
|
|
12324
|
-
}
|
|
12325
|
-
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
12326
|
-
if (!trustBase) {
|
|
12327
|
-
return {
|
|
12328
|
-
success: false,
|
|
12329
|
-
error: "Trust base not available. Oracle provider must implement getTrustBase()"
|
|
12330
|
-
};
|
|
12331
|
-
}
|
|
12332
|
-
try {
|
|
12333
|
-
const signingService = await this.createSigningService();
|
|
12334
|
-
const { UnmaskedPredicateReference: UnmaskedPredicateReference4 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12335
|
-
const { TokenType: TokenType6 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
12336
|
-
const UNICITY_TOKEN_TYPE_HEX3 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
12337
|
-
const tokenType = new TokenType6(Buffer.from(UNICITY_TOKEN_TYPE_HEX3, "hex"));
|
|
12338
|
-
const addressRef = await UnmaskedPredicateReference4.create(
|
|
12339
|
-
tokenType,
|
|
12340
|
-
signingService.algorithm,
|
|
12341
|
-
signingService.publicKey,
|
|
12342
|
-
HashAlgorithm5.SHA256
|
|
12343
|
-
);
|
|
12344
|
-
const ownerAddress = await addressRef.toAddress();
|
|
12345
|
-
const minter = new NametagMinter({
|
|
12346
|
-
stateTransitionClient: stClient,
|
|
12347
|
-
trustBase,
|
|
12348
|
-
signingService,
|
|
12349
|
-
debug: this.moduleConfig.debug
|
|
12350
|
-
});
|
|
12351
|
-
const result = await minter.mintNametag(nametag, ownerAddress);
|
|
12352
|
-
if (result.success && result.nametagData) {
|
|
12353
|
-
await this.setNametag(result.nametagData);
|
|
12354
|
-
logger.debug("Payments", `Unicity ID minted and saved: ${result.nametagData.name}`);
|
|
12355
|
-
this.deps.emitEvent("nametag:registered", {
|
|
12356
|
-
nametag: result.nametagData.name,
|
|
12357
|
-
addressIndex: 0
|
|
12358
|
-
// Primary address
|
|
12359
|
-
});
|
|
12360
|
-
}
|
|
12361
|
-
return result;
|
|
12362
|
-
} catch (error) {
|
|
12363
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
12364
|
-
logger.debug("Payments", "mintNametag failed:", errorMsg);
|
|
12365
|
-
return {
|
|
12366
|
-
success: false,
|
|
12367
|
-
error: errorMsg
|
|
12368
|
-
};
|
|
12369
|
-
}
|
|
12370
|
-
}
|
|
12371
12337
|
/**
|
|
12372
12338
|
* Mint a fungible token directly to this wallet (genesis mint).
|
|
12373
12339
|
*
|
|
@@ -12408,18 +12374,18 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12408
12374
|
}
|
|
12409
12375
|
try {
|
|
12410
12376
|
const signingService = await this.createSigningService();
|
|
12411
|
-
const { TokenId:
|
|
12377
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
12412
12378
|
const { TokenCoinData: TokenCoinData3 } = await import("@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData");
|
|
12413
|
-
const { UnmaskedPredicateReference:
|
|
12379
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12414
12380
|
const tokenTypeBytes = fromHex4("f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509");
|
|
12415
12381
|
const tokenType = new TokenType3(tokenTypeBytes);
|
|
12416
12382
|
const tokenIdBytes = new Uint8Array(32);
|
|
12417
12383
|
crypto.getRandomValues(tokenIdBytes);
|
|
12418
|
-
const tokenId = new
|
|
12384
|
+
const tokenId = new TokenId4(tokenIdBytes);
|
|
12419
12385
|
const coinIdBytes = fromHex4(coinIdHex);
|
|
12420
12386
|
const coinId = new CoinId4(coinIdBytes);
|
|
12421
12387
|
const coinData = TokenCoinData3.create([[coinId, amount]]);
|
|
12422
|
-
const addressRef = await
|
|
12388
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
12423
12389
|
tokenType,
|
|
12424
12390
|
signingService.algorithm,
|
|
12425
12391
|
signingService.publicKey,
|
|
@@ -12428,7 +12394,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12428
12394
|
const ownerAddress = await addressRef.toAddress();
|
|
12429
12395
|
const salt = new Uint8Array(32);
|
|
12430
12396
|
crypto.getRandomValues(salt);
|
|
12431
|
-
const mintData = await
|
|
12397
|
+
const mintData = await MintTransactionData2.create(
|
|
12432
12398
|
tokenId,
|
|
12433
12399
|
tokenType,
|
|
12434
12400
|
null,
|
|
@@ -12443,7 +12409,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12443
12409
|
null
|
|
12444
12410
|
// reason: null (genesis, no burn predecessor)
|
|
12445
12411
|
);
|
|
12446
|
-
const commitment = await
|
|
12412
|
+
const commitment = await MintCommitment2.create(mintData);
|
|
12447
12413
|
const MAX_RETRIES = 3;
|
|
12448
12414
|
let lastStatus;
|
|
12449
12415
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
@@ -12460,14 +12426,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12460
12426
|
}
|
|
12461
12427
|
const inclusionProof = await waitInclusionProof5(trustBase, stClient, commitment);
|
|
12462
12428
|
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
12463
|
-
const predicate = await
|
|
12429
|
+
const predicate = await UnmaskedPredicate4.create(
|
|
12464
12430
|
tokenId,
|
|
12465
12431
|
tokenType,
|
|
12466
12432
|
signingService,
|
|
12467
12433
|
HashAlgorithm5.SHA256,
|
|
12468
12434
|
salt
|
|
12469
12435
|
);
|
|
12470
|
-
const tokenState = new
|
|
12436
|
+
const tokenState = new TokenState4(predicate, null);
|
|
12471
12437
|
const sdkToken = await SdkToken2.mint(trustBase, tokenState, genesisTransaction);
|
|
12472
12438
|
const tokenIdHex = tokenId.toJSON();
|
|
12473
12439
|
const symbol = this.getCoinSymbol(coinIdHex);
|
|
@@ -12494,29 +12460,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12494
12460
|
return { success: false, error: `Local mint failed: ${msg}` };
|
|
12495
12461
|
}
|
|
12496
12462
|
}
|
|
12497
|
-
/**
|
|
12498
|
-
* Check if a nametag is available for minting
|
|
12499
|
-
* @param nametag - The nametag to check (e.g., "alice" or "@alice")
|
|
12500
|
-
*/
|
|
12501
|
-
async isNametagAvailable(nametag) {
|
|
12502
|
-
this.ensureInitialized();
|
|
12503
|
-
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
12504
|
-
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
12505
|
-
if (!stClient || !trustBase) {
|
|
12506
|
-
return false;
|
|
12507
|
-
}
|
|
12508
|
-
try {
|
|
12509
|
-
const signingService = await this.createSigningService();
|
|
12510
|
-
const minter = new NametagMinter({
|
|
12511
|
-
stateTransitionClient: stClient,
|
|
12512
|
-
trustBase,
|
|
12513
|
-
signingService
|
|
12514
|
-
});
|
|
12515
|
-
return await minter.isNametagAvailable(nametag);
|
|
12516
|
-
} catch {
|
|
12517
|
-
return false;
|
|
12518
|
-
}
|
|
12519
|
-
}
|
|
12520
12463
|
// ===========================================================================
|
|
12521
12464
|
// Public API - Sync & Validate
|
|
12522
12465
|
// ===========================================================================
|
|
@@ -12832,7 +12775,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12832
12775
|
const privateKeyBytes = new Uint8Array(
|
|
12833
12776
|
privateKeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
|
12834
12777
|
);
|
|
12835
|
-
return
|
|
12778
|
+
return SigningService2.createFromSecret(privateKeyBytes);
|
|
12836
12779
|
}
|
|
12837
12780
|
/**
|
|
12838
12781
|
* Get the wallet's signing public key (used for token ownership predicates).
|
|
@@ -12847,14 +12790,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12847
12790
|
* Create DirectAddress from a public key using UnmaskedPredicateReference
|
|
12848
12791
|
*/
|
|
12849
12792
|
async createDirectAddressFromPubkey(pubkeyHex) {
|
|
12850
|
-
const { UnmaskedPredicateReference:
|
|
12851
|
-
const { TokenType:
|
|
12852
|
-
const
|
|
12853
|
-
const tokenType = new
|
|
12793
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12794
|
+
const { TokenType: TokenType4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
12795
|
+
const UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
12796
|
+
const tokenType = new TokenType4(Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex"));
|
|
12854
12797
|
const pubkeyBytes = new Uint8Array(
|
|
12855
12798
|
pubkeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
|
12856
12799
|
);
|
|
12857
|
-
const addressRef = await
|
|
12800
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
12858
12801
|
tokenType,
|
|
12859
12802
|
"secp256k1",
|
|
12860
12803
|
pubkeyBytes,
|
|
@@ -12866,10 +12809,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12866
12809
|
* Resolve recipient to IAddress for L3 transfers.
|
|
12867
12810
|
* Uses pre-resolved PeerInfo when available to avoid redundant network queries.
|
|
12868
12811
|
*/
|
|
12869
|
-
async resolveRecipientAddress(recipient,
|
|
12812
|
+
async resolveRecipientAddress(recipient, _addressMode = "auto", peerInfo) {
|
|
12870
12813
|
const { AddressFactory } = await import("@unicitylabs/state-transition-sdk/lib/address/AddressFactory");
|
|
12871
|
-
|
|
12872
|
-
if (recipient.startsWith("PROXY:") || recipient.startsWith("DIRECT:")) {
|
|
12814
|
+
if (recipient.startsWith("DIRECT:")) {
|
|
12873
12815
|
return AddressFactory.createAddress(recipient);
|
|
12874
12816
|
}
|
|
12875
12817
|
if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {
|
|
@@ -12879,28 +12821,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12879
12821
|
const info = peerInfo ?? await this.deps?.transport.resolve?.(recipient) ?? null;
|
|
12880
12822
|
if (!info) {
|
|
12881
12823
|
throw new SphereError(
|
|
12882
|
-
`Recipient "${recipient}" not found. Use @nametag, a
|
|
12824
|
+
`Recipient "${recipient}" not found. Use @nametag, a DIRECT: address, or a 33-byte hex pubkey.`,
|
|
12883
12825
|
"INVALID_RECIPIENT"
|
|
12884
12826
|
);
|
|
12885
12827
|
}
|
|
12886
12828
|
const nametag = recipient.startsWith("@") ? recipient.slice(1) : info.nametag || recipient;
|
|
12887
|
-
if (
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
if (!info.directAddress) {
|
|
12893
|
-
throw new SphereError(`"${nametag}" has no DirectAddress stored. It may be a legacy registration.`, "INVALID_RECIPIENT");
|
|
12894
|
-
}
|
|
12895
|
-
logger.debug("Payments", `Using DirectAddress for "${nametag}" (forced): ${info.directAddress.slice(0, 30)}...`);
|
|
12896
|
-
return AddressFactory.createAddress(info.directAddress);
|
|
12897
|
-
}
|
|
12898
|
-
if (info.directAddress) {
|
|
12899
|
-
logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
|
|
12900
|
-
return AddressFactory.createAddress(info.directAddress);
|
|
12829
|
+
if (!info.directAddress) {
|
|
12830
|
+
throw new SphereError(
|
|
12831
|
+
`"${nametag}" has no DirectAddress \u2014 the recipient must publish a key-based identity binding.`,
|
|
12832
|
+
"INVALID_RECIPIENT"
|
|
12833
|
+
);
|
|
12901
12834
|
}
|
|
12902
|
-
logger.debug("Payments", `Using
|
|
12903
|
-
return
|
|
12835
|
+
logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
|
|
12836
|
+
return AddressFactory.createAddress(info.directAddress);
|
|
12904
12837
|
}
|
|
12905
12838
|
/**
|
|
12906
12839
|
* Handle NOSTR-FIRST commitment-only transfer (recipient side)
|
|
@@ -12955,14 +12888,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12955
12888
|
const addressScheme = recipientAddress.scheme;
|
|
12956
12889
|
const signingService = await this.createSigningService();
|
|
12957
12890
|
const transferSalt = transferTx.data.salt;
|
|
12958
|
-
const recipientPredicate = await
|
|
12891
|
+
const recipientPredicate = await UnmaskedPredicate4.create(
|
|
12959
12892
|
sourceToken.id,
|
|
12960
12893
|
sourceToken.type,
|
|
12961
12894
|
signingService,
|
|
12962
12895
|
HashAlgorithm5.SHA256,
|
|
12963
12896
|
transferSalt
|
|
12964
12897
|
);
|
|
12965
|
-
const recipientState = new
|
|
12898
|
+
const recipientState = new TokenState4(recipientPredicate, null);
|
|
12966
12899
|
let nametagTokens = [];
|
|
12967
12900
|
if (addressScheme === AddressScheme.PROXY) {
|
|
12968
12901
|
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
@@ -13059,6 +12992,67 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13059
12992
|
}
|
|
13060
12993
|
}
|
|
13061
12994
|
}
|
|
12995
|
+
/**
|
|
12996
|
+
* v2 engine transfer (sender-driven): the sender handed us a FINISHED token.
|
|
12997
|
+
* Decode the blob, dedup by the genesis-stable token id, store it as a
|
|
12998
|
+
* confirmed token, and emit/record the receipt. No commitment / inclusion-proof
|
|
12999
|
+
* / finalization round-trip (contrast the v1 sourceToken+transferTx path).
|
|
13000
|
+
*/
|
|
13001
|
+
async handleV2Transfer(payload, senderPubkey) {
|
|
13002
|
+
this.ensureInitialized();
|
|
13003
|
+
if (!this.loaded && this.loadedPromise) {
|
|
13004
|
+
await this.loadedPromise;
|
|
13005
|
+
}
|
|
13006
|
+
const engine = this.deps.tokenEngine;
|
|
13007
|
+
if (!engine) return;
|
|
13008
|
+
let token;
|
|
13009
|
+
try {
|
|
13010
|
+
token = await engine.decodeToken(decodeTokenBlob(hexToBytes2(payload.tokenBlob)));
|
|
13011
|
+
} catch (err) {
|
|
13012
|
+
logger.error("Payments", "V2 transfer: failed to decode token blob:", err);
|
|
13013
|
+
return;
|
|
13014
|
+
}
|
|
13015
|
+
const id = `v2_${engine.tokenId(token)}`;
|
|
13016
|
+
if (this.tokens.has(id)) {
|
|
13017
|
+
logger.debug("Payments", `V2 transfer ${id.slice(0, 16)}... already present, skipping`);
|
|
13018
|
+
return;
|
|
13019
|
+
}
|
|
13020
|
+
const info = await parseTokenInfo(payload.tokenBlob, engine);
|
|
13021
|
+
const registry = TokenRegistry.getInstance();
|
|
13022
|
+
const uiToken = {
|
|
13023
|
+
id,
|
|
13024
|
+
coinId: info.coinId,
|
|
13025
|
+
symbol: registry.getSymbol(info.coinId) || info.symbol,
|
|
13026
|
+
name: registry.getName(info.coinId) || info.name,
|
|
13027
|
+
decimals: registry.getDecimals(info.coinId) ?? info.decimals,
|
|
13028
|
+
amount: info.amount,
|
|
13029
|
+
status: "confirmed",
|
|
13030
|
+
createdAt: Date.now(),
|
|
13031
|
+
updatedAt: Date.now(),
|
|
13032
|
+
sdkData: payload.tokenBlob
|
|
13033
|
+
};
|
|
13034
|
+
await this.addToken(uiToken);
|
|
13035
|
+
const senderInfo = await this.resolveSenderInfo(senderPubkey);
|
|
13036
|
+
this.deps.emitEvent("transfer:incoming", {
|
|
13037
|
+
id,
|
|
13038
|
+
senderPubkey,
|
|
13039
|
+
senderNametag: senderInfo.senderNametag,
|
|
13040
|
+
tokens: [uiToken],
|
|
13041
|
+
memo: payload.memo,
|
|
13042
|
+
receivedAt: Date.now()
|
|
13043
|
+
});
|
|
13044
|
+
await this.addToHistory({
|
|
13045
|
+
type: "RECEIVED",
|
|
13046
|
+
amount: info.amount,
|
|
13047
|
+
coinId: info.coinId,
|
|
13048
|
+
symbol: uiToken.symbol,
|
|
13049
|
+
timestamp: Date.now(),
|
|
13050
|
+
senderPubkey,
|
|
13051
|
+
...senderInfo,
|
|
13052
|
+
memo: payload.memo,
|
|
13053
|
+
tokenId: id
|
|
13054
|
+
});
|
|
13055
|
+
}
|
|
13062
13056
|
async handleIncomingTransfer(transfer) {
|
|
13063
13057
|
if (!this.loaded && this.loadedPromise) {
|
|
13064
13058
|
await this.loadedPromise;
|
|
@@ -13066,6 +13060,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13066
13060
|
try {
|
|
13067
13061
|
const payload = transfer.payload;
|
|
13068
13062
|
logger.debug("Payments", "handleIncomingTransfer: keys=", Object.keys(payload).join(","));
|
|
13063
|
+
if (this.deps.tokenEngine && isV2TransferPayload(transfer.payload)) {
|
|
13064
|
+
await this.handleV2Transfer(transfer.payload, transfer.senderTransportPubkey);
|
|
13065
|
+
return;
|
|
13066
|
+
}
|
|
13069
13067
|
let combinedBundle = null;
|
|
13070
13068
|
if (isCombinedTransferBundleV6(payload)) {
|
|
13071
13069
|
combinedBundle = payload;
|
|
@@ -13147,7 +13145,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13147
13145
|
const hasTransactionData = transferTxInput.transactionData !== void 0;
|
|
13148
13146
|
const hasAuthenticator = transferTxInput.authenticator !== void 0;
|
|
13149
13147
|
if (hasData && hasInclusionProof) {
|
|
13150
|
-
transferTx = await
|
|
13148
|
+
transferTx = await TransferTransaction3.fromJSON(transferTxInput);
|
|
13151
13149
|
} else if (hasTransactionData && hasAuthenticator) {
|
|
13152
13150
|
const commitment = await TransferCommitment4.fromJSON(transferTxInput);
|
|
13153
13151
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
@@ -13168,7 +13166,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13168
13166
|
transferTx = commitment.toTransaction(inclusionProof);
|
|
13169
13167
|
} else {
|
|
13170
13168
|
try {
|
|
13171
|
-
transferTx = await
|
|
13169
|
+
transferTx = await TransferTransaction3.fromJSON(transferTxInput);
|
|
13172
13170
|
} catch {
|
|
13173
13171
|
const commitment = await TransferCommitment4.fromJSON(transferTxInput);
|
|
13174
13172
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
@@ -13210,7 +13208,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13210
13208
|
logger.warn("Payments", "Received invalid token");
|
|
13211
13209
|
return;
|
|
13212
13210
|
}
|
|
13213
|
-
const tokenInfo = await parseTokenInfo(tokenData);
|
|
13211
|
+
const tokenInfo = await parseTokenInfo(tokenData, this.deps?.tokenEngine);
|
|
13214
13212
|
const token = {
|
|
13215
13213
|
id: tokenInfo.tokenId ?? crypto.randomUUID(),
|
|
13216
13214
|
coinId: tokenInfo.coinId,
|
|
@@ -13516,24 +13514,6 @@ function createPaymentsModule(config) {
|
|
|
13516
13514
|
return new PaymentsModule(config);
|
|
13517
13515
|
}
|
|
13518
13516
|
|
|
13519
|
-
// modules/payments/TokenSplitCalculator.ts
|
|
13520
|
-
init_logger();
|
|
13521
|
-
import { Token as SdkToken3 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
13522
|
-
import { CoinId as CoinId5 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
13523
|
-
|
|
13524
|
-
// modules/payments/BackgroundCommitmentService.ts
|
|
13525
|
-
init_logger();
|
|
13526
|
-
init_errors();
|
|
13527
|
-
|
|
13528
|
-
// modules/payments/TokenRecoveryService.ts
|
|
13529
|
-
init_logger();
|
|
13530
|
-
import { TokenId as TokenId4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
13531
|
-
import { TokenState as TokenState6 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
13532
|
-
import { TokenType as TokenType4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
13533
|
-
import { CoinId as CoinId6 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
13534
|
-
import { HashAlgorithm as HashAlgorithm6 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
13535
|
-
import { UnmaskedPredicate as UnmaskedPredicate6 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
13536
|
-
|
|
13537
13517
|
// modules/communications/CommunicationsModule.ts
|
|
13538
13518
|
init_logger();
|
|
13539
13519
|
init_errors();
|
|
@@ -17175,7 +17155,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17175
17155
|
const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : void 0;
|
|
17176
17156
|
return abytes(bytes, sizer);
|
|
17177
17157
|
}
|
|
17178
|
-
class
|
|
17158
|
+
class Signature2 {
|
|
17179
17159
|
r;
|
|
17180
17160
|
s;
|
|
17181
17161
|
recovery;
|
|
@@ -17195,7 +17175,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17195
17175
|
let recid;
|
|
17196
17176
|
if (format === "der") {
|
|
17197
17177
|
const { r: r2, s: s2 } = DER.toSig(abytes(bytes));
|
|
17198
|
-
return new
|
|
17178
|
+
return new Signature2(r2, s2);
|
|
17199
17179
|
}
|
|
17200
17180
|
if (format === "recovered") {
|
|
17201
17181
|
recid = bytes[0];
|
|
@@ -17205,7 +17185,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17205
17185
|
const L = lengths.signature / 2;
|
|
17206
17186
|
const r = bytes.subarray(0, L);
|
|
17207
17187
|
const s = bytes.subarray(L, L * 2);
|
|
17208
|
-
return new
|
|
17188
|
+
return new Signature2(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
17209
17189
|
}
|
|
17210
17190
|
static fromHex(hex, format) {
|
|
17211
17191
|
return this.fromBytes(hexToBytes(hex), format);
|
|
@@ -17217,7 +17197,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17217
17197
|
return recovery;
|
|
17218
17198
|
}
|
|
17219
17199
|
addRecoveryBit(recovery) {
|
|
17220
|
-
return new
|
|
17200
|
+
return new Signature2(this.r, this.s, recovery);
|
|
17221
17201
|
}
|
|
17222
17202
|
recoverPublicKey(messageHash) {
|
|
17223
17203
|
const { r, s } = this;
|
|
@@ -17309,7 +17289,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17309
17289
|
normS = Fn.neg(s);
|
|
17310
17290
|
recovery ^= 1;
|
|
17311
17291
|
}
|
|
17312
|
-
return new
|
|
17292
|
+
return new Signature2(r, normS, hasLargeCofactor ? void 0 : recovery);
|
|
17313
17293
|
}
|
|
17314
17294
|
return { seed, k2sig };
|
|
17315
17295
|
}
|
|
@@ -17324,12 +17304,12 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17324
17304
|
publicKey = abytes(publicKey, void 0, "publicKey");
|
|
17325
17305
|
message = validateMsgAndHash(message, prehash);
|
|
17326
17306
|
if (!isBytes(signature)) {
|
|
17327
|
-
const end = signature instanceof
|
|
17307
|
+
const end = signature instanceof Signature2 ? ", use sig.toBytes()" : "";
|
|
17328
17308
|
throw new Error("verify expects Uint8Array signature" + end);
|
|
17329
17309
|
}
|
|
17330
17310
|
validateSigLength(signature, format);
|
|
17331
17311
|
try {
|
|
17332
|
-
const sig =
|
|
17312
|
+
const sig = Signature2.fromBytes(signature, format);
|
|
17333
17313
|
const P = Point.fromBytes(publicKey);
|
|
17334
17314
|
if (lowS && sig.hasHighS())
|
|
17335
17315
|
return false;
|
|
@@ -17350,7 +17330,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17350
17330
|
function recoverPublicKey(signature, message, opts = {}) {
|
|
17351
17331
|
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|
17352
17332
|
message = validateMsgAndHash(message, prehash);
|
|
17353
|
-
return
|
|
17333
|
+
return Signature2.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes();
|
|
17354
17334
|
}
|
|
17355
17335
|
return Object.freeze({
|
|
17356
17336
|
keygen,
|
|
@@ -17362,7 +17342,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17362
17342
|
sign,
|
|
17363
17343
|
verify,
|
|
17364
17344
|
recoverPublicKey,
|
|
17365
|
-
Signature,
|
|
17345
|
+
Signature: Signature2,
|
|
17366
17346
|
hash
|
|
17367
17347
|
});
|
|
17368
17348
|
}
|
|
@@ -18634,7 +18614,7 @@ function freezeCoinAsset(coinAsset, state, latestSender) {
|
|
|
18634
18614
|
}
|
|
18635
18615
|
|
|
18636
18616
|
// modules/accounting/AccountingModule.ts
|
|
18637
|
-
import { Token as
|
|
18617
|
+
import { Token as SdkToken3 } from "@unicitylabs/state-transition-sdk/lib/token/Token.js";
|
|
18638
18618
|
var LOG_TAG2 = "Accounting";
|
|
18639
18619
|
var INV_LEDGER_PREFIX = "inv_ledger:";
|
|
18640
18620
|
var AccountingModule = class _AccountingModule {
|
|
@@ -19287,129 +19267,146 @@ var AccountingModule = class _AccountingModule {
|
|
|
19287
19267
|
);
|
|
19288
19268
|
}
|
|
19289
19269
|
try {
|
|
19290
|
-
|
|
19291
|
-
|
|
19292
|
-
const
|
|
19293
|
-
|
|
19294
|
-
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19270
|
+
let invoiceId;
|
|
19271
|
+
let sdkData;
|
|
19272
|
+
const engine = deps.tokenEngine;
|
|
19273
|
+
if (engine) {
|
|
19274
|
+
const invoiceToken = await engine.mintDataToken({
|
|
19275
|
+
recipientPubkey: hexToBytes(deps.identity.chainPubkey),
|
|
19276
|
+
data: invoiceBytesEncoded,
|
|
19277
|
+
tokenType: hexToBytes(INVOICE_TOKEN_TYPE_HEX),
|
|
19278
|
+
salt
|
|
19279
|
+
});
|
|
19280
|
+
invoiceId = engine.tokenId(invoiceToken);
|
|
19281
|
+
if (this.invoiceTermsCache.has(invoiceId)) {
|
|
19282
|
+
throw new SphereError(`Invoice already exists locally: ${invoiceId}`, "INVOICE_ALREADY_EXISTS");
|
|
19283
|
+
}
|
|
19284
|
+
sdkData = bytesToHex(encodeTokenBlob(engine.encodeToken(invoiceToken)));
|
|
19285
|
+
} else {
|
|
19286
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
|
|
19287
|
+
const { TokenType: TokenType4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType.js");
|
|
19288
|
+
const { MintTransactionData: MintTransactionData3 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData.js");
|
|
19289
|
+
const { MintCommitment: MintCommitment3 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment.js");
|
|
19290
|
+
const { SigningService: SigningService3 } = await import("@unicitylabs/state-transition-sdk/lib/sign/SigningService.js");
|
|
19291
|
+
const { HashAlgorithm: HashAlgorithm6 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
|
|
19292
|
+
const { DataHasher: DataHasher2 } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19293
|
+
const { UnmaskedPredicate: UnmaskedPredicate5 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate.js");
|
|
19294
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference.js");
|
|
19295
|
+
const { TokenState: TokenState5 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenState.js");
|
|
19296
|
+
const { Token: SdkToken4 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token.js");
|
|
19297
|
+
const { waitInclusionProof: waitInclusionProof6 } = await import("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js");
|
|
19298
|
+
const hash = await new DataHasher2(HashAlgorithm6.SHA256).update(invoiceBytesEncoded).digest();
|
|
19299
|
+
const invoiceTokenId = new TokenId4(hash.imprint);
|
|
19300
|
+
invoiceId = invoiceTokenId.toJSON();
|
|
19301
|
+
if (this.invoiceTermsCache.has(invoiceId)) {
|
|
19302
|
+
throw new SphereError(
|
|
19303
|
+
`Invoice already exists locally: ${invoiceId}`,
|
|
19304
|
+
"INVOICE_ALREADY_EXISTS"
|
|
19305
|
+
);
|
|
19306
|
+
}
|
|
19307
|
+
const invoiceTokenType = new TokenType4(
|
|
19308
|
+
Buffer.from(INVOICE_TOKEN_TYPE_HEX, "hex")
|
|
19309
19309
|
);
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19322
|
-
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
|
|
19332
|
-
|
|
19333
|
-
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
|
|
19342
|
-
|
|
19343
|
-
const MAX_RETRIES = 3;
|
|
19344
|
-
let submitSuccess = false;
|
|
19345
|
-
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
19346
|
-
try {
|
|
19347
|
-
if (this.config.debug) {
|
|
19348
|
-
logger.debug(
|
|
19349
|
-
LOG_TAG2,
|
|
19350
|
-
`Submitting invoice commitment (attempt ${attempt}/${MAX_RETRIES})...`
|
|
19351
|
-
);
|
|
19352
|
-
}
|
|
19353
|
-
const response = await stClient.submitMintCommitment(commitment);
|
|
19354
|
-
if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
|
|
19310
|
+
const signingService = await SigningService3.createFromSecret(signingKeyBytes);
|
|
19311
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
19312
|
+
invoiceTokenType,
|
|
19313
|
+
signingService.algorithm,
|
|
19314
|
+
signingService.publicKey,
|
|
19315
|
+
HashAlgorithm6.SHA256
|
|
19316
|
+
);
|
|
19317
|
+
const ownerAddress = await addressRef.toAddress();
|
|
19318
|
+
const mintData = await MintTransactionData3.create(
|
|
19319
|
+
invoiceTokenId,
|
|
19320
|
+
invoiceTokenType,
|
|
19321
|
+
invoiceBytesEncoded,
|
|
19322
|
+
// tokenData: serialized InvoiceTerms (UTF-8 JSON)
|
|
19323
|
+
null,
|
|
19324
|
+
// coinData: null (non-fungible invoice token)
|
|
19325
|
+
ownerAddress,
|
|
19326
|
+
salt,
|
|
19327
|
+
null,
|
|
19328
|
+
// recipientDataHash: null
|
|
19329
|
+
null
|
|
19330
|
+
// reason: null
|
|
19331
|
+
);
|
|
19332
|
+
if (this.config.debug) {
|
|
19333
|
+
logger.debug(LOG_TAG2, `Created MintTransactionData for invoice ${invoiceId}`);
|
|
19334
|
+
}
|
|
19335
|
+
const commitment = await MintCommitment3.create(mintData);
|
|
19336
|
+
if (this.config.debug) {
|
|
19337
|
+
logger.debug(LOG_TAG2, "Created MintCommitment for invoice");
|
|
19338
|
+
}
|
|
19339
|
+
const MAX_RETRIES = 3;
|
|
19340
|
+
let submitSuccess = false;
|
|
19341
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
19342
|
+
try {
|
|
19355
19343
|
if (this.config.debug) {
|
|
19356
19344
|
logger.debug(
|
|
19357
19345
|
LOG_TAG2,
|
|
19358
|
-
|
|
19346
|
+
`Submitting invoice commitment (attempt ${attempt}/${MAX_RETRIES})...`
|
|
19359
19347
|
);
|
|
19360
19348
|
}
|
|
19361
|
-
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
|
|
19349
|
+
const response = await stClient.submitMintCommitment(commitment);
|
|
19350
|
+
if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
|
|
19351
|
+
if (this.config.debug) {
|
|
19352
|
+
logger.debug(
|
|
19353
|
+
LOG_TAG2,
|
|
19354
|
+
response.status === "REQUEST_ID_EXISTS" ? "Invoice commitment already exists (idempotent re-mint)" : "Invoice commitment submitted successfully"
|
|
19355
|
+
);
|
|
19356
|
+
}
|
|
19357
|
+
submitSuccess = true;
|
|
19358
|
+
break;
|
|
19359
|
+
} else {
|
|
19360
|
+
logger.warn(LOG_TAG2, `Invoice commitment submission failed: ${response.status}`);
|
|
19361
|
+
if (attempt === MAX_RETRIES) {
|
|
19362
|
+
throw new SphereError(
|
|
19363
|
+
`Failed to mint invoice token: commitment rejected after ${MAX_RETRIES} attempts: ${response.status}`,
|
|
19364
|
+
"INVOICE_MINT_FAILED"
|
|
19365
|
+
);
|
|
19366
|
+
}
|
|
19367
|
+
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19368
|
+
}
|
|
19369
|
+
} catch (retryErr) {
|
|
19370
|
+
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;
|
|
19371
|
+
logger.warn(LOG_TAG2, `Invoice commitment attempt ${attempt} error:`, retryErr);
|
|
19365
19372
|
if (attempt === MAX_RETRIES) {
|
|
19366
19373
|
throw new SphereError(
|
|
19367
|
-
`Failed to mint invoice token:
|
|
19368
|
-
"INVOICE_MINT_FAILED"
|
|
19374
|
+
`Failed to mint invoice token: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
|
|
19375
|
+
"INVOICE_MINT_FAILED",
|
|
19376
|
+
retryErr
|
|
19369
19377
|
);
|
|
19370
19378
|
}
|
|
19371
19379
|
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19372
19380
|
}
|
|
19373
|
-
} catch (retryErr) {
|
|
19374
|
-
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;
|
|
19375
|
-
logger.warn(LOG_TAG2, `Invoice commitment attempt ${attempt} error:`, retryErr);
|
|
19376
|
-
if (attempt === MAX_RETRIES) {
|
|
19377
|
-
throw new SphereError(
|
|
19378
|
-
`Failed to mint invoice token: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
|
|
19379
|
-
"INVOICE_MINT_FAILED",
|
|
19380
|
-
retryErr
|
|
19381
|
-
);
|
|
19382
|
-
}
|
|
19383
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19384
19381
|
}
|
|
19385
|
-
|
|
19386
|
-
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
|
|
19382
|
+
if (!submitSuccess) {
|
|
19383
|
+
throw new SphereError(
|
|
19384
|
+
"Failed to mint invoice token: commitment submission failed after retries",
|
|
19385
|
+
"INVOICE_MINT_FAILED"
|
|
19386
|
+
);
|
|
19387
|
+
}
|
|
19388
|
+
if (this.config.debug) {
|
|
19389
|
+
logger.debug(LOG_TAG2, "Waiting for invoice inclusion proof...");
|
|
19390
|
+
}
|
|
19391
|
+
const inclusionProof = await waitInclusionProof6(trustBase, stClient, commitment);
|
|
19392
|
+
if (this.config.debug) {
|
|
19393
|
+
logger.debug(LOG_TAG2, "Invoice inclusion proof received");
|
|
19394
|
+
}
|
|
19395
|
+
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
19396
|
+
const invoicePredicate = await UnmaskedPredicate5.create(
|
|
19397
|
+
invoiceTokenId,
|
|
19398
|
+
invoiceTokenType,
|
|
19399
|
+
signingService,
|
|
19400
|
+
HashAlgorithm6.SHA256,
|
|
19401
|
+
salt
|
|
19390
19402
|
);
|
|
19403
|
+
const tokenState = new TokenState5(invoicePredicate, null);
|
|
19404
|
+
const sdkToken = await SdkToken4.mint(trustBase, tokenState, genesisTransaction);
|
|
19405
|
+
if (this.config.debug) {
|
|
19406
|
+
logger.debug(LOG_TAG2, "Invoice token minted successfully");
|
|
19407
|
+
}
|
|
19408
|
+
sdkData = JSON.stringify(sdkToken.toJSON());
|
|
19391
19409
|
}
|
|
19392
|
-
if (this.config.debug) {
|
|
19393
|
-
logger.debug(LOG_TAG2, "Waiting for invoice inclusion proof...");
|
|
19394
|
-
}
|
|
19395
|
-
const inclusionProof = await waitInclusionProof6(trustBase, stClient, commitment);
|
|
19396
|
-
if (this.config.debug) {
|
|
19397
|
-
logger.debug(LOG_TAG2, "Invoice inclusion proof received");
|
|
19398
|
-
}
|
|
19399
|
-
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
19400
|
-
const invoicePredicate = await UnmaskedPredicate7.create(
|
|
19401
|
-
invoiceTokenId,
|
|
19402
|
-
invoiceTokenType,
|
|
19403
|
-
signingService,
|
|
19404
|
-
HashAlgorithm8.SHA256,
|
|
19405
|
-
salt
|
|
19406
|
-
);
|
|
19407
|
-
const tokenState = new TokenState7(invoicePredicate, null);
|
|
19408
|
-
const sdkToken = await SdkToken5.mint(trustBase, tokenState, genesisTransaction);
|
|
19409
|
-
if (this.config.debug) {
|
|
19410
|
-
logger.debug(LOG_TAG2, "Invoice token minted successfully");
|
|
19411
|
-
}
|
|
19412
|
-
const sdkTokenJson = sdkToken.toJSON();
|
|
19413
19410
|
const uiToken = {
|
|
19414
19411
|
id: invoiceId,
|
|
19415
19412
|
coinId: INVOICE_TOKEN_TYPE_HEX,
|
|
@@ -19420,7 +19417,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19420
19417
|
status: "confirmed",
|
|
19421
19418
|
createdAt: terms.createdAt,
|
|
19422
19419
|
updatedAt: terms.createdAt,
|
|
19423
|
-
sdkData
|
|
19420
|
+
sdkData
|
|
19424
19421
|
};
|
|
19425
19422
|
await deps.payments.addToken(uiToken);
|
|
19426
19423
|
this.invoiceTermsCache.set(invoiceId, this._normalizeInvoiceTerms(terms));
|
|
@@ -19431,17 +19428,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19431
19428
|
const allTokens = deps.payments.getTokens();
|
|
19432
19429
|
let anyScanDirty = false;
|
|
19433
19430
|
for (const token of allTokens) {
|
|
19434
|
-
if (
|
|
19435
|
-
let txf;
|
|
19436
|
-
try {
|
|
19437
|
-
txf = JSON.parse(token.sdkData);
|
|
19438
|
-
} catch {
|
|
19439
|
-
continue;
|
|
19440
|
-
}
|
|
19441
|
-
const txCount = txf.transactions?.length ?? 0;
|
|
19442
|
-
if (txCount === 0) continue;
|
|
19443
|
-
this._processTokenTransactions(token.id, txf, 0);
|
|
19444
|
-
anyScanDirty = true;
|
|
19431
|
+
if (await this._scanTokenForAttribution(token, 0)) anyScanDirty = true;
|
|
19445
19432
|
}
|
|
19446
19433
|
const archivedTokensForScan = deps.payments.getArchivedTokens();
|
|
19447
19434
|
for (const [archivedId, txf] of archivedTokensForScan) {
|
|
@@ -19457,7 +19444,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19457
19444
|
if (this.config.debug) {
|
|
19458
19445
|
logger.debug(LOG_TAG2, `Invoice created and stored: ${invoiceId}`);
|
|
19459
19446
|
}
|
|
19460
|
-
const txfToken =
|
|
19447
|
+
const txfToken = engine ? sdkData : JSON.parse(sdkData);
|
|
19461
19448
|
return {
|
|
19462
19449
|
success: true,
|
|
19463
19450
|
invoiceId,
|
|
@@ -19498,36 +19485,60 @@ var AccountingModule = class _AccountingModule {
|
|
|
19498
19485
|
this.ensureNotDestroyed();
|
|
19499
19486
|
this.ensureInitialized();
|
|
19500
19487
|
const deps = this.deps;
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
);
|
|
19514
|
-
|
|
19515
|
-
|
|
19516
|
-
|
|
19488
|
+
let terms;
|
|
19489
|
+
let tokenId;
|
|
19490
|
+
let sdkDataForStore;
|
|
19491
|
+
const engine = deps.tokenEngine;
|
|
19492
|
+
const isV2 = !!engine && typeof token === "string";
|
|
19493
|
+
if (isV2) {
|
|
19494
|
+
const sphereToken = await engine.decodeToken(decodeTokenBlob(hexToBytes(token)));
|
|
19495
|
+
const verifyResult = await engine.verify(sphereToken);
|
|
19496
|
+
if (!verifyResult.ok) {
|
|
19497
|
+
throw new SphereError("Invoice import failed: inclusion proof is invalid.", "INVOICE_INVALID_PROOF");
|
|
19498
|
+
}
|
|
19499
|
+
tokenId = engine.tokenId(sphereToken);
|
|
19500
|
+
const data = engine.readTokenData(sphereToken);
|
|
19501
|
+
if (!data) {
|
|
19502
|
+
throw new SphereError("Invoice import failed: missing or invalid tokenData field.", "INVOICE_INVALID_DATA");
|
|
19503
|
+
}
|
|
19517
19504
|
try {
|
|
19518
|
-
|
|
19519
|
-
jsonString = new TextDecoder().decode(bytes);
|
|
19505
|
+
terms = JSON.parse(new TextDecoder().decode(data));
|
|
19520
19506
|
} catch {
|
|
19507
|
+
throw new SphereError("Invoice import failed: tokenData is not valid JSON.", "INVOICE_INVALID_DATA");
|
|
19521
19508
|
}
|
|
19522
|
-
|
|
19523
|
-
|
|
19524
|
-
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
|
|
19509
|
+
sdkDataForStore = token;
|
|
19510
|
+
} else {
|
|
19511
|
+
const tokenType = token.genesis?.data?.tokenType;
|
|
19512
|
+
if (tokenType !== INVOICE_TOKEN_TYPE_HEX) {
|
|
19513
|
+
throw new SphereError(
|
|
19514
|
+
`Invoice import failed: token type "${tokenType}" is not the expected invoice type.`,
|
|
19515
|
+
"INVOICE_WRONG_TOKEN_TYPE"
|
|
19516
|
+
);
|
|
19517
|
+
}
|
|
19518
|
+
const tokenData = token.genesis?.data?.tokenData;
|
|
19519
|
+
if (!tokenData || typeof tokenData !== "string") {
|
|
19520
|
+
throw new SphereError(
|
|
19521
|
+
"Invoice import failed: missing or invalid tokenData field.",
|
|
19522
|
+
"INVOICE_INVALID_DATA"
|
|
19523
|
+
);
|
|
19524
|
+
}
|
|
19525
|
+
let jsonString = tokenData;
|
|
19526
|
+
if (!/^\s*[[{"]/.test(tokenData)) {
|
|
19527
|
+
try {
|
|
19528
|
+
const bytes = hexToBytes(tokenData);
|
|
19529
|
+
jsonString = new TextDecoder().decode(bytes);
|
|
19530
|
+
} catch {
|
|
19531
|
+
}
|
|
19532
|
+
}
|
|
19533
|
+
try {
|
|
19534
|
+
terms = JSON.parse(jsonString);
|
|
19535
|
+
} catch {
|
|
19536
|
+
throw new SphereError(
|
|
19537
|
+
"Invoice import failed: tokenData is not valid JSON.",
|
|
19538
|
+
"INVOICE_INVALID_DATA"
|
|
19539
|
+
);
|
|
19540
|
+
}
|
|
19541
|
+
sdkDataForStore = JSON.stringify(token);
|
|
19531
19542
|
}
|
|
19532
19543
|
if (!terms || typeof terms !== "object") {
|
|
19533
19544
|
throw new SphereError(
|
|
@@ -19614,7 +19625,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19614
19625
|
}
|
|
19615
19626
|
}
|
|
19616
19627
|
}
|
|
19617
|
-
|
|
19628
|
+
if (!isV2) tokenId = token.genesis?.data?.tokenId;
|
|
19618
19629
|
if (!tokenId || typeof tokenId !== "string") {
|
|
19619
19630
|
throw new SphereError(
|
|
19620
19631
|
"Invoice import failed: missing tokenId in genesis data.",
|
|
@@ -19627,13 +19638,13 @@ var AccountingModule = class _AccountingModule {
|
|
|
19627
19638
|
"INVOICE_ALREADY_EXISTS"
|
|
19628
19639
|
);
|
|
19629
19640
|
}
|
|
19630
|
-
{
|
|
19631
|
-
const { DataHasher } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19632
|
-
const { HashAlgorithm:
|
|
19633
|
-
const { TokenId:
|
|
19641
|
+
if (!isV2) {
|
|
19642
|
+
const { DataHasher: DataHasher2 } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19643
|
+
const { HashAlgorithm: HashAlgorithm6 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
|
|
19644
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
|
|
19634
19645
|
const reSerializedBytes = new TextEncoder().encode(canonicalSerialize(terms));
|
|
19635
|
-
const hash = await new
|
|
19636
|
-
const reTokenId = new
|
|
19646
|
+
const hash = await new DataHasher2(HashAlgorithm6.SHA256).update(reSerializedBytes).digest();
|
|
19647
|
+
const reTokenId = new TokenId4(hash.imprint).toJSON();
|
|
19637
19648
|
if (reTokenId !== tokenId) {
|
|
19638
19649
|
throw new SphereError(
|
|
19639
19650
|
"Invoice import failed: parsed terms do not match on-chain token ID (canonical hash mismatch).",
|
|
@@ -19641,35 +19652,37 @@ var AccountingModule = class _AccountingModule {
|
|
|
19641
19652
|
);
|
|
19642
19653
|
}
|
|
19643
19654
|
}
|
|
19644
|
-
if (!
|
|
19645
|
-
|
|
19646
|
-
"Trust base unavailable \u2014 cannot verify invoice proof. Ensure oracle supports getTrustBase().",
|
|
19647
|
-
"INVOICE_INVALID_PROOF"
|
|
19648
|
-
);
|
|
19649
|
-
}
|
|
19650
|
-
try {
|
|
19651
|
-
const sdkToken = await SdkToken4.fromJSON(token);
|
|
19652
|
-
const verifyResult = await sdkToken.verify(deps.trustBase);
|
|
19653
|
-
const verifyOk = verifyResult.isSuccessful === true;
|
|
19654
|
-
if (!verifyOk) {
|
|
19655
|
+
if (!isV2) {
|
|
19656
|
+
if (!deps.trustBase || deps.trustBase instanceof Uint8Array && deps.trustBase.length === 0) {
|
|
19655
19657
|
throw new SphereError(
|
|
19656
|
-
"
|
|
19658
|
+
"Trust base unavailable \u2014 cannot verify invoice proof. Ensure oracle supports getTrustBase().",
|
|
19657
19659
|
"INVOICE_INVALID_PROOF"
|
|
19658
19660
|
);
|
|
19659
19661
|
}
|
|
19660
|
-
|
|
19661
|
-
|
|
19662
|
+
try {
|
|
19663
|
+
const sdkToken = await SdkToken3.fromJSON(token);
|
|
19664
|
+
const verifyResult = await sdkToken.verify(deps.trustBase);
|
|
19665
|
+
const verifyOk = verifyResult.isSuccessful === true;
|
|
19666
|
+
if (!verifyOk) {
|
|
19667
|
+
throw new SphereError(
|
|
19668
|
+
"Invoice import failed: inclusion proof is invalid.",
|
|
19669
|
+
"INVOICE_INVALID_PROOF"
|
|
19670
|
+
);
|
|
19671
|
+
}
|
|
19672
|
+
const canonicalTokenId = sdkToken.id?.toJSON?.() ?? null;
|
|
19673
|
+
if (!canonicalTokenId || canonicalTokenId !== tokenId) {
|
|
19674
|
+
throw new SphereError(
|
|
19675
|
+
`Invoice import failed: tokenId mismatch or unverifiable \u2014 JSON claims ${tokenId}, cryptographic identity is ${canonicalTokenId ?? "unknown"}`,
|
|
19676
|
+
"INVOICE_INVALID_DATA"
|
|
19677
|
+
);
|
|
19678
|
+
}
|
|
19679
|
+
} catch (err) {
|
|
19680
|
+
if (err instanceof SphereError) throw err;
|
|
19662
19681
|
throw new SphereError(
|
|
19663
|
-
`Invoice import failed:
|
|
19664
|
-
"
|
|
19682
|
+
`Invoice import failed: proof verification error \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
19683
|
+
"INVOICE_INVALID_PROOF"
|
|
19665
19684
|
);
|
|
19666
19685
|
}
|
|
19667
|
-
} catch (err) {
|
|
19668
|
-
if (err instanceof SphereError) throw err;
|
|
19669
|
-
throw new SphereError(
|
|
19670
|
-
`Invoice import failed: proof verification error \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
19671
|
-
"INVOICE_INVALID_PROOF"
|
|
19672
|
-
);
|
|
19673
19686
|
}
|
|
19674
19687
|
try {
|
|
19675
19688
|
const uiToken = {
|
|
@@ -19682,7 +19695,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19682
19695
|
status: "confirmed",
|
|
19683
19696
|
createdAt: terms.createdAt,
|
|
19684
19697
|
updatedAt: terms.createdAt,
|
|
19685
|
-
sdkData:
|
|
19698
|
+
sdkData: sdkDataForStore
|
|
19686
19699
|
};
|
|
19687
19700
|
await deps.payments.addToken(uiToken);
|
|
19688
19701
|
} catch (err) {
|
|
@@ -19737,17 +19750,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
19737
19750
|
const allTokens = deps.payments.getTokens();
|
|
19738
19751
|
let anyDirty = false;
|
|
19739
19752
|
for (const existingToken of allTokens) {
|
|
19740
|
-
if (!existingToken.sdkData) continue;
|
|
19741
|
-
let txf;
|
|
19742
|
-
try {
|
|
19743
|
-
txf = JSON.parse(existingToken.sdkData);
|
|
19744
|
-
} catch {
|
|
19745
|
-
continue;
|
|
19746
|
-
}
|
|
19747
|
-
const transactions = txf.transactions ?? [];
|
|
19748
19753
|
const startIndex = this.tokenScanState.get(existingToken.id) ?? 0;
|
|
19749
|
-
if (
|
|
19750
|
-
this._processTokenTransactions(existingToken.id, txf, startIndex);
|
|
19754
|
+
if (await this._scanTokenForAttribution(existingToken, startIndex)) {
|
|
19751
19755
|
anyDirty = true;
|
|
19752
19756
|
}
|
|
19753
19757
|
}
|
|
@@ -21637,17 +21641,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
21637
21641
|
const allTokens = deps.payments.getTokens();
|
|
21638
21642
|
let anyDirty = false;
|
|
21639
21643
|
for (const token of allTokens) {
|
|
21640
|
-
if (!token.sdkData) continue;
|
|
21641
|
-
let txf;
|
|
21642
|
-
try {
|
|
21643
|
-
txf = JSON.parse(token.sdkData);
|
|
21644
|
-
} catch {
|
|
21645
|
-
continue;
|
|
21646
|
-
}
|
|
21647
|
-
const transactions = txf.transactions ?? [];
|
|
21648
21644
|
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
21649
|
-
if (
|
|
21650
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21645
|
+
if (await this._scanTokenForAttribution(token, startIndex)) {
|
|
21651
21646
|
anyDirty = true;
|
|
21652
21647
|
}
|
|
21653
21648
|
}
|
|
@@ -21702,6 +21697,53 @@ var AccountingModule = class _AccountingModule {
|
|
|
21702
21697
|
* @param txf - Parsed TxfToken.
|
|
21703
21698
|
* @param startIndex - First unprocessed transaction index.
|
|
21704
21699
|
*/
|
|
21700
|
+
/**
|
|
21701
|
+
* Attribute one payment token's invoice memo(s) to the ledger.
|
|
21702
|
+
*
|
|
21703
|
+
* v2 (engine blob): the token carries a single on-chain memo. We decode it and
|
|
21704
|
+
* shim the token into a v1-shaped `txf` (coinData ← engine.readValue, the memo
|
|
21705
|
+
* ← engine.readMemo) so the battle-hardened `_processTokenTransactions` runs
|
|
21706
|
+
* UNCHANGED — same dedup, direction, provisional/synthetic/orphan handling.
|
|
21707
|
+
* v1 (TXF JSON): parse and scan transactions directly.
|
|
21708
|
+
*
|
|
21709
|
+
* `startIndex` is the per-token watermark (0 for a full retroactive scan).
|
|
21710
|
+
* Returns true when the token was scanned. Async because engine.decodeToken is.
|
|
21711
|
+
*/
|
|
21712
|
+
async _scanTokenForAttribution(token, startIndex) {
|
|
21713
|
+
if (!token.sdkData) return false;
|
|
21714
|
+
const engine = this.deps?.tokenEngine;
|
|
21715
|
+
const isBlob = token.sdkData.length >= 2 && token.sdkData.length % 2 === 0 && token.sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(token.sdkData);
|
|
21716
|
+
if (engine && isBlob) {
|
|
21717
|
+
let syntheticTxf;
|
|
21718
|
+
try {
|
|
21719
|
+
const sphereToken = await engine.decodeToken(decodeTokenBlob(hexToBytes(token.sdkData)));
|
|
21720
|
+
const memo = engine.readMemo(sphereToken);
|
|
21721
|
+
if (!memo) return false;
|
|
21722
|
+
const coinData = (engine.readValue(sphereToken)?.assets ?? []).map(
|
|
21723
|
+
(a) => [a.coinId, a.amount.toString()]
|
|
21724
|
+
);
|
|
21725
|
+
syntheticTxf = {
|
|
21726
|
+
genesis: { data: { coinData } },
|
|
21727
|
+
transactions: [{ data: { message: bytesToHex(memo) }, inclusionProof: {} }]
|
|
21728
|
+
};
|
|
21729
|
+
} catch {
|
|
21730
|
+
return false;
|
|
21731
|
+
}
|
|
21732
|
+
this._processTokenTransactions(token.id, syntheticTxf, startIndex);
|
|
21733
|
+
return true;
|
|
21734
|
+
}
|
|
21735
|
+
let txf;
|
|
21736
|
+
try {
|
|
21737
|
+
txf = JSON.parse(token.sdkData);
|
|
21738
|
+
} catch {
|
|
21739
|
+
return false;
|
|
21740
|
+
}
|
|
21741
|
+
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
21742
|
+
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21743
|
+
return true;
|
|
21744
|
+
}
|
|
21745
|
+
return false;
|
|
21746
|
+
}
|
|
21705
21747
|
_processTokenTransactions(tokenId, txf, startIndex) {
|
|
21706
21748
|
const transactions = txf.transactions ?? [];
|
|
21707
21749
|
let lastSuccessIdx = startIndex;
|
|
@@ -21981,17 +22023,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
21981
22023
|
async _handleIncomingTransfer(transfer) {
|
|
21982
22024
|
if (this.destroyed) return;
|
|
21983
22025
|
for (const token of transfer.tokens) {
|
|
21984
|
-
|
|
21985
|
-
let txf;
|
|
21986
|
-
try {
|
|
21987
|
-
txf = JSON.parse(token.sdkData);
|
|
21988
|
-
} catch {
|
|
21989
|
-
continue;
|
|
21990
|
-
}
|
|
21991
|
-
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
21992
|
-
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
21993
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21994
|
-
}
|
|
22026
|
+
await this._scanTokenForAttribution(token, this.tokenScanState.get(token.id) ?? 0);
|
|
21995
22027
|
}
|
|
21996
22028
|
if (this.destroyed) return;
|
|
21997
22029
|
await this._flushDirtyLedgerEntries();
|
|
@@ -22129,16 +22161,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
22129
22161
|
if (this.destroyed) return;
|
|
22130
22162
|
for (const token of result.tokens) {
|
|
22131
22163
|
if (!token.sdkData) continue;
|
|
22132
|
-
|
|
22133
|
-
try {
|
|
22134
|
-
txf = JSON.parse(token.sdkData);
|
|
22135
|
-
} catch {
|
|
22136
|
-
continue;
|
|
22137
|
-
}
|
|
22138
|
-
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
22139
|
-
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
22140
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
22141
|
-
}
|
|
22164
|
+
await this._scanTokenForAttribution(token, this.tokenScanState.get(token.id) ?? 0);
|
|
22142
22165
|
const relatedInvoices = this.tokenInvoiceMap.get(token.id);
|
|
22143
22166
|
if (relatedInvoices) {
|
|
22144
22167
|
for (const invoiceId of relatedInvoices) {
|
|
@@ -22209,15 +22232,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
22209
22232
|
const tokens = this.deps.payments.getTokens();
|
|
22210
22233
|
const token = tokens.find((t) => t.id === tokenId);
|
|
22211
22234
|
if (!token?.sdkData) return;
|
|
22212
|
-
let txf;
|
|
22213
|
-
try {
|
|
22214
|
-
txf = JSON.parse(token.sdkData);
|
|
22215
|
-
} catch {
|
|
22216
|
-
return;
|
|
22217
|
-
}
|
|
22218
22235
|
const startIndex = this.tokenScanState.get(tokenId) ?? 0;
|
|
22219
|
-
if ((
|
|
22220
|
-
this._processTokenTransactions(tokenId, txf, startIndex);
|
|
22236
|
+
if (await this._scanTokenForAttribution(token, startIndex)) {
|
|
22221
22237
|
if (this.destroyed) return;
|
|
22222
22238
|
await this._flushDirtyLedgerEntries();
|
|
22223
22239
|
}
|
|
@@ -27650,29 +27666,421 @@ async function parseAndDecryptWalletDat(data, password, onProgress) {
|
|
|
27650
27666
|
};
|
|
27651
27667
|
}
|
|
27652
27668
|
|
|
27669
|
+
// token-engine/identity.ts
|
|
27670
|
+
var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
27671
|
+
var SIGNING_ALGORITHM = "secp256k1";
|
|
27672
|
+
var EMBEDDED_PREDICATE_UNMASKED = 0;
|
|
27673
|
+
var HASH_ALGORITHM_SHA256 = 0n;
|
|
27674
|
+
var SHA256_IMPRINT_PREFIX = new Uint8Array([0, 0]);
|
|
27675
|
+
function hexToBytes4(hex) {
|
|
27676
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
27677
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
27678
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
27679
|
+
}
|
|
27680
|
+
return bytes;
|
|
27681
|
+
}
|
|
27682
|
+
function sha2565(data) {
|
|
27683
|
+
return new DataHasher(HashAlgorithm2.SHA256).update(data).digest().then((h) => h.data);
|
|
27684
|
+
}
|
|
27685
|
+
async function deriveDirectAddress(publicKey) {
|
|
27686
|
+
const tokenTypeCbor = CborSerializer.encodeByteString(hexToBytes4(UNICITY_TOKEN_TYPE_HEX));
|
|
27687
|
+
const reference = CborSerializer.encodeArray(
|
|
27688
|
+
CborSerializer.encodeByteString(new Uint8Array([EMBEDDED_PREDICATE_UNMASKED])),
|
|
27689
|
+
CborSerializer.encodeByteString(tokenTypeCbor),
|
|
27690
|
+
CborSerializer.encodeTextString(SIGNING_ALGORITHM),
|
|
27691
|
+
CborSerializer.encodeUnsignedInteger(HASH_ALGORITHM_SHA256),
|
|
27692
|
+
CborSerializer.encodeByteString(publicKey)
|
|
27693
|
+
);
|
|
27694
|
+
const refHash = await sha2565(reference);
|
|
27695
|
+
const imprint = new Uint8Array([...SHA256_IMPRINT_PREFIX, ...refHash]);
|
|
27696
|
+
const checksum = (await sha2565(imprint)).slice(0, 4);
|
|
27697
|
+
return `DIRECT://${HexConverter.encode(imprint)}${HexConverter.encode(checksum)}`;
|
|
27698
|
+
}
|
|
27699
|
+
|
|
27700
|
+
// token-engine/factory.ts
|
|
27701
|
+
init_errors();
|
|
27702
|
+
|
|
27703
|
+
// token-engine/SpherePaymentData.ts
|
|
27704
|
+
init_errors();
|
|
27705
|
+
var COIN_ID_PATTERN = /^([0-9a-f]{2})+$/;
|
|
27706
|
+
function assertAsset(coinId, amount) {
|
|
27707
|
+
if (!COIN_ID_PATTERN.test(coinId)) {
|
|
27708
|
+
throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
|
|
27709
|
+
}
|
|
27710
|
+
if (amount < 0n) {
|
|
27711
|
+
throw new SphereError(`Asset amount must be non-negative: ${amount.toString()}`, "VALIDATION_ERROR");
|
|
27712
|
+
}
|
|
27713
|
+
}
|
|
27714
|
+
function sphereAssetToSdk(coinId, amount) {
|
|
27715
|
+
assertAsset(coinId, amount);
|
|
27716
|
+
return new Asset(new AssetId(HexConverter.decode(coinId)), amount);
|
|
27717
|
+
}
|
|
27718
|
+
var SpherePaymentData = class _SpherePaymentData {
|
|
27719
|
+
constructor(assets, _memo = null) {
|
|
27720
|
+
this.assets = assets;
|
|
27721
|
+
this._memo = _memo;
|
|
27722
|
+
}
|
|
27723
|
+
/** Sphere-private CBOR tag (verified free in the v2 SDK tag space). */
|
|
27724
|
+
static CBOR_TAG = 39050n;
|
|
27725
|
+
/** Envelope version; bump when the structure changes. */
|
|
27726
|
+
static VERSION = 1n;
|
|
27727
|
+
/** Opaque, app-defined memo carried alongside the value (e.g. invoice attribution). */
|
|
27728
|
+
get memo() {
|
|
27729
|
+
return this._memo ? new Uint8Array(this._memo) : null;
|
|
27730
|
+
}
|
|
27731
|
+
/** Wrap an existing SDK asset collection (+ optional opaque memo). */
|
|
27732
|
+
static create(assets, memo = null) {
|
|
27733
|
+
return new _SpherePaymentData(assets, memo);
|
|
27734
|
+
}
|
|
27735
|
+
/** Build from a sphere-domain value (hex coin id → bigint amount) + optional opaque memo. */
|
|
27736
|
+
static fromValue(value, memo = null) {
|
|
27737
|
+
const assets = value.assets.map((a) => sphereAssetToSdk(a.coinId, a.amount));
|
|
27738
|
+
return new _SpherePaymentData(PaymentAssetCollection.create(...assets), memo);
|
|
27739
|
+
}
|
|
27740
|
+
/** Decode from the CBOR envelope produced by {@link encode}. */
|
|
27741
|
+
static fromCBOR(bytes) {
|
|
27742
|
+
const tag = CborDeserializer.decodeTag(bytes);
|
|
27743
|
+
if (tag.tag !== _SpherePaymentData.CBOR_TAG) {
|
|
27744
|
+
throw new CborError(`Invalid SpherePaymentData tag: ${tag.tag}`);
|
|
27745
|
+
}
|
|
27746
|
+
const fields = CborDeserializer.decodeArray(tag.data, 3);
|
|
27747
|
+
const version = CborDeserializer.decodeUnsignedInteger(fields[0]);
|
|
27748
|
+
if (version !== _SpherePaymentData.VERSION) {
|
|
27749
|
+
throw new CborError(`Unsupported SpherePaymentData version: ${version}`);
|
|
27750
|
+
}
|
|
27751
|
+
const memo = CborDeserializer.decodeNullable(fields[2], CborDeserializer.decodeByteString);
|
|
27752
|
+
return new _SpherePaymentData(PaymentAssetCollection.fromCBOR(fields[1]), memo);
|
|
27753
|
+
}
|
|
27754
|
+
/** Deterministic, versioned, tagged CBOR: `tag(39050)[ version, assets, memo? ]`. */
|
|
27755
|
+
encode() {
|
|
27756
|
+
return Promise.resolve(
|
|
27757
|
+
CborSerializer.encodeTag(
|
|
27758
|
+
_SpherePaymentData.CBOR_TAG,
|
|
27759
|
+
CborSerializer.encodeArray(
|
|
27760
|
+
CborSerializer.encodeUnsignedInteger(_SpherePaymentData.VERSION),
|
|
27761
|
+
this.assets.toCBOR(),
|
|
27762
|
+
CborSerializer.encodeNullable(this._memo, CborSerializer.encodeByteString)
|
|
27763
|
+
)
|
|
27764
|
+
)
|
|
27765
|
+
);
|
|
27766
|
+
}
|
|
27767
|
+
/** Project to a sphere-domain value (hex coin id + bigint amount), preserving order. */
|
|
27768
|
+
toValue() {
|
|
27769
|
+
return {
|
|
27770
|
+
assets: this.assets.toArray().map((a) => ({
|
|
27771
|
+
coinId: HexConverter.encode(a.id.bytes),
|
|
27772
|
+
amount: a.value
|
|
27773
|
+
}))
|
|
27774
|
+
};
|
|
27775
|
+
}
|
|
27776
|
+
/** Balance of a single coin within this payload (0n when absent). */
|
|
27777
|
+
balanceOf(coinId) {
|
|
27778
|
+
if (!COIN_ID_PATTERN.test(coinId)) {
|
|
27779
|
+
throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
|
|
27780
|
+
}
|
|
27781
|
+
const asset = this.assets.get(new AssetId(HexConverter.decode(coinId)));
|
|
27782
|
+
return asset ? asset.value : 0n;
|
|
27783
|
+
}
|
|
27784
|
+
};
|
|
27785
|
+
function decodeSpherePaymentData(bytes) {
|
|
27786
|
+
return Promise.resolve(SpherePaymentData.fromCBOR(bytes));
|
|
27787
|
+
}
|
|
27788
|
+
|
|
27789
|
+
// token-engine/SphereTokenEngine.ts
|
|
27790
|
+
init_errors();
|
|
27791
|
+
var SphereTokenEngine = class {
|
|
27792
|
+
constructor(deps) {
|
|
27793
|
+
this.deps = deps;
|
|
27794
|
+
}
|
|
27795
|
+
// ── identity ────────────────────────────────────────────────────────────────
|
|
27796
|
+
getIdentity() {
|
|
27797
|
+
return { chainPubkey: new Uint8Array(this.deps.signingService.publicKey) };
|
|
27798
|
+
}
|
|
27799
|
+
/** Legacy DIRECT:// address (Path A). Async: the derivation hashes via the SDK. */
|
|
27800
|
+
deriveIdentityAddress(pubkey) {
|
|
27801
|
+
return deriveDirectAddress(pubkey ?? this.deps.signingService.publicKey);
|
|
27802
|
+
}
|
|
27803
|
+
// ── value (read) ─────────────────────────────────────────────────────────────
|
|
27804
|
+
readValue(token) {
|
|
27805
|
+
return token.value;
|
|
27806
|
+
}
|
|
27807
|
+
balanceOf(token, coinId) {
|
|
27808
|
+
let sum = 0n;
|
|
27809
|
+
for (const asset of token.value?.assets ?? []) {
|
|
27810
|
+
if (asset.coinId === coinId) sum += asset.amount;
|
|
27811
|
+
}
|
|
27812
|
+
return sum;
|
|
27813
|
+
}
|
|
27814
|
+
tokenId(token) {
|
|
27815
|
+
return token.blob.tokenId;
|
|
27816
|
+
}
|
|
27817
|
+
readMemo(token) {
|
|
27818
|
+
const sdkToken = token.sdkToken;
|
|
27819
|
+
if (sdkToken.transactions.length > 0) {
|
|
27820
|
+
return sdkToken.latestTransaction.data;
|
|
27821
|
+
}
|
|
27822
|
+
const data = sdkToken.genesis.data;
|
|
27823
|
+
if (data && this.isSpherePaymentData(data)) {
|
|
27824
|
+
return SpherePaymentData.fromCBOR(data).memo;
|
|
27825
|
+
}
|
|
27826
|
+
return null;
|
|
27827
|
+
}
|
|
27828
|
+
readTokenData(token) {
|
|
27829
|
+
const data = token.sdkToken.genesis.data;
|
|
27830
|
+
return data ? new Uint8Array(data) : null;
|
|
27831
|
+
}
|
|
27832
|
+
// ── lifecycle ────────────────────────────────────────────────────────────────
|
|
27833
|
+
async mint(params, options) {
|
|
27834
|
+
const recipient = SignaturePredicate.create(params.recipientPubkey);
|
|
27835
|
+
const data = params.value ? await SpherePaymentData.fromValue(params.value).encode() : null;
|
|
27836
|
+
const mintTx = await MintTransaction.create(this.deps.networkId, recipient, data);
|
|
27837
|
+
const certificationData = await CertificationData.fromMintTransaction(mintTx);
|
|
27838
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
27839
|
+
if (response.status !== CertificationStatus.SUCCESS) {
|
|
27840
|
+
throw new SphereError(`Mint certification failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
27841
|
+
}
|
|
27842
|
+
const proof = await waitInclusionProof2(
|
|
27843
|
+
this.deps.client,
|
|
27844
|
+
this.deps.trustBase,
|
|
27845
|
+
this.deps.predicateVerifier,
|
|
27846
|
+
mintTx,
|
|
27847
|
+
options?.signal
|
|
27848
|
+
);
|
|
27849
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
27850
|
+
const token = await Token2.mint(
|
|
27851
|
+
this.deps.trustBase,
|
|
27852
|
+
this.deps.predicateVerifier,
|
|
27853
|
+
this.deps.mintJustificationVerifier,
|
|
27854
|
+
certified
|
|
27855
|
+
);
|
|
27856
|
+
return this.wrapToken(token);
|
|
27857
|
+
}
|
|
27858
|
+
async mintDataToken(params, options) {
|
|
27859
|
+
const recipient = SignaturePredicate.create(params.recipientPubkey);
|
|
27860
|
+
const tokenType = params.tokenType ? new TokenType(params.tokenType) : TokenType.generate();
|
|
27861
|
+
const salt = params.salt ? TokenSalt.fromBytes(params.salt) : TokenSalt.generate();
|
|
27862
|
+
const mintTx = await MintTransaction.create(this.deps.networkId, recipient, params.data, tokenType, salt);
|
|
27863
|
+
const certificationData = await CertificationData.fromMintTransaction(mintTx);
|
|
27864
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
27865
|
+
if (response.status !== CertificationStatus.SUCCESS) {
|
|
27866
|
+
throw new SphereError(`Data-token mint failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
27867
|
+
}
|
|
27868
|
+
const proof = await waitInclusionProof2(
|
|
27869
|
+
this.deps.client,
|
|
27870
|
+
this.deps.trustBase,
|
|
27871
|
+
this.deps.predicateVerifier,
|
|
27872
|
+
mintTx,
|
|
27873
|
+
options?.signal
|
|
27874
|
+
);
|
|
27875
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
27876
|
+
const token = await Token2.mint(
|
|
27877
|
+
this.deps.trustBase,
|
|
27878
|
+
this.deps.predicateVerifier,
|
|
27879
|
+
this.deps.mintJustificationVerifier,
|
|
27880
|
+
certified
|
|
27881
|
+
);
|
|
27882
|
+
return this.wrapToken(token);
|
|
27883
|
+
}
|
|
27884
|
+
async transfer(params, options) {
|
|
27885
|
+
this.assertOwned(params.token);
|
|
27886
|
+
const recipient = SignaturePredicate.create(params.recipientPubkey);
|
|
27887
|
+
const stateMask = crypto.getRandomValues(new Uint8Array(32));
|
|
27888
|
+
const transferTx = await TransferTransaction.create(params.token.sdkToken, recipient, stateMask, params.data ?? null);
|
|
27889
|
+
const unlockScript = await SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);
|
|
27890
|
+
const certificationData = await CertificationData.fromTransaction(transferTx, unlockScript);
|
|
27891
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
27892
|
+
if (response.status !== CertificationStatus.SUCCESS) {
|
|
27893
|
+
throw new SphereError(`Transfer certification failed: ${response.status}`, "TRANSFER_FAILED");
|
|
27894
|
+
}
|
|
27895
|
+
const proof = await waitInclusionProof2(
|
|
27896
|
+
this.deps.client,
|
|
27897
|
+
this.deps.trustBase,
|
|
27898
|
+
this.deps.predicateVerifier,
|
|
27899
|
+
transferTx,
|
|
27900
|
+
options?.signal
|
|
27901
|
+
);
|
|
27902
|
+
const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
27903
|
+
const transferred = await params.token.sdkToken.transfer(this.deps.trustBase, this.deps.predicateVerifier, certified);
|
|
27904
|
+
return this.wrapToken(transferred);
|
|
27905
|
+
}
|
|
27906
|
+
async split(params, options) {
|
|
27907
|
+
this.assertOwned(params.token);
|
|
27908
|
+
if (params.outputs.length === 0) {
|
|
27909
|
+
throw new SphereError("Split requires at least one output", "VALIDATION_ERROR");
|
|
27910
|
+
}
|
|
27911
|
+
const requests = params.outputs.map(
|
|
27912
|
+
(o) => SplitTokenRequest.create(
|
|
27913
|
+
SignaturePredicate.create(o.recipientPubkey),
|
|
27914
|
+
PaymentAssetCollection.create(sphereAssetToSdk(o.coinId, o.amount))
|
|
27915
|
+
)
|
|
27916
|
+
);
|
|
27917
|
+
const split = await TokenSplit.split(params.token.sdkToken, decodeSpherePaymentData, requests);
|
|
27918
|
+
const burnUnlock = await SignaturePredicateUnlockScript.create(split.burn.transaction, this.deps.signingService);
|
|
27919
|
+
const burnCert = await CertificationData.fromTransaction(split.burn.transaction, burnUnlock);
|
|
27920
|
+
const burnResponse = await this.deps.client.submitCertificationRequest(burnCert);
|
|
27921
|
+
if (burnResponse.status !== CertificationStatus.SUCCESS) {
|
|
27922
|
+
throw new SphereError(`Split burn failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
27923
|
+
}
|
|
27924
|
+
const burnProof = await waitInclusionProof2(
|
|
27925
|
+
this.deps.client,
|
|
27926
|
+
this.deps.trustBase,
|
|
27927
|
+
this.deps.predicateVerifier,
|
|
27928
|
+
split.burn.transaction,
|
|
27929
|
+
options?.signal
|
|
27930
|
+
);
|
|
27931
|
+
const burnCertified = await split.burn.transaction.toCertifiedTransaction(
|
|
27932
|
+
this.deps.trustBase,
|
|
27933
|
+
this.deps.predicateVerifier,
|
|
27934
|
+
burnProof
|
|
27935
|
+
);
|
|
27936
|
+
const burntToken = await params.token.sdkToken.transfer(
|
|
27937
|
+
this.deps.trustBase,
|
|
27938
|
+
this.deps.predicateVerifier,
|
|
27939
|
+
burnCertified
|
|
27940
|
+
);
|
|
27941
|
+
const outputs = [];
|
|
27942
|
+
for (let i = 0; i < split.tokens.length; i++) {
|
|
27943
|
+
const splitToken = split.tokens[i];
|
|
27944
|
+
const data = await SpherePaymentData.create(splitToken.assets, params.outputs[i].data ?? null).encode();
|
|
27945
|
+
const justification = SplitMintJustification.create(burntToken, splitToken.proofs).toCBOR();
|
|
27946
|
+
const mintTx = await MintTransaction.create(
|
|
27947
|
+
splitToken.networkId,
|
|
27948
|
+
splitToken.recipient,
|
|
27949
|
+
data,
|
|
27950
|
+
splitToken.tokenType,
|
|
27951
|
+
splitToken.salt,
|
|
27952
|
+
justification
|
|
27953
|
+
);
|
|
27954
|
+
const certData = await CertificationData.fromMintTransaction(mintTx);
|
|
27955
|
+
const response = await this.deps.client.submitCertificationRequest(certData);
|
|
27956
|
+
if (response.status !== CertificationStatus.SUCCESS) {
|
|
27957
|
+
throw new SphereError(`Split mint failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
27958
|
+
}
|
|
27959
|
+
const proof = await waitInclusionProof2(
|
|
27960
|
+
this.deps.client,
|
|
27961
|
+
this.deps.trustBase,
|
|
27962
|
+
this.deps.predicateVerifier,
|
|
27963
|
+
mintTx,
|
|
27964
|
+
options?.signal
|
|
27965
|
+
);
|
|
27966
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
27967
|
+
const token = await Token2.mint(
|
|
27968
|
+
this.deps.trustBase,
|
|
27969
|
+
this.deps.predicateVerifier,
|
|
27970
|
+
this.deps.mintJustificationVerifier,
|
|
27971
|
+
certified
|
|
27972
|
+
);
|
|
27973
|
+
outputs.push(this.wrapToken(token));
|
|
27974
|
+
}
|
|
27975
|
+
return { outputs };
|
|
27976
|
+
}
|
|
27977
|
+
// ── verification ─────────────────────────────────────────────────────────────
|
|
27978
|
+
async verify(token, _options) {
|
|
27979
|
+
const result = await token.sdkToken.verify(
|
|
27980
|
+
this.deps.trustBase,
|
|
27981
|
+
this.deps.predicateVerifier,
|
|
27982
|
+
this.deps.mintJustificationVerifier
|
|
27983
|
+
);
|
|
27984
|
+
return result.status === VerificationStatus.OK ? { ok: true } : { ok: false, reason: String(result.status) };
|
|
27985
|
+
}
|
|
27986
|
+
async isSpent(token, _options) {
|
|
27987
|
+
const probe = await TransferTransaction.create(
|
|
27988
|
+
token.sdkToken,
|
|
27989
|
+
SignaturePredicate.create(this.deps.signingService.publicKey),
|
|
27990
|
+
new Uint8Array(32)
|
|
27991
|
+
);
|
|
27992
|
+
const stateId = await StateId.fromTransaction(probe);
|
|
27993
|
+
const response = await this.deps.client.getInclusionProof(stateId);
|
|
27994
|
+
return response.inclusionProof.inclusionCertificate !== null;
|
|
27995
|
+
}
|
|
27996
|
+
// ── serialization ────────────────────────────────────────────────────────────
|
|
27997
|
+
encodeToken(token) {
|
|
27998
|
+
return token.blob;
|
|
27999
|
+
}
|
|
28000
|
+
async decodeToken(blob) {
|
|
28001
|
+
const sdkToken = await Token2.fromCBOR(blob.token);
|
|
28002
|
+
if (sdkToken.genesis.networkId.id !== this.deps.networkId.id) {
|
|
28003
|
+
throw new SphereError(
|
|
28004
|
+
`Token network mismatch: token is on network ${sdkToken.genesis.networkId.id}, engine on ${this.deps.networkId.id}`,
|
|
28005
|
+
"VALIDATION_ERROR"
|
|
28006
|
+
);
|
|
28007
|
+
}
|
|
28008
|
+
return this.wrapToken(sdkToken);
|
|
28009
|
+
}
|
|
28010
|
+
// ── internals ────────────────────────────────────────────────────────────────
|
|
28011
|
+
/** Fail fast if this engine's key does not own the token's current state. */
|
|
28012
|
+
assertOwned(token) {
|
|
28013
|
+
const owner = token.sdkToken.latestTransaction.recipient;
|
|
28014
|
+
const mine = EncodedPredicate.fromPredicate(SignaturePredicate.create(this.deps.signingService.publicKey));
|
|
28015
|
+
if (!EncodedPredicate.equals(owner, mine)) {
|
|
28016
|
+
throw new SphereError("Cannot transfer a token not owned by this engine identity", "VALIDATION_ERROR");
|
|
28017
|
+
}
|
|
28018
|
+
}
|
|
28019
|
+
/** Wrap an SDK token into a SphereToken: cache its blob (incl. stable tokenId) + decoded value. */
|
|
28020
|
+
wrapToken(sdkToken) {
|
|
28021
|
+
const data = sdkToken.genesis.data;
|
|
28022
|
+
let value = null;
|
|
28023
|
+
if (data && this.isSpherePaymentData(data)) {
|
|
28024
|
+
try {
|
|
28025
|
+
value = SpherePaymentData.fromCBOR(data).toValue();
|
|
28026
|
+
} catch (err) {
|
|
28027
|
+
throw new SphereError(
|
|
28028
|
+
`Failed to decode token payment data: ${err instanceof Error ? err.message : String(err)}`,
|
|
28029
|
+
"VALIDATION_ERROR"
|
|
28030
|
+
);
|
|
28031
|
+
}
|
|
28032
|
+
}
|
|
28033
|
+
const blob = {
|
|
28034
|
+
v: TOKEN_BLOB_VERSION,
|
|
28035
|
+
network: sdkToken.genesis.networkId.id,
|
|
28036
|
+
tokenId: HexConverter.encode(sdkToken.id.bytes),
|
|
28037
|
+
token: sdkToken.toCBOR()
|
|
28038
|
+
};
|
|
28039
|
+
return { sdkToken, blob, value };
|
|
28040
|
+
}
|
|
28041
|
+
/** True if the bytes are a SpherePaymentData envelope (value token) vs a raw data token. */
|
|
28042
|
+
isSpherePaymentData(data) {
|
|
28043
|
+
try {
|
|
28044
|
+
return CborDeserializer.decodeTag(data).tag === SpherePaymentData.CBOR_TAG;
|
|
28045
|
+
} catch {
|
|
28046
|
+
return false;
|
|
28047
|
+
}
|
|
28048
|
+
}
|
|
28049
|
+
};
|
|
28050
|
+
|
|
28051
|
+
// token-engine/factory.ts
|
|
28052
|
+
async function createSphereTokenEngine(config) {
|
|
28053
|
+
if (config.trustBaseJson == null) {
|
|
28054
|
+
throw new SphereError("Engine config requires a trust base (trustBaseJson)", "INVALID_CONFIG");
|
|
28055
|
+
}
|
|
28056
|
+
const trustBase = RootTrustBase.fromJSON(config.trustBaseJson);
|
|
28057
|
+
const predicateVerifier = PredicateVerifierService.create();
|
|
28058
|
+
const mintJustificationVerifier = new MintJustificationVerifierService();
|
|
28059
|
+
mintJustificationVerifier.register(
|
|
28060
|
+
new SplitMintJustificationVerifier(trustBase, predicateVerifier, decodeSpherePaymentData)
|
|
28061
|
+
);
|
|
28062
|
+
const deps = {
|
|
28063
|
+
client: new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
|
|
28064
|
+
trustBase,
|
|
28065
|
+
predicateVerifier,
|
|
28066
|
+
mintJustificationVerifier,
|
|
28067
|
+
signingService: new SigningService(config.privateKey),
|
|
28068
|
+
// The trust base is the single source of truth for the network id (it carries
|
|
28069
|
+
// NetworkId.fromId, so any id works — e.g. testnet2 = 4 — with no enum entry).
|
|
28070
|
+
networkId: trustBase.networkId
|
|
28071
|
+
};
|
|
28072
|
+
return new SphereTokenEngine(deps);
|
|
28073
|
+
}
|
|
28074
|
+
|
|
27653
28075
|
// core/Sphere.ts
|
|
27654
|
-
import {
|
|
27655
|
-
import { TokenType as TokenType5 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
27656
|
-
import { HashAlgorithm as HashAlgorithm7 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
|
|
27657
|
-
import { UnmaskedPredicateReference as UnmaskedPredicateReference3 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference";
|
|
27658
|
-
import { normalizeNametag as normalizeNametag2, isPhoneNumber } from "@unicitylabs/nostr-js-sdk";
|
|
28076
|
+
import { normalizeNametag, isPhoneNumber } from "@unicitylabs/nostr-js-sdk";
|
|
27659
28077
|
function isValidNametag2(nametag) {
|
|
27660
28078
|
if (isPhoneNumber(nametag)) return true;
|
|
27661
28079
|
return /^[a-z0-9_-]{3,20}$/.test(nametag);
|
|
27662
28080
|
}
|
|
27663
|
-
var UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
27664
28081
|
async function deriveL3PredicateAddress(privateKey) {
|
|
27665
|
-
const
|
|
27666
|
-
|
|
27667
|
-
const tokenTypeBytes = Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex");
|
|
27668
|
-
const tokenType = new TokenType5(tokenTypeBytes);
|
|
27669
|
-
const predicateRef = UnmaskedPredicateReference3.create(
|
|
27670
|
-
tokenType,
|
|
27671
|
-
signingService.algorithm,
|
|
27672
|
-
signingService.publicKey,
|
|
27673
|
-
HashAlgorithm7.SHA256
|
|
27674
|
-
);
|
|
27675
|
-
return (await (await predicateRef).toAddress()).toString();
|
|
28082
|
+
const prehashedPublicKey = getPublicKey(sha2562(privateKey, "hex"));
|
|
28083
|
+
return deriveDirectAddress(hexToBytes2(prehashedPublicKey));
|
|
27676
28084
|
}
|
|
27677
28085
|
var Sphere = class _Sphere {
|
|
27678
28086
|
// Singleton
|
|
@@ -27694,14 +28102,14 @@ var Sphere = class _Sphere {
|
|
|
27694
28102
|
_addressIdToIndex = /* @__PURE__ */ new Map();
|
|
27695
28103
|
/** Nametag cache: addressId -> (nametagIndex -> nametag). Separate from tracked addresses. */
|
|
27696
28104
|
_addressNametags = /* @__PURE__ */ new Map();
|
|
27697
|
-
/** Cached PROXY address (computed once when nametag is set) */
|
|
27698
|
-
_cachedProxyAddress = void 0;
|
|
27699
28105
|
// Providers
|
|
27700
28106
|
_storage;
|
|
27701
28107
|
_tokenStorageProviders = /* @__PURE__ */ new Map();
|
|
27702
28108
|
_transport;
|
|
27703
28109
|
_oracle;
|
|
27704
28110
|
_priceProvider;
|
|
28111
|
+
/** v2 token engine (built per active address from the oracle); injected into modules. */
|
|
28112
|
+
_tokenEngine;
|
|
27705
28113
|
// Modules (single-instance — backward compat, delegates to active address)
|
|
27706
28114
|
_payments;
|
|
27707
28115
|
_communications;
|
|
@@ -28042,20 +28450,6 @@ var Sphere = class _Sphere {
|
|
|
28042
28450
|
await sphere.syncIdentityWithTransport();
|
|
28043
28451
|
sphere._initialized = true;
|
|
28044
28452
|
_Sphere.instance = sphere;
|
|
28045
|
-
if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {
|
|
28046
|
-
progress?.({ step: "registering_nametag", message: "Restoring nametag token..." });
|
|
28047
|
-
logger.debug("Sphere", `Unicity ID @${sphere._identity.nametag} has no token, attempting to mint...`);
|
|
28048
|
-
try {
|
|
28049
|
-
const result = await sphere.mintNametag(sphere._identity.nametag);
|
|
28050
|
-
if (result.success) {
|
|
28051
|
-
logger.debug("Sphere", `Nametag token minted successfully on load`);
|
|
28052
|
-
} else {
|
|
28053
|
-
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
28054
|
-
}
|
|
28055
|
-
} catch (err) {
|
|
28056
|
-
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
28057
|
-
}
|
|
28058
|
-
}
|
|
28059
28453
|
if (options.discoverAddresses !== false && sphere._transport.discoverAddresses && sphere._masterKey) {
|
|
28060
28454
|
progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
|
|
28061
28455
|
try {
|
|
@@ -29107,13 +29501,13 @@ var Sphere = class _Sphere {
|
|
|
29107
29501
|
oracle: this._oracle,
|
|
29108
29502
|
emitEvent: this.emitEvent.bind(this),
|
|
29109
29503
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
29110
|
-
price: this._priceProvider ?? void 0
|
|
29504
|
+
price: this._priceProvider ?? void 0,
|
|
29505
|
+
tokenEngine: moduleSet.tokenEngine
|
|
29111
29506
|
});
|
|
29112
29507
|
}
|
|
29113
29508
|
}
|
|
29114
29509
|
this._identity = newIdentity;
|
|
29115
29510
|
this._currentAddressIndex = index;
|
|
29116
|
-
await this._updateCachedProxyAddress();
|
|
29117
29511
|
const activeModules = this._addressModules.get(index);
|
|
29118
29512
|
this._payments = activeModules.payments;
|
|
29119
29513
|
this._communications = activeModules.communications;
|
|
@@ -29151,35 +29545,10 @@ var Sphere = class _Sphere {
|
|
|
29151
29545
|
}
|
|
29152
29546
|
if (newNametag) {
|
|
29153
29547
|
await this.persistAddressNametags();
|
|
29154
|
-
if (!this._payments.hasNametag()) {
|
|
29155
|
-
logger.debug("Sphere", `Minting nametag token for @${newNametag}...`);
|
|
29156
|
-
try {
|
|
29157
|
-
const result = await this.mintNametag(newNametag);
|
|
29158
|
-
if (result.success) {
|
|
29159
|
-
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
29160
|
-
} else {
|
|
29161
|
-
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
29162
|
-
}
|
|
29163
|
-
} catch (err) {
|
|
29164
|
-
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
29165
|
-
}
|
|
29166
|
-
}
|
|
29167
29548
|
this.emitEvent("nametag:registered", {
|
|
29168
29549
|
nametag: newNametag,
|
|
29169
29550
|
addressIndex: index
|
|
29170
29551
|
});
|
|
29171
|
-
} else if (this._identity?.nametag && !this._payments.hasNametag()) {
|
|
29172
|
-
logger.debug("Sphere", `Unicity ID @${this._identity.nametag} has no token after switch, minting...`);
|
|
29173
|
-
try {
|
|
29174
|
-
const result = await this.mintNametag(this._identity.nametag);
|
|
29175
|
-
if (result.success) {
|
|
29176
|
-
logger.debug("Sphere", `Nametag token minted successfully after switch`);
|
|
29177
|
-
} else {
|
|
29178
|
-
logger.warn("Sphere", `Could not mint nametag token after switch: ${result.error}`);
|
|
29179
|
-
}
|
|
29180
|
-
} catch (err) {
|
|
29181
|
-
logger.warn("Sphere", `Nametag token mint failed after switch:`, err);
|
|
29182
|
-
}
|
|
29183
29552
|
}
|
|
29184
29553
|
}
|
|
29185
29554
|
/**
|
|
@@ -29209,6 +29578,7 @@ var Sphere = class _Sphere {
|
|
|
29209
29578
|
const communications = createCommunicationsModule(this._communicationsConfig);
|
|
29210
29579
|
const groupChat = this._groupChatConfig ? createGroupChatModule(this._groupChatConfig) : null;
|
|
29211
29580
|
const market = this._marketConfig ? createMarketModule(this._marketConfig) : null;
|
|
29581
|
+
const tokenEngine = await this.buildTokenEngine(identity);
|
|
29212
29582
|
payments.initialize({
|
|
29213
29583
|
identity,
|
|
29214
29584
|
storage: this._storage,
|
|
@@ -29217,7 +29587,8 @@ var Sphere = class _Sphere {
|
|
|
29217
29587
|
oracle: this._oracle,
|
|
29218
29588
|
emitEvent,
|
|
29219
29589
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
29220
|
-
price: this._priceProvider ?? void 0
|
|
29590
|
+
price: this._priceProvider ?? void 0,
|
|
29591
|
+
tokenEngine
|
|
29221
29592
|
});
|
|
29222
29593
|
communications.initialize({
|
|
29223
29594
|
identity,
|
|
@@ -29253,7 +29624,8 @@ var Sphere = class _Sphere {
|
|
|
29253
29624
|
emitEvent,
|
|
29254
29625
|
on: this.on.bind(this),
|
|
29255
29626
|
storage: this._storage,
|
|
29256
|
-
communications
|
|
29627
|
+
communications,
|
|
29628
|
+
tokenEngine
|
|
29257
29629
|
});
|
|
29258
29630
|
} else {
|
|
29259
29631
|
logger.warn("Sphere", "Accounting module enabled but no token storage available \u2014 disabling");
|
|
@@ -29313,6 +29685,7 @@ var Sphere = class _Sphere {
|
|
|
29313
29685
|
market,
|
|
29314
29686
|
transportAdapter: adapter,
|
|
29315
29687
|
tokenStorageProviders: new Map(tokenStorageProviders),
|
|
29688
|
+
tokenEngine,
|
|
29316
29689
|
initialized: true
|
|
29317
29690
|
};
|
|
29318
29691
|
this._addressModules.set(index, moduleSet);
|
|
@@ -29868,15 +30241,6 @@ var Sphere = class _Sphere {
|
|
|
29868
30241
|
hasNametag() {
|
|
29869
30242
|
return !!this._identity?.nametag;
|
|
29870
30243
|
}
|
|
29871
|
-
/**
|
|
29872
|
-
* Get the PROXY address for the current nametag
|
|
29873
|
-
* PROXY addresses are derived from the nametag hash and require
|
|
29874
|
-
* the nametag token to claim funds sent to them
|
|
29875
|
-
* @returns PROXY address string or undefined if no nametag
|
|
29876
|
-
*/
|
|
29877
|
-
getProxyAddress() {
|
|
29878
|
-
return this._cachedProxyAddress;
|
|
29879
|
-
}
|
|
29880
30244
|
/**
|
|
29881
30245
|
* Resolve any identifier to full peer information.
|
|
29882
30246
|
* Accepts @nametag, bare nametag, DIRECT://, PROXY://, L1 address, or transport pubkey.
|
|
@@ -29911,17 +30275,6 @@ var Sphere = class _Sphere {
|
|
|
29911
30275
|
throw new SphereError(`Cannot resolve address: ${address.slice(0, 30)}`, "INVALID_RECIPIENT");
|
|
29912
30276
|
}
|
|
29913
30277
|
}
|
|
29914
|
-
/** Compute and cache the PROXY address from the current nametag */
|
|
29915
|
-
async _updateCachedProxyAddress() {
|
|
29916
|
-
const nametag = this._identity?.nametag;
|
|
29917
|
-
if (!nametag) {
|
|
29918
|
-
this._cachedProxyAddress = void 0;
|
|
29919
|
-
return;
|
|
29920
|
-
}
|
|
29921
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
29922
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
29923
|
-
this._cachedProxyAddress = proxyAddr.toString();
|
|
29924
|
-
}
|
|
29925
30278
|
/**
|
|
29926
30279
|
* Register a nametag for the current active address
|
|
29927
30280
|
* Each address can have its own independent nametag
|
|
@@ -29949,17 +30302,6 @@ var Sphere = class _Sphere {
|
|
|
29949
30302
|
if (this._identity?.nametag) {
|
|
29950
30303
|
throw new SphereError(`Unicity ID already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`, "ALREADY_INITIALIZED");
|
|
29951
30304
|
}
|
|
29952
|
-
if (!this._payments.hasNametag()) {
|
|
29953
|
-
logger.debug("Sphere", `Minting nametag token for @${cleanNametag}...`);
|
|
29954
|
-
const result = await this.mintNametag(cleanNametag);
|
|
29955
|
-
if (!result.success) {
|
|
29956
|
-
throw new SphereError(
|
|
29957
|
-
`Failed to mint nametag token: ${result.error}`,
|
|
29958
|
-
"AGGREGATOR_ERROR"
|
|
29959
|
-
);
|
|
29960
|
-
}
|
|
29961
|
-
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
29962
|
-
}
|
|
29963
30305
|
if (this._transport.publishIdentityBinding) {
|
|
29964
30306
|
const success = await this._transport.publishIdentityBinding(
|
|
29965
30307
|
this._identity.chainPubkey,
|
|
@@ -29972,7 +30314,6 @@ var Sphere = class _Sphere {
|
|
|
29972
30314
|
}
|
|
29973
30315
|
}
|
|
29974
30316
|
this._identity.nametag = cleanNametag;
|
|
29975
|
-
await this._updateCachedProxyAddress();
|
|
29976
30317
|
const currentAddressId = this._trackedAddresses.get(this._currentAddressIndex)?.addressId;
|
|
29977
30318
|
if (currentAddressId) {
|
|
29978
30319
|
let nametags = this._addressNametags.get(currentAddressId);
|
|
@@ -30005,35 +30346,19 @@ var Sphere = class _Sphere {
|
|
|
30005
30346
|
await this._storage.saveTrackedAddresses(entries);
|
|
30006
30347
|
}
|
|
30007
30348
|
/**
|
|
30008
|
-
*
|
|
30009
|
-
* This creates the nametag token required for receiving tokens via PROXY addresses (@nametag)
|
|
30349
|
+
* Check whether a nametag is available to register.
|
|
30010
30350
|
*
|
|
30011
|
-
*
|
|
30012
|
-
*
|
|
30351
|
+
* D5: nametags are Nostr bindings (name ↔ chainPubkey), not on-chain tokens. Availability is
|
|
30352
|
+
* first-seen-wins — a name is available iff no binding resolves for it.
|
|
30013
30353
|
*
|
|
30014
|
-
* @example
|
|
30015
|
-
* ```typescript
|
|
30016
|
-
* // Mint nametag token for receiving via @alice
|
|
30017
|
-
* const result = await sphere.mintNametag('alice');
|
|
30018
|
-
* if (result.success) {
|
|
30019
|
-
* console.log('Nametag minted:', result.nametagData?.name);
|
|
30020
|
-
* } else {
|
|
30021
|
-
* console.error('Mint failed:', result.error);
|
|
30022
|
-
* }
|
|
30023
|
-
* ```
|
|
30024
|
-
*/
|
|
30025
|
-
async mintNametag(nametag) {
|
|
30026
|
-
this.ensureReady();
|
|
30027
|
-
return this._payments.mintNametag(nametag);
|
|
30028
|
-
}
|
|
30029
|
-
/**
|
|
30030
|
-
* Check if a nametag is available for minting
|
|
30031
30354
|
* @param nametag - The nametag to check (e.g., "alice" or "@alice")
|
|
30032
|
-
* @returns true if available, false if taken
|
|
30355
|
+
* @returns true if available, false if already taken
|
|
30033
30356
|
*/
|
|
30034
30357
|
async isNametagAvailable(nametag) {
|
|
30035
30358
|
this.ensureReady();
|
|
30036
|
-
|
|
30359
|
+
if (!this._transport.resolveNametag) return true;
|
|
30360
|
+
const bound = await this._transport.resolveNametag(this.cleanNametag(nametag));
|
|
30361
|
+
return bound == null;
|
|
30037
30362
|
}
|
|
30038
30363
|
/**
|
|
30039
30364
|
* Load tracked addresses from storage.
|
|
@@ -30208,7 +30533,6 @@ var Sphere = class _Sphere {
|
|
|
30208
30533
|
}
|
|
30209
30534
|
if (recoveredNametag && !this._identity?.nametag) {
|
|
30210
30535
|
this._identity.nametag = recoveredNametag;
|
|
30211
|
-
await this._updateCachedProxyAddress();
|
|
30212
30536
|
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
30213
30537
|
let nametags = this._addressNametags.get(entry.addressId);
|
|
30214
30538
|
if (!nametags) {
|
|
@@ -30297,7 +30621,6 @@ var Sphere = class _Sphere {
|
|
|
30297
30621
|
try {
|
|
30298
30622
|
if (this._identity) {
|
|
30299
30623
|
this._identity.nametag = recoveredNametag;
|
|
30300
|
-
await this._updateCachedProxyAddress();
|
|
30301
30624
|
}
|
|
30302
30625
|
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
30303
30626
|
let nametags = this._addressNametags.get(entry.addressId);
|
|
@@ -30317,7 +30640,7 @@ var Sphere = class _Sphere {
|
|
|
30317
30640
|
*/
|
|
30318
30641
|
cleanNametag(raw) {
|
|
30319
30642
|
const stripped = raw.startsWith("@") ? raw.slice(1) : raw;
|
|
30320
|
-
return
|
|
30643
|
+
return normalizeNametag(stripped);
|
|
30321
30644
|
}
|
|
30322
30645
|
// ===========================================================================
|
|
30323
30646
|
// Public Methods - Lifecycle
|
|
@@ -30497,7 +30820,6 @@ var Sphere = class _Sphere {
|
|
|
30497
30820
|
} else if (this._identity && nametag) {
|
|
30498
30821
|
this._identity.nametag = nametag;
|
|
30499
30822
|
}
|
|
30500
|
-
await this._updateCachedProxyAddress();
|
|
30501
30823
|
}
|
|
30502
30824
|
async initializeIdentityFromMnemonic(mnemonic, derivationPath) {
|
|
30503
30825
|
const basePath = derivationPath ?? DEFAULT_BASE_PATH;
|
|
@@ -30648,10 +30970,45 @@ var Sphere = class _Sphere {
|
|
|
30648
30970
|
this._providerEventCleanups = [];
|
|
30649
30971
|
this._lastProviderConnected.clear();
|
|
30650
30972
|
}
|
|
30973
|
+
/**
|
|
30974
|
+
* Construct the v2 token engine for a given address identity (defaults to the
|
|
30975
|
+
* active one) from the oracle's gateway URL + trust base and that address's
|
|
30976
|
+
* signing key. The engine is per-address — each address signs with its own key.
|
|
30977
|
+
* The trust base is the single source of truth for the network id (so any id
|
|
30978
|
+
* works — e.g. testnet2 = 4 — with no enum entry). Returns undefined (modules
|
|
30979
|
+
* keep their legacy path) when the oracle can't supply a trust base / url, or
|
|
30980
|
+
* construction fails — a misconfigured oracle never breaks initialization.
|
|
30981
|
+
*/
|
|
30982
|
+
async buildTokenEngine(identity) {
|
|
30983
|
+
const oracle = this._oracle;
|
|
30984
|
+
const privateKey = (identity ?? this._identity)?.privateKey;
|
|
30985
|
+
const trustBaseJson = oracle.getTrustBaseJson?.() ?? null;
|
|
30986
|
+
const aggregatorUrl = oracle.getAggregatorUrl?.();
|
|
30987
|
+
if (!trustBaseJson || !aggregatorUrl || !privateKey) {
|
|
30988
|
+
logger.warn("Sphere", "v2 token engine not constructed (oracle has no trust base / url, or no identity) \u2014 legacy path");
|
|
30989
|
+
return void 0;
|
|
30990
|
+
}
|
|
30991
|
+
try {
|
|
30992
|
+
return await createSphereTokenEngine({
|
|
30993
|
+
aggregatorUrl,
|
|
30994
|
+
apiKey: oracle.getApiKey?.(),
|
|
30995
|
+
privateKey: hexToBytes2(privateKey),
|
|
30996
|
+
trustBaseJson
|
|
30997
|
+
});
|
|
30998
|
+
} catch (err) {
|
|
30999
|
+
logger.warn(
|
|
31000
|
+
"Sphere",
|
|
31001
|
+
`Failed to construct v2 token engine \u2014 modules use the legacy path: ${err instanceof Error ? err.message : String(err)}`
|
|
31002
|
+
);
|
|
31003
|
+
return void 0;
|
|
31004
|
+
}
|
|
31005
|
+
}
|
|
30651
31006
|
async initializeModules() {
|
|
30652
31007
|
const emitEvent = this.emitEvent.bind(this);
|
|
30653
31008
|
const adapter = await this.ensureTransportMux(this._currentAddressIndex, this._identity);
|
|
30654
31009
|
const moduleTransport = adapter ?? this._transport;
|
|
31010
|
+
this._tokenEngine = await this.buildTokenEngine();
|
|
31011
|
+
const tokenEngine = this._tokenEngine;
|
|
30655
31012
|
this._payments.initialize({
|
|
30656
31013
|
identity: this._identity,
|
|
30657
31014
|
storage: this._storage,
|
|
@@ -30662,7 +31019,8 @@ var Sphere = class _Sphere {
|
|
|
30662
31019
|
// Pass chain code for L1 HD derivation
|
|
30663
31020
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
30664
31021
|
price: this._priceProvider ?? void 0,
|
|
30665
|
-
disabledProviderIds: this._disabledProviders
|
|
31022
|
+
disabledProviderIds: this._disabledProviders,
|
|
31023
|
+
tokenEngine
|
|
30666
31024
|
});
|
|
30667
31025
|
this._communications.initialize({
|
|
30668
31026
|
identity: this._identity,
|
|
@@ -30698,7 +31056,8 @@ var Sphere = class _Sphere {
|
|
|
30698
31056
|
emitEvent,
|
|
30699
31057
|
on: this.on.bind(this),
|
|
30700
31058
|
storage: this._storage,
|
|
30701
|
-
communications: this._communications
|
|
31059
|
+
communications: this._communications,
|
|
31060
|
+
tokenEngine
|
|
30702
31061
|
});
|
|
30703
31062
|
} else {
|
|
30704
31063
|
logger.warn("Sphere", "Accounting module enabled but no token storage available \u2014 disabling");
|
|
@@ -30760,6 +31119,7 @@ var Sphere = class _Sphere {
|
|
|
30760
31119
|
market: this._market,
|
|
30761
31120
|
transportAdapter: adapter,
|
|
30762
31121
|
tokenStorageProviders: new Map(this._tokenStorageProviders),
|
|
31122
|
+
tokenEngine: this._tokenEngine,
|
|
30763
31123
|
initialized: true
|
|
30764
31124
|
});
|
|
30765
31125
|
}
|
|
@@ -31110,37 +31470,22 @@ init_constants();
|
|
|
31110
31470
|
|
|
31111
31471
|
// validation/token-validator.ts
|
|
31112
31472
|
init_logger();
|
|
31473
|
+
function stateIdOf(token) {
|
|
31474
|
+
return sha2562(bytesToHex3(token.blob.token), "hex");
|
|
31475
|
+
}
|
|
31113
31476
|
var TokenValidator = class {
|
|
31114
|
-
|
|
31115
|
-
|
|
31116
|
-
skipVerification;
|
|
31117
|
-
// Cache for spent state verification
|
|
31477
|
+
engine;
|
|
31478
|
+
// Spent-status cache: SPENT is permanent (immutable), UNSPENT expires after a TTL.
|
|
31118
31479
|
spentStateCache = /* @__PURE__ */ new Map();
|
|
31119
31480
|
UNSPENT_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
31120
31481
|
// 5 minutes
|
|
31121
|
-
constructor(
|
|
31122
|
-
this.
|
|
31123
|
-
this.trustBase = options.trustBase || null;
|
|
31124
|
-
this.skipVerification = options.skipVerification || false;
|
|
31482
|
+
constructor(engine) {
|
|
31483
|
+
this.engine = engine;
|
|
31125
31484
|
}
|
|
31126
|
-
|
|
31127
|
-
* Set the aggregator client
|
|
31128
|
-
*/
|
|
31129
|
-
setAggregatorClient(client) {
|
|
31130
|
-
this.aggregatorClient = client;
|
|
31131
|
-
}
|
|
31132
|
-
/**
|
|
31133
|
-
* Set the trust base
|
|
31134
|
-
*/
|
|
31135
|
-
setTrustBase(trustBase) {
|
|
31136
|
-
this.trustBase = trustBase;
|
|
31137
|
-
}
|
|
31138
|
-
// =============================================================================
|
|
31485
|
+
// ===========================================================================
|
|
31139
31486
|
// Public API
|
|
31140
|
-
//
|
|
31141
|
-
/**
|
|
31142
|
-
* Validate all tokens (parallel with batch limit)
|
|
31143
|
-
*/
|
|
31487
|
+
// ===========================================================================
|
|
31488
|
+
/** Validate all tokens (parallel, with a batch limit). */
|
|
31144
31489
|
async validateAllTokens(tokens, options) {
|
|
31145
31490
|
const validTokens = [];
|
|
31146
31491
|
const issues = [];
|
|
@@ -31149,293 +31494,88 @@ var TokenValidator = class {
|
|
|
31149
31494
|
let completed = 0;
|
|
31150
31495
|
for (let i = 0; i < tokens.length; i += batchSize) {
|
|
31151
31496
|
const batch = tokens.slice(i, i + batchSize);
|
|
31152
|
-
const batchResults = await Promise.
|
|
31153
|
-
batch.map(async (token) => {
|
|
31154
|
-
try {
|
|
31155
|
-
const result = await this.validateToken(token);
|
|
31156
|
-
return { token, result };
|
|
31157
|
-
} catch (err) {
|
|
31158
|
-
return {
|
|
31159
|
-
token,
|
|
31160
|
-
result: {
|
|
31161
|
-
isValid: false,
|
|
31162
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
31163
|
-
}
|
|
31164
|
-
};
|
|
31165
|
-
}
|
|
31166
|
-
})
|
|
31497
|
+
const batchResults = await Promise.all(
|
|
31498
|
+
batch.map(async (token) => ({ token, result: await this.validateToken(token) }))
|
|
31167
31499
|
);
|
|
31168
|
-
for (const
|
|
31500
|
+
for (const { token, result } of batchResults) {
|
|
31169
31501
|
completed++;
|
|
31170
|
-
if (
|
|
31171
|
-
|
|
31172
|
-
if (result.isValid) {
|
|
31173
|
-
validTokens.push(token);
|
|
31174
|
-
} else {
|
|
31175
|
-
issues.push({
|
|
31176
|
-
tokenId: token.id,
|
|
31177
|
-
reason: result.reason || "Unknown validation error",
|
|
31178
|
-
recoverable: false
|
|
31179
|
-
});
|
|
31180
|
-
}
|
|
31502
|
+
if (result.isValid) {
|
|
31503
|
+
validTokens.push(token);
|
|
31181
31504
|
} else {
|
|
31182
31505
|
issues.push({
|
|
31183
|
-
tokenId:
|
|
31184
|
-
reason:
|
|
31506
|
+
tokenId: stateIdOf(token),
|
|
31507
|
+
reason: result.reason || "Unknown validation error",
|
|
31185
31508
|
recoverable: false
|
|
31186
31509
|
});
|
|
31187
31510
|
}
|
|
31188
31511
|
}
|
|
31189
|
-
|
|
31190
|
-
options.onProgress(completed, total);
|
|
31191
|
-
}
|
|
31512
|
+
options?.onProgress?.(completed, total);
|
|
31192
31513
|
}
|
|
31193
31514
|
return { validTokens, issues };
|
|
31194
31515
|
}
|
|
31195
|
-
/**
|
|
31196
|
-
* Validate a single token
|
|
31197
|
-
*/
|
|
31516
|
+
/** Validate a single token's structural integrity against the trust base. */
|
|
31198
31517
|
async validateToken(token) {
|
|
31199
|
-
|
|
31200
|
-
|
|
31201
|
-
isValid: false,
|
|
31202
|
-
reason: "Token has no SDK data"
|
|
31203
|
-
};
|
|
31204
|
-
}
|
|
31205
|
-
let txfToken;
|
|
31206
|
-
try {
|
|
31207
|
-
txfToken = JSON.parse(token.sdkData);
|
|
31208
|
-
} catch {
|
|
31209
|
-
return {
|
|
31210
|
-
isValid: false,
|
|
31211
|
-
reason: "Failed to parse token SDK data as JSON"
|
|
31212
|
-
};
|
|
31213
|
-
}
|
|
31214
|
-
if (!this.hasValidTxfStructure(txfToken)) {
|
|
31215
|
-
return {
|
|
31216
|
-
isValid: false,
|
|
31217
|
-
reason: "Token data missing required TXF fields (genesis, state)"
|
|
31218
|
-
};
|
|
31219
|
-
}
|
|
31220
|
-
const uncommitted = this.getUncommittedTransactions(txfToken);
|
|
31221
|
-
if (uncommitted.length > 0) {
|
|
31222
|
-
return {
|
|
31223
|
-
isValid: false,
|
|
31224
|
-
reason: `${uncommitted.length} uncommitted transaction(s)`
|
|
31225
|
-
};
|
|
31226
|
-
}
|
|
31227
|
-
if (this.trustBase && !this.skipVerification) {
|
|
31228
|
-
try {
|
|
31229
|
-
const verificationResult = await this.verifyWithSdk(txfToken);
|
|
31230
|
-
if (!verificationResult.success) {
|
|
31231
|
-
return {
|
|
31232
|
-
isValid: false,
|
|
31233
|
-
reason: verificationResult.error || "SDK verification failed"
|
|
31234
|
-
};
|
|
31235
|
-
}
|
|
31236
|
-
} catch (err) {
|
|
31237
|
-
logger.warn("Validation", "SDK verification skipped:", err instanceof Error ? err.message : err);
|
|
31238
|
-
}
|
|
31239
|
-
}
|
|
31240
|
-
return { isValid: true };
|
|
31518
|
+
const result = await this.engine.verify(token);
|
|
31519
|
+
return result.ok ? { isValid: true } : { isValid: false, reason: result.reason || "Verification failed" };
|
|
31241
31520
|
}
|
|
31242
31521
|
/**
|
|
31243
|
-
*
|
|
31522
|
+
* Whether a token's current state has been spent on the network. Cached:
|
|
31523
|
+
* SPENT permanently, UNSPENT for `UNSPENT_CACHE_TTL_MS`. Graceful — an engine
|
|
31524
|
+
* error is treated as unspent (and not cached).
|
|
31244
31525
|
*/
|
|
31245
|
-
async
|
|
31246
|
-
|
|
31247
|
-
|
|
31248
|
-
|
|
31249
|
-
|
|
31250
|
-
|
|
31251
|
-
if (cached !== void 0) {
|
|
31252
|
-
if (cached.isSpent) {
|
|
31253
|
-
return true;
|
|
31254
|
-
}
|
|
31255
|
-
if (Date.now() - cached.timestamp < this.UNSPENT_CACHE_TTL_MS) {
|
|
31256
|
-
return false;
|
|
31257
|
-
}
|
|
31526
|
+
async isSpent(token) {
|
|
31527
|
+
const key = stateIdOf(token);
|
|
31528
|
+
const cached = this.spentStateCache.get(key);
|
|
31529
|
+
if (cached) {
|
|
31530
|
+
if (cached.isSpent) return true;
|
|
31531
|
+
if (Date.now() - cached.timestamp < this.UNSPENT_CACHE_TTL_MS) return false;
|
|
31258
31532
|
}
|
|
31533
|
+
let spent;
|
|
31259
31534
|
try {
|
|
31260
|
-
|
|
31261
|
-
const { DataHash } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHash");
|
|
31262
|
-
const pubKeyBytes = Buffer.from(publicKey, "hex");
|
|
31263
|
-
const stateHashObj = DataHash.fromJSON(stateHash);
|
|
31264
|
-
const requestId2 = await RequestId.create(pubKeyBytes, stateHashObj);
|
|
31265
|
-
const response = await this.aggregatorClient.getInclusionProof(requestId2);
|
|
31266
|
-
let isSpent = false;
|
|
31267
|
-
if (response.inclusionProof) {
|
|
31268
|
-
const proof = response.inclusionProof;
|
|
31269
|
-
const pathResult = await proof.merkleTreePath.verify(
|
|
31270
|
-
requestId2.toBitString().toBigInt()
|
|
31271
|
-
);
|
|
31272
|
-
if (pathResult.isPathValid && pathResult.isPathIncluded && proof.authenticator !== null) {
|
|
31273
|
-
isSpent = true;
|
|
31274
|
-
}
|
|
31275
|
-
}
|
|
31276
|
-
this.spentStateCache.set(cacheKey, {
|
|
31277
|
-
isSpent,
|
|
31278
|
-
timestamp: Date.now()
|
|
31279
|
-
});
|
|
31280
|
-
return isSpent;
|
|
31535
|
+
spent = await this.engine.isSpent(token);
|
|
31281
31536
|
} catch (err) {
|
|
31282
|
-
logger.warn("Validation", "Error checking
|
|
31537
|
+
logger.warn("Validation", "Error checking spent status:", err);
|
|
31283
31538
|
return false;
|
|
31284
31539
|
}
|
|
31540
|
+
this.spentStateCache.set(key, { isSpent: spent, timestamp: Date.now() });
|
|
31541
|
+
return spent;
|
|
31285
31542
|
}
|
|
31286
|
-
/**
|
|
31287
|
-
|
|
31288
|
-
*
|
|
31289
|
-
* Follows the same approach as the Sphere webgui TokenValidationService:
|
|
31290
|
-
* 1. Parse TXF using SDK's Token.fromJSON()
|
|
31291
|
-
* 2. Calculate CURRENT state hash via sdkToken.state.calculateHash()
|
|
31292
|
-
* 3. Create RequestId via RequestId.create(walletPubKey, calculatedHash)
|
|
31293
|
-
*
|
|
31294
|
-
* Uses wallet's own pubkey (not source state predicate key) because "spent" means
|
|
31295
|
-
* the CURRENT OWNER committed this state. Using the source state key would falsely
|
|
31296
|
-
* detect received tokens as "spent" (sender's commitment matches source state).
|
|
31297
|
-
*/
|
|
31298
|
-
async checkSpentTokens(tokens, publicKey, options) {
|
|
31543
|
+
/** Check which of the given tokens are spent, returning them by per-state id. */
|
|
31544
|
+
async checkSpentTokens(tokens, options) {
|
|
31299
31545
|
const spentTokens = [];
|
|
31300
31546
|
const errors = [];
|
|
31301
|
-
if (!this.aggregatorClient) {
|
|
31302
|
-
errors.push("Aggregator client not available");
|
|
31303
|
-
return { spentTokens, errors };
|
|
31304
|
-
}
|
|
31305
31547
|
const batchSize = options?.batchSize ?? 3;
|
|
31306
31548
|
const total = tokens.length;
|
|
31307
31549
|
let completed = 0;
|
|
31308
|
-
const { Token: SdkToken5 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
31309
|
-
const { RequestId } = await import("@unicitylabs/state-transition-sdk/lib/api/RequestId");
|
|
31310
|
-
const pubKeyBytes = Buffer.from(publicKey, "hex");
|
|
31311
31550
|
for (let i = 0; i < tokens.length; i += batchSize) {
|
|
31312
31551
|
const batch = tokens.slice(i, i + batchSize);
|
|
31313
31552
|
const batchResults = await Promise.allSettled(
|
|
31314
|
-
batch.map(async (token) => {
|
|
31315
|
-
try {
|
|
31316
|
-
const txf = tokenToTxf(token);
|
|
31317
|
-
if (!txf) {
|
|
31318
|
-
return { tokenId: token.id, localId: token.id, stateHash: "", spent: false, error: "Invalid TXF" };
|
|
31319
|
-
}
|
|
31320
|
-
const tokenId = txf.genesis?.data?.tokenId || token.id;
|
|
31321
|
-
const sdkToken = await SdkToken5.fromJSON(txf);
|
|
31322
|
-
const calculatedStateHash = await sdkToken.state.calculateHash();
|
|
31323
|
-
const calculatedStateHashStr = calculatedStateHash.toJSON();
|
|
31324
|
-
const cacheKey = `${tokenId}:${calculatedStateHashStr}:${publicKey}`;
|
|
31325
|
-
const cached = this.spentStateCache.get(cacheKey);
|
|
31326
|
-
if (cached !== void 0) {
|
|
31327
|
-
if (cached.isSpent) {
|
|
31328
|
-
return { tokenId, localId: token.id, stateHash: calculatedStateHashStr, spent: true };
|
|
31329
|
-
}
|
|
31330
|
-
if (Date.now() - cached.timestamp < this.UNSPENT_CACHE_TTL_MS) {
|
|
31331
|
-
return { tokenId, localId: token.id, stateHash: calculatedStateHashStr, spent: false };
|
|
31332
|
-
}
|
|
31333
|
-
}
|
|
31334
|
-
const { DataHash } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHash");
|
|
31335
|
-
const stateHashObj = DataHash.fromJSON(calculatedStateHashStr);
|
|
31336
|
-
const requestId2 = await RequestId.create(pubKeyBytes, stateHashObj);
|
|
31337
|
-
const response = await this.aggregatorClient.getInclusionProof(requestId2);
|
|
31338
|
-
let isSpent = false;
|
|
31339
|
-
if (response.inclusionProof) {
|
|
31340
|
-
const proof = response.inclusionProof;
|
|
31341
|
-
const pathResult = await proof.merkleTreePath.verify(
|
|
31342
|
-
requestId2.toBitString().toBigInt()
|
|
31343
|
-
);
|
|
31344
|
-
if (pathResult.isPathValid && pathResult.isPathIncluded && proof.authenticator !== null) {
|
|
31345
|
-
isSpent = true;
|
|
31346
|
-
}
|
|
31347
|
-
}
|
|
31348
|
-
this.spentStateCache.set(cacheKey, {
|
|
31349
|
-
isSpent,
|
|
31350
|
-
timestamp: Date.now()
|
|
31351
|
-
});
|
|
31352
|
-
return { tokenId, localId: token.id, stateHash: calculatedStateHashStr, spent: isSpent };
|
|
31353
|
-
} catch (err) {
|
|
31354
|
-
return {
|
|
31355
|
-
tokenId: token.id,
|
|
31356
|
-
localId: token.id,
|
|
31357
|
-
stateHash: "",
|
|
31358
|
-
spent: false,
|
|
31359
|
-
error: err instanceof Error ? err.message : String(err)
|
|
31360
|
-
};
|
|
31361
|
-
}
|
|
31362
|
-
})
|
|
31553
|
+
batch.map(async (token) => ({ stateId: stateIdOf(token), spent: await this.isSpent(token) }))
|
|
31363
31554
|
);
|
|
31364
31555
|
for (const result of batchResults) {
|
|
31365
31556
|
completed++;
|
|
31366
31557
|
if (result.status === "fulfilled") {
|
|
31367
|
-
if (result.value.spent) {
|
|
31368
|
-
spentTokens.push({
|
|
31369
|
-
tokenId: result.value.tokenId,
|
|
31370
|
-
localId: result.value.localId,
|
|
31371
|
-
stateHash: result.value.stateHash
|
|
31372
|
-
});
|
|
31373
|
-
}
|
|
31374
|
-
if (result.value.error) {
|
|
31375
|
-
errors.push(`Token ${result.value.tokenId}: ${result.value.error}`);
|
|
31376
|
-
}
|
|
31558
|
+
if (result.value.spent) spentTokens.push({ stateId: result.value.stateId });
|
|
31377
31559
|
} else {
|
|
31378
31560
|
errors.push(String(result.reason));
|
|
31379
31561
|
}
|
|
31380
31562
|
}
|
|
31381
|
-
|
|
31382
|
-
options.onProgress(completed, total);
|
|
31383
|
-
}
|
|
31563
|
+
options?.onProgress?.(completed, total);
|
|
31384
31564
|
}
|
|
31385
31565
|
return { spentTokens, errors };
|
|
31386
31566
|
}
|
|
31387
|
-
/**
|
|
31388
|
-
* Clear the spent state cache
|
|
31389
|
-
*/
|
|
31567
|
+
/** Clear the spent-status cache. */
|
|
31390
31568
|
clearSpentStateCache() {
|
|
31391
31569
|
this.spentStateCache.clear();
|
|
31392
31570
|
}
|
|
31393
|
-
// =============================================================================
|
|
31394
|
-
// Private Helpers
|
|
31395
|
-
// =============================================================================
|
|
31396
|
-
hasValidTxfStructure(obj) {
|
|
31397
|
-
if (!obj || typeof obj !== "object") return false;
|
|
31398
|
-
const txf = obj;
|
|
31399
|
-
return !!(txf.genesis && typeof txf.genesis === "object" && txf.state && typeof txf.state === "object");
|
|
31400
|
-
}
|
|
31401
|
-
getUncommittedTransactions(txfToken) {
|
|
31402
|
-
const txf = txfToken;
|
|
31403
|
-
const transactions = txf.transactions;
|
|
31404
|
-
if (!transactions || !Array.isArray(transactions)) {
|
|
31405
|
-
return [];
|
|
31406
|
-
}
|
|
31407
|
-
return transactions.filter((tx) => tx.inclusionProof === null);
|
|
31408
|
-
}
|
|
31409
|
-
async verifyWithSdk(txfToken) {
|
|
31410
|
-
try {
|
|
31411
|
-
const { Token: Token5 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
31412
|
-
const sdkToken = await Token5.fromJSON(txfToken);
|
|
31413
|
-
if (!this.trustBase) {
|
|
31414
|
-
return { success: true };
|
|
31415
|
-
}
|
|
31416
|
-
const result = await sdkToken.verify(this.trustBase);
|
|
31417
|
-
if (!result.isSuccessful) {
|
|
31418
|
-
return {
|
|
31419
|
-
success: false,
|
|
31420
|
-
error: String(result) || "Verification failed"
|
|
31421
|
-
};
|
|
31422
|
-
}
|
|
31423
|
-
return { success: true };
|
|
31424
|
-
} catch (err) {
|
|
31425
|
-
return {
|
|
31426
|
-
success: false,
|
|
31427
|
-
error: err instanceof Error ? err.message : String(err)
|
|
31428
|
-
};
|
|
31429
|
-
}
|
|
31430
|
-
}
|
|
31431
31571
|
};
|
|
31432
|
-
function createTokenValidator(
|
|
31433
|
-
return new TokenValidator(
|
|
31572
|
+
function createTokenValidator(engine) {
|
|
31573
|
+
return new TokenValidator(engine);
|
|
31434
31574
|
}
|
|
31435
31575
|
|
|
31436
31576
|
// index.ts
|
|
31437
31577
|
import {
|
|
31438
|
-
normalizeNametag as
|
|
31578
|
+
normalizeNametag as normalizeNametag2,
|
|
31439
31579
|
isPhoneNumber as isPhoneNumber2,
|
|
31440
31580
|
hashNametag,
|
|
31441
31581
|
hashAddressForTag,
|
|
@@ -31841,7 +31981,7 @@ export {
|
|
|
31841
31981
|
mnemonicToSeedSync2 as mnemonicToSeedSync,
|
|
31842
31982
|
normalizeAddress,
|
|
31843
31983
|
normalizeCoinId,
|
|
31844
|
-
|
|
31984
|
+
normalizeNametag2 as normalizeNametag,
|
|
31845
31985
|
normalizeSdkTokenToStorage,
|
|
31846
31986
|
objectToTxf,
|
|
31847
31987
|
parseAddress,
|