@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.cjs
CHANGED
|
@@ -917,8 +917,8 @@ __export(index_exports, {
|
|
|
917
917
|
NETWORKS: () => NETWORKS,
|
|
918
918
|
NIP29_KINDS: () => NIP29_KINDS,
|
|
919
919
|
NOSTR_EVENT_KINDS: () => NOSTR_EVENT_KINDS,
|
|
920
|
-
NostrClient: () =>
|
|
921
|
-
NostrKeyManager: () =>
|
|
920
|
+
NostrClient: () => import_nostr_js_sdk6.NostrClient,
|
|
921
|
+
NostrKeyManager: () => import_nostr_js_sdk6.NostrKeyManager,
|
|
922
922
|
PaymentsModule: () => PaymentsModule,
|
|
923
923
|
SIGN_MESSAGE_PREFIX: () => SIGN_MESSAGE_PREFIX,
|
|
924
924
|
STORAGE_KEYS: () => STORAGE_KEYS,
|
|
@@ -936,7 +936,7 @@ __export(index_exports, {
|
|
|
936
936
|
TokenValidator: () => TokenValidator,
|
|
937
937
|
addressesMatch: () => addressesMatch,
|
|
938
938
|
archivedKeyFromTokenId: () => archivedKeyFromTokenId,
|
|
939
|
-
areSameNametag: () =>
|
|
939
|
+
areSameNametag: () => import_nostr_js_sdk5.areSameNametag,
|
|
940
940
|
base58Decode: () => base58Decode,
|
|
941
941
|
base58Encode: () => base58Encode2,
|
|
942
942
|
buildManifest: () => buildManifest,
|
|
@@ -966,7 +966,7 @@ __export(index_exports, {
|
|
|
966
966
|
decryptCMasterKey: () => decryptCMasterKey,
|
|
967
967
|
decryptJson: () => decryptJson,
|
|
968
968
|
decryptMnemonic: () => decryptMnemonic,
|
|
969
|
-
decryptNametag: () =>
|
|
969
|
+
decryptNametag: () => import_nostr_js_sdk5.decryptNametag,
|
|
970
970
|
decryptPrivateKey: () => decryptPrivateKey,
|
|
971
971
|
decryptSimple: () => decryptSimple,
|
|
972
972
|
decryptTextFormatKey: () => decryptTextFormatKey,
|
|
@@ -979,7 +979,7 @@ __export(index_exports, {
|
|
|
979
979
|
encodeBech32: () => encodeBech32,
|
|
980
980
|
encrypt: () => encrypt2,
|
|
981
981
|
encryptMnemonic: () => encryptMnemonic,
|
|
982
|
-
encryptNametag: () =>
|
|
982
|
+
encryptNametag: () => import_nostr_js_sdk5.encryptNametag,
|
|
983
983
|
encryptSimple: () => encryptSimple,
|
|
984
984
|
encryptWallet: () => encryptWallet,
|
|
985
985
|
extractFromText: () => extractFromText,
|
|
@@ -1008,8 +1008,8 @@ __export(index_exports, {
|
|
|
1008
1008
|
hasUncommittedTransactions: () => hasUncommittedTransactions,
|
|
1009
1009
|
hasValidTxfData: () => hasValidTxfData,
|
|
1010
1010
|
hash160: () => hash160,
|
|
1011
|
-
hashAddressForTag: () =>
|
|
1012
|
-
hashNametag: () =>
|
|
1011
|
+
hashAddressForTag: () => import_nostr_js_sdk5.hashAddressForTag,
|
|
1012
|
+
hashNametag: () => import_nostr_js_sdk5.hashNametag,
|
|
1013
1013
|
hashSignMessage: () => hashSignMessage,
|
|
1014
1014
|
hexToBytes: () => hexToBytes2,
|
|
1015
1015
|
hexToWIF: () => hexToWIF,
|
|
@@ -1024,7 +1024,7 @@ __export(index_exports, {
|
|
|
1024
1024
|
isKnownToken: () => isKnownToken,
|
|
1025
1025
|
isPaymentSessionTerminal: () => isPaymentSessionTerminal,
|
|
1026
1026
|
isPaymentSessionTimedOut: () => isPaymentSessionTimedOut,
|
|
1027
|
-
isPhoneNumber: () =>
|
|
1027
|
+
isPhoneNumber: () => import_nostr_js_sdk5.isPhoneNumber,
|
|
1028
1028
|
isSQLiteDatabase: () => isSQLiteDatabase,
|
|
1029
1029
|
isSphereError: () => isSphereError,
|
|
1030
1030
|
isTextWalletEncrypted: () => isTextWalletEncrypted,
|
|
@@ -1043,7 +1043,7 @@ __export(index_exports, {
|
|
|
1043
1043
|
mnemonicToSeedSync: () => mnemonicToSeedSync2,
|
|
1044
1044
|
normalizeAddress: () => normalizeAddress,
|
|
1045
1045
|
normalizeCoinId: () => normalizeCoinId,
|
|
1046
|
-
normalizeNametag: () =>
|
|
1046
|
+
normalizeNametag: () => import_nostr_js_sdk5.normalizeNametag,
|
|
1047
1047
|
normalizeSdkTokenToStorage: () => normalizeSdkTokenToStorage,
|
|
1048
1048
|
objectToTxf: () => objectToTxf,
|
|
1049
1049
|
parseAddress: () => parseAddress,
|
|
@@ -2096,26 +2096,14 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2096
2096
|
}
|
|
2097
2097
|
/**
|
|
2098
2098
|
* Convert a BindingInfo (from nostr-js-sdk) to PeerInfo (sphere-sdk type).
|
|
2099
|
-
* Computes PROXY address from nametag if available.
|
|
2100
2099
|
*/
|
|
2101
2100
|
async bindingInfoToPeerInfo(binding, nametag) {
|
|
2102
|
-
const nametagValue = nametag || binding.nametag;
|
|
2103
|
-
let proxyAddress = binding.proxyAddress;
|
|
2104
|
-
if (nametagValue && !proxyAddress) {
|
|
2105
|
-
try {
|
|
2106
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
2107
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametagValue);
|
|
2108
|
-
proxyAddress = proxyAddr.toString();
|
|
2109
|
-
} catch {
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
2101
|
return {
|
|
2113
|
-
nametag:
|
|
2102
|
+
nametag: nametag || binding.nametag,
|
|
2114
2103
|
transportPubkey: binding.transportPubkey,
|
|
2115
2104
|
chainPubkey: binding.publicKey || "",
|
|
2116
2105
|
l1Address: binding.l1Address || "",
|
|
2117
2106
|
directAddress: binding.directAddress || "",
|
|
2118
|
-
proxyAddress,
|
|
2119
2107
|
timestamp: binding.timestamp
|
|
2120
2108
|
};
|
|
2121
2109
|
}
|
|
@@ -2141,7 +2129,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2141
2129
|
chainPubkey: content.public_key || "",
|
|
2142
2130
|
l1Address: content.l1_address || "",
|
|
2143
2131
|
directAddress: content.direct_address || "",
|
|
2144
|
-
proxyAddress: content.proxy_address || void 0,
|
|
2145
2132
|
timestamp: bindingEvent.created_at * 1e3
|
|
2146
2133
|
};
|
|
2147
2134
|
} catch {
|
|
@@ -2184,7 +2171,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2184
2171
|
chainPubkey: content.public_key || "",
|
|
2185
2172
|
l1Address: content.l1_address || "",
|
|
2186
2173
|
directAddress: content.direct_address || "",
|
|
2187
|
-
proxyAddress: content.proxy_address || void 0,
|
|
2188
2174
|
timestamp: event.created_at * 1e3
|
|
2189
2175
|
});
|
|
2190
2176
|
} catch {
|
|
@@ -2254,8 +2240,6 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2254
2240
|
}
|
|
2255
2241
|
const nostrPubkey = this.getNostrPubkey();
|
|
2256
2242
|
if (nametag) {
|
|
2257
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
2258
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
2259
2243
|
try {
|
|
2260
2244
|
const success2 = await this.nostrClient.publishNametagBinding(
|
|
2261
2245
|
nametag,
|
|
@@ -2263,8 +2247,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2263
2247
|
{
|
|
2264
2248
|
publicKey: chainPubkey,
|
|
2265
2249
|
l1Address,
|
|
2266
|
-
directAddress
|
|
2267
|
-
proxyAddress: proxyAddr.toString()
|
|
2250
|
+
directAddress
|
|
2268
2251
|
}
|
|
2269
2252
|
);
|
|
2270
2253
|
if (success2) {
|
|
@@ -6468,6 +6451,13 @@ function createL1PaymentsModule(config) {
|
|
|
6468
6451
|
return new L1PaymentsModule(config);
|
|
6469
6452
|
}
|
|
6470
6453
|
|
|
6454
|
+
// types/v2-transfer.ts
|
|
6455
|
+
function isV2TransferPayload(obj) {
|
|
6456
|
+
if (!obj || typeof obj !== "object") return false;
|
|
6457
|
+
const p = obj;
|
|
6458
|
+
return p.type === "V2_TRANSFER" && typeof p.tokenBlob === "string" && p.tokenBlob.length > 0;
|
|
6459
|
+
}
|
|
6460
|
+
|
|
6471
6461
|
// modules/payments/TokenSplitExecutor.ts
|
|
6472
6462
|
init_logger();
|
|
6473
6463
|
init_errors();
|
|
@@ -6841,18 +6831,117 @@ var TokenReservationLedger = class {
|
|
|
6841
6831
|
// modules/payments/SpendQueue.ts
|
|
6842
6832
|
init_logger();
|
|
6843
6833
|
init_errors();
|
|
6844
|
-
var
|
|
6834
|
+
var import_Token3 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
6845
6835
|
var import_CoinId2 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
|
|
6836
|
+
|
|
6837
|
+
// token-engine/sdk.ts
|
|
6838
|
+
var import_StateTransitionClient = require("state-transition-sdk-v2/lib/StateTransitionClient.js");
|
|
6839
|
+
var import_AggregatorClient = require("state-transition-sdk-v2/lib/api/AggregatorClient.js");
|
|
6840
|
+
var import_NetworkId = require("state-transition-sdk-v2/lib/api/NetworkId.js");
|
|
6841
|
+
var import_CertificationData = require("state-transition-sdk-v2/lib/api/CertificationData.js");
|
|
6842
|
+
var import_CertificationResponse = require("state-transition-sdk-v2/lib/api/CertificationResponse.js");
|
|
6843
|
+
var import_StateId = require("state-transition-sdk-v2/lib/api/StateId.js");
|
|
6844
|
+
var import_InclusionProof = require("state-transition-sdk-v2/lib/api/InclusionProof.js");
|
|
6845
|
+
var import_InclusionProofResponse = require("state-transition-sdk-v2/lib/api/InclusionProofResponse.js");
|
|
6846
|
+
var import_RootTrustBase = require("state-transition-sdk-v2/lib/api/bft/RootTrustBase.js");
|
|
6847
|
+
var import_InclusionProofUtils2 = require("state-transition-sdk-v2/lib/util/InclusionProofUtils.js");
|
|
6848
|
+
var import_Token2 = require("state-transition-sdk-v2/lib/transaction/Token.js");
|
|
6849
|
+
var import_MintTransaction = require("state-transition-sdk-v2/lib/transaction/MintTransaction.js");
|
|
6850
|
+
var import_TransferTransaction = require("state-transition-sdk-v2/lib/transaction/TransferTransaction.js");
|
|
6851
|
+
var import_CertifiedMintTransaction = require("state-transition-sdk-v2/lib/transaction/CertifiedMintTransaction.js");
|
|
6852
|
+
var import_CertifiedTransferTransaction = require("state-transition-sdk-v2/lib/transaction/CertifiedTransferTransaction.js");
|
|
6853
|
+
var import_TokenId2 = require("state-transition-sdk-v2/lib/transaction/TokenId.js");
|
|
6854
|
+
var import_TokenType = require("state-transition-sdk-v2/lib/transaction/TokenType.js");
|
|
6855
|
+
var import_TokenSalt = require("state-transition-sdk-v2/lib/transaction/TokenSalt.js");
|
|
6856
|
+
var import_MintJustificationVerifierService = require("state-transition-sdk-v2/lib/transaction/verification/MintJustificationVerifierService.js");
|
|
6857
|
+
var import_EncodedPredicate = require("state-transition-sdk-v2/lib/predicate/EncodedPredicate.js");
|
|
6858
|
+
var import_SignaturePredicate = require("state-transition-sdk-v2/lib/predicate/builtin/SignaturePredicate.js");
|
|
6859
|
+
var import_SignaturePredicateUnlockScript = require("state-transition-sdk-v2/lib/predicate/builtin/SignaturePredicateUnlockScript.js");
|
|
6860
|
+
var import_BurnPredicate = require("state-transition-sdk-v2/lib/predicate/builtin/BurnPredicate.js");
|
|
6861
|
+
var import_PredicateVerifierService = require("state-transition-sdk-v2/lib/predicate/verification/PredicateVerifierService.js");
|
|
6862
|
+
var import_SigningService = require("state-transition-sdk-v2/lib/crypto/secp256k1/SigningService.js");
|
|
6863
|
+
var import_Signature = require("state-transition-sdk-v2/lib/crypto/secp256k1/Signature.js");
|
|
6864
|
+
var import_MintSigningService = require("state-transition-sdk-v2/lib/crypto/MintSigningService.js");
|
|
6865
|
+
var import_HashAlgorithm2 = require("state-transition-sdk-v2/lib/crypto/hash/HashAlgorithm.js");
|
|
6866
|
+
var import_DataHash = require("state-transition-sdk-v2/lib/crypto/hash/DataHash.js");
|
|
6867
|
+
var import_DataHasher = require("state-transition-sdk-v2/lib/crypto/hash/DataHasher.js");
|
|
6868
|
+
var import_DataHasherFactory = require("state-transition-sdk-v2/lib/crypto/hash/DataHasherFactory.js");
|
|
6869
|
+
var import_CborSerializer = require("state-transition-sdk-v2/lib/serialization/cbor/CborSerializer.js");
|
|
6870
|
+
var import_CborDeserializer = require("state-transition-sdk-v2/lib/serialization/cbor/CborDeserializer.js");
|
|
6871
|
+
var import_CborError = require("state-transition-sdk-v2/lib/serialization/cbor/CborError.js");
|
|
6872
|
+
var import_Asset = require("state-transition-sdk-v2/lib/payment/asset/Asset.js");
|
|
6873
|
+
var import_AssetId = require("state-transition-sdk-v2/lib/payment/asset/AssetId.js");
|
|
6874
|
+
var import_PaymentAssetCollection = require("state-transition-sdk-v2/lib/payment/asset/PaymentAssetCollection.js");
|
|
6875
|
+
var import_TokenSplit = require("state-transition-sdk-v2/lib/payment/TokenSplit.js");
|
|
6876
|
+
var import_SplitTokenRequest = require("state-transition-sdk-v2/lib/payment/SplitTokenRequest.js");
|
|
6877
|
+
var import_SplitToken = require("state-transition-sdk-v2/lib/payment/SplitToken.js");
|
|
6878
|
+
var import_SplitAssetProof = require("state-transition-sdk-v2/lib/payment/SplitAssetProof.js");
|
|
6879
|
+
var import_SplitMintJustification = require("state-transition-sdk-v2/lib/payment/SplitMintJustification.js");
|
|
6880
|
+
var import_SplitMintJustificationVerifier = require("state-transition-sdk-v2/lib/payment/SplitMintJustificationVerifier.js");
|
|
6881
|
+
var import_VerificationStatus = require("state-transition-sdk-v2/lib/verification/VerificationStatus.js");
|
|
6882
|
+
var import_VerificationResult = require("state-transition-sdk-v2/lib/verification/VerificationResult.js");
|
|
6883
|
+
var import_HexConverter = require("state-transition-sdk-v2/lib/util/HexConverter.js");
|
|
6884
|
+
var import_BigintConverter = require("state-transition-sdk-v2/lib/util/BigintConverter.js");
|
|
6885
|
+
var import_BitString = require("state-transition-sdk-v2/lib/util/BitString.js");
|
|
6886
|
+
|
|
6887
|
+
// token-engine/token-blob.ts
|
|
6888
|
+
var TOKEN_BLOB_TAG = 39051n;
|
|
6889
|
+
var TOKEN_BLOB_VERSION = 1;
|
|
6890
|
+
function encodeTokenBlob(blob) {
|
|
6891
|
+
return import_CborSerializer.CborSerializer.encodeTag(
|
|
6892
|
+
TOKEN_BLOB_TAG,
|
|
6893
|
+
import_CborSerializer.CborSerializer.encodeArray(
|
|
6894
|
+
import_CborSerializer.CborSerializer.encodeUnsignedInteger(BigInt(blob.v)),
|
|
6895
|
+
import_CborSerializer.CborSerializer.encodeUnsignedInteger(BigInt(blob.network)),
|
|
6896
|
+
import_CborSerializer.CborSerializer.encodeTextString(blob.tokenId),
|
|
6897
|
+
import_CborSerializer.CborSerializer.encodeByteString(blob.token)
|
|
6898
|
+
)
|
|
6899
|
+
);
|
|
6900
|
+
}
|
|
6901
|
+
function decodeTokenBlob(bytes) {
|
|
6902
|
+
const tag = import_CborDeserializer.CborDeserializer.decodeTag(bytes);
|
|
6903
|
+
if (tag.tag !== TOKEN_BLOB_TAG) {
|
|
6904
|
+
throw new import_CborError.CborError(`Invalid TokenBlob tag: ${tag.tag}`);
|
|
6905
|
+
}
|
|
6906
|
+
const fields = import_CborDeserializer.CborDeserializer.decodeArray(tag.data, 4);
|
|
6907
|
+
const v = Number(import_CborDeserializer.CborDeserializer.decodeUnsignedInteger(fields[0]));
|
|
6908
|
+
if (v !== TOKEN_BLOB_VERSION) {
|
|
6909
|
+
throw new import_CborError.CborError(`Unsupported TokenBlob version: ${v}`);
|
|
6910
|
+
}
|
|
6911
|
+
return {
|
|
6912
|
+
v,
|
|
6913
|
+
network: Number(import_CborDeserializer.CborDeserializer.decodeUnsignedInteger(fields[1])),
|
|
6914
|
+
tokenId: import_CborDeserializer.CborDeserializer.decodeTextString(fields[2]),
|
|
6915
|
+
token: import_CborDeserializer.CborDeserializer.decodeByteString(fields[3])
|
|
6916
|
+
};
|
|
6917
|
+
}
|
|
6918
|
+
|
|
6919
|
+
// modules/payments/SpendQueue.ts
|
|
6846
6920
|
var QUEUE_TIMEOUT_MS = 3e4;
|
|
6847
6921
|
var QUEUE_MAX_SIZE = 100;
|
|
6848
6922
|
var TAG2 = "SpendQueue";
|
|
6849
6923
|
var SpendPlanner = class {
|
|
6924
|
+
/**
|
|
6925
|
+
* Token engine (path B). When injected, value reads go through the v2 engine
|
|
6926
|
+
* (sdkData = engine blob); otherwise the legacy v1 SdkToken path is used. The
|
|
6927
|
+
* engine is wired after construction via {@link setEngine} (it is bound to the
|
|
6928
|
+
* payments deps, which arrive after the planner is built).
|
|
6929
|
+
*/
|
|
6930
|
+
engine;
|
|
6931
|
+
constructor(engine) {
|
|
6932
|
+
this.engine = engine;
|
|
6933
|
+
}
|
|
6934
|
+
/** Inject (or clear) the token engine. */
|
|
6935
|
+
setEngine(engine) {
|
|
6936
|
+
this.engine = engine;
|
|
6937
|
+
}
|
|
6850
6938
|
/**
|
|
6851
6939
|
* Async pre-computation: parse all tokens for a given coinId.
|
|
6852
6940
|
* Called BEFORE the synchronous critical section.
|
|
6853
6941
|
*
|
|
6854
|
-
* Filters to confirmed tokens matching the coinId,
|
|
6855
|
-
*
|
|
6942
|
+
* Filters to confirmed tokens matching the coinId, decodes each token's
|
|
6943
|
+
* sdkData and extracts the bigint amount — via the v2 engine when injected,
|
|
6944
|
+
* else the legacy v1 SdkToken path.
|
|
6856
6945
|
*/
|
|
6857
6946
|
async buildParsedPool(tokens, coinId) {
|
|
6858
6947
|
const pool = /* @__PURE__ */ new Map();
|
|
@@ -6861,20 +6950,28 @@ var SpendPlanner = class {
|
|
|
6861
6950
|
if (t.status !== "confirmed") continue;
|
|
6862
6951
|
if (!t.sdkData) continue;
|
|
6863
6952
|
try {
|
|
6864
|
-
const
|
|
6865
|
-
|
|
6866
|
-
const realAmount = this.getTokenBalance(sdkToken, coinId);
|
|
6867
|
-
if (realAmount <= 0n) {
|
|
6953
|
+
const { sdkToken, amount } = this.engine ? await this.parseViaEngine(t.sdkData, coinId) : await this.parseViaSdk(t.sdkData, coinId);
|
|
6954
|
+
if (amount <= 0n) {
|
|
6868
6955
|
logger.warn(TAG2, `Token ${t.id} has 0 balance for coinId ${coinId}`);
|
|
6869
6956
|
continue;
|
|
6870
6957
|
}
|
|
6871
|
-
pool.set(t.id, { token: t, sdkToken, amount
|
|
6958
|
+
pool.set(t.id, { token: t, sdkToken, amount });
|
|
6872
6959
|
} catch (e) {
|
|
6873
6960
|
logger.warn(TAG2, "Failed to parse token", t.id, e);
|
|
6874
6961
|
}
|
|
6875
6962
|
}
|
|
6876
6963
|
return pool;
|
|
6877
6964
|
}
|
|
6965
|
+
/** Engine path (v2): sdkData is the engine blob (hex of CBOR(TokenBlob)). */
|
|
6966
|
+
async parseViaEngine(sdkData, coinId) {
|
|
6967
|
+
const token = await this.engine.decodeToken(decodeTokenBlob(hexToBytes2(sdkData)));
|
|
6968
|
+
return { sdkToken: token, amount: this.engine.balanceOf(token, coinId) };
|
|
6969
|
+
}
|
|
6970
|
+
/** Legacy v1 path: sdkData is TXF JSON. */
|
|
6971
|
+
async parseViaSdk(sdkData, coinId) {
|
|
6972
|
+
const sdkToken = await import_Token3.Token.fromJSON(JSON.parse(sdkData));
|
|
6973
|
+
return { sdkToken, amount: this.getTokenBalance(sdkToken, coinId) };
|
|
6974
|
+
}
|
|
6878
6975
|
/**
|
|
6879
6976
|
* SYNCHRONOUS critical section. NO await allowed anywhere in this method.
|
|
6880
6977
|
*
|
|
@@ -7285,174 +7382,6 @@ var SpendQueue = class {
|
|
|
7285
7382
|
}
|
|
7286
7383
|
};
|
|
7287
7384
|
|
|
7288
|
-
// modules/payments/NametagMinter.ts
|
|
7289
|
-
init_logger();
|
|
7290
|
-
var import_Token3 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
7291
|
-
var import_TokenId2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
7292
|
-
var import_TokenType = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
7293
|
-
var import_TokenState2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
7294
|
-
var import_MintTransactionData = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
|
|
7295
|
-
var import_MintCommitment = require("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment");
|
|
7296
|
-
var import_HashAlgorithm2 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
|
|
7297
|
-
var import_UnmaskedPredicate2 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
|
|
7298
|
-
var import_InclusionProofUtils2 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
|
|
7299
|
-
var import_nostr_js_sdk3 = require("@unicitylabs/nostr-js-sdk");
|
|
7300
|
-
var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
7301
|
-
var NametagMinter = class {
|
|
7302
|
-
client;
|
|
7303
|
-
trustBase;
|
|
7304
|
-
signingService;
|
|
7305
|
-
skipVerification;
|
|
7306
|
-
debug;
|
|
7307
|
-
constructor(config) {
|
|
7308
|
-
this.client = config.stateTransitionClient;
|
|
7309
|
-
this.trustBase = config.trustBase;
|
|
7310
|
-
this.signingService = config.signingService;
|
|
7311
|
-
this.skipVerification = config.skipVerification ?? false;
|
|
7312
|
-
this.debug = config.debug ?? false;
|
|
7313
|
-
}
|
|
7314
|
-
log(message, ...args) {
|
|
7315
|
-
logger.debug("NametagMinter", message, ...args);
|
|
7316
|
-
}
|
|
7317
|
-
/**
|
|
7318
|
-
* Check if a nametag is available (not already minted)
|
|
7319
|
-
*/
|
|
7320
|
-
async isNametagAvailable(nametag) {
|
|
7321
|
-
try {
|
|
7322
|
-
const stripped = nametag.startsWith("@") ? nametag.slice(1) : nametag;
|
|
7323
|
-
const cleanNametag = (0, import_nostr_js_sdk3.normalizeNametag)(stripped);
|
|
7324
|
-
const nametagTokenId = await import_TokenId2.TokenId.fromNameTag(cleanNametag);
|
|
7325
|
-
const isMinted = await this.client.isMinted(this.trustBase, nametagTokenId);
|
|
7326
|
-
return !isMinted;
|
|
7327
|
-
} catch (error) {
|
|
7328
|
-
this.log("Error checking nametag availability:", error);
|
|
7329
|
-
return false;
|
|
7330
|
-
}
|
|
7331
|
-
}
|
|
7332
|
-
/**
|
|
7333
|
-
* Mint a nametag token on-chain
|
|
7334
|
-
*
|
|
7335
|
-
* @param nametag - The nametag to mint (e.g., "alice" or "@alice")
|
|
7336
|
-
* @param ownerAddress - The owner's direct address
|
|
7337
|
-
* @returns MintNametagResult with token if successful
|
|
7338
|
-
*/
|
|
7339
|
-
async mintNametag(nametag, ownerAddress) {
|
|
7340
|
-
const stripped = nametag.startsWith("@") ? nametag.slice(1) : nametag;
|
|
7341
|
-
const cleanNametag = (0, import_nostr_js_sdk3.normalizeNametag)(stripped);
|
|
7342
|
-
this.log(`Starting mint for nametag: ${cleanNametag}`);
|
|
7343
|
-
try {
|
|
7344
|
-
const nametagTokenId = await import_TokenId2.TokenId.fromNameTag(cleanNametag);
|
|
7345
|
-
const nametagTokenType = new import_TokenType.TokenType(
|
|
7346
|
-
Buffer.from(UNICITY_TOKEN_TYPE_HEX, "hex")
|
|
7347
|
-
);
|
|
7348
|
-
const nametagBytes = new TextEncoder().encode(cleanNametag);
|
|
7349
|
-
const pubKey = this.signingService.publicKey;
|
|
7350
|
-
const saltInput = new Uint8Array(pubKey.length + nametagBytes.length);
|
|
7351
|
-
saltInput.set(pubKey, 0);
|
|
7352
|
-
saltInput.set(nametagBytes, pubKey.length);
|
|
7353
|
-
const saltBuffer = await crypto.subtle.digest("SHA-256", saltInput);
|
|
7354
|
-
const salt = new Uint8Array(saltBuffer);
|
|
7355
|
-
this.log("Generated deterministic salt");
|
|
7356
|
-
const mintData = await import_MintTransactionData.MintTransactionData.createFromNametag(
|
|
7357
|
-
cleanNametag,
|
|
7358
|
-
nametagTokenType,
|
|
7359
|
-
ownerAddress,
|
|
7360
|
-
salt,
|
|
7361
|
-
ownerAddress
|
|
7362
|
-
);
|
|
7363
|
-
this.log("Created MintTransactionData");
|
|
7364
|
-
const commitment = await import_MintCommitment.MintCommitment.create(mintData);
|
|
7365
|
-
this.log("Created MintCommitment");
|
|
7366
|
-
const MAX_RETRIES = 3;
|
|
7367
|
-
let submitSuccess = false;
|
|
7368
|
-
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
7369
|
-
try {
|
|
7370
|
-
this.log(`Submitting commitment (attempt ${attempt}/${MAX_RETRIES})...`);
|
|
7371
|
-
const response = await this.client.submitMintCommitment(commitment);
|
|
7372
|
-
if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
|
|
7373
|
-
this.log(`Commitment ${response.status === "REQUEST_ID_EXISTS" ? "already exists" : "submitted successfully"}`);
|
|
7374
|
-
submitSuccess = true;
|
|
7375
|
-
break;
|
|
7376
|
-
} else {
|
|
7377
|
-
this.log(`Commitment failed: ${response.status}`);
|
|
7378
|
-
if (attempt === MAX_RETRIES) {
|
|
7379
|
-
return {
|
|
7380
|
-
success: false,
|
|
7381
|
-
error: `Failed to submit commitment after ${MAX_RETRIES} attempts: ${response.status}`
|
|
7382
|
-
};
|
|
7383
|
-
}
|
|
7384
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
7385
|
-
}
|
|
7386
|
-
} catch (error) {
|
|
7387
|
-
this.log(`Attempt ${attempt} error:`, error);
|
|
7388
|
-
if (attempt === MAX_RETRIES) {
|
|
7389
|
-
return {
|
|
7390
|
-
success: false,
|
|
7391
|
-
error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`
|
|
7392
|
-
};
|
|
7393
|
-
}
|
|
7394
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
7395
|
-
}
|
|
7396
|
-
}
|
|
7397
|
-
if (!submitSuccess) {
|
|
7398
|
-
return {
|
|
7399
|
-
success: false,
|
|
7400
|
-
error: "Failed to submit commitment after retries"
|
|
7401
|
-
};
|
|
7402
|
-
}
|
|
7403
|
-
this.log("Waiting for inclusion proof...");
|
|
7404
|
-
const inclusionProof = await (0, import_InclusionProofUtils2.waitInclusionProof)(this.trustBase, this.client, commitment);
|
|
7405
|
-
this.log("Received inclusion proof");
|
|
7406
|
-
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
7407
|
-
const nametagPredicate = await import_UnmaskedPredicate2.UnmaskedPredicate.create(
|
|
7408
|
-
nametagTokenId,
|
|
7409
|
-
nametagTokenType,
|
|
7410
|
-
this.signingService,
|
|
7411
|
-
import_HashAlgorithm2.HashAlgorithm.SHA256,
|
|
7412
|
-
salt
|
|
7413
|
-
);
|
|
7414
|
-
const tokenState = new import_TokenState2.TokenState(nametagPredicate, null);
|
|
7415
|
-
let token;
|
|
7416
|
-
if (this.skipVerification) {
|
|
7417
|
-
this.log("Creating token WITHOUT verification (dev mode)");
|
|
7418
|
-
const tokenJson = {
|
|
7419
|
-
version: "2.0",
|
|
7420
|
-
state: tokenState.toJSON(),
|
|
7421
|
-
genesis: genesisTransaction.toJSON(),
|
|
7422
|
-
transactions: [],
|
|
7423
|
-
nametags: []
|
|
7424
|
-
};
|
|
7425
|
-
token = await import_Token3.Token.fromJSON(tokenJson);
|
|
7426
|
-
} else {
|
|
7427
|
-
token = await import_Token3.Token.mint(
|
|
7428
|
-
this.trustBase,
|
|
7429
|
-
tokenState,
|
|
7430
|
-
genesisTransaction
|
|
7431
|
-
);
|
|
7432
|
-
}
|
|
7433
|
-
this.log(`Nametag minted successfully: ${cleanNametag}`);
|
|
7434
|
-
const nametagData = {
|
|
7435
|
-
name: cleanNametag,
|
|
7436
|
-
token: token.toJSON(),
|
|
7437
|
-
timestamp: Date.now(),
|
|
7438
|
-
format: "txf",
|
|
7439
|
-
version: "2.0"
|
|
7440
|
-
};
|
|
7441
|
-
return {
|
|
7442
|
-
success: true,
|
|
7443
|
-
token,
|
|
7444
|
-
nametagData
|
|
7445
|
-
};
|
|
7446
|
-
} catch (error) {
|
|
7447
|
-
this.log("Minting failed:", error);
|
|
7448
|
-
return {
|
|
7449
|
-
success: false,
|
|
7450
|
-
error: error instanceof Error ? error.message : String(error)
|
|
7451
|
-
};
|
|
7452
|
-
}
|
|
7453
|
-
}
|
|
7454
|
-
};
|
|
7455
|
-
|
|
7456
7385
|
// modules/payments/PaymentsModule.ts
|
|
7457
7386
|
init_constants();
|
|
7458
7387
|
|
|
@@ -8069,6 +7998,15 @@ function txfToToken(tokenId, txf) {
|
|
|
8069
7998
|
sdkData: JSON.stringify(txf)
|
|
8070
7999
|
};
|
|
8071
8000
|
}
|
|
8001
|
+
function isV2TokenBlob(sdkData) {
|
|
8002
|
+
return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
8003
|
+
}
|
|
8004
|
+
function v2TokenId(token) {
|
|
8005
|
+
return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
|
|
8006
|
+
}
|
|
8007
|
+
function isV2TokenEntry(entry) {
|
|
8008
|
+
return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
|
|
8009
|
+
}
|
|
8072
8010
|
async function buildTxfStorageData(tokens, meta, options) {
|
|
8073
8011
|
const storageData = {
|
|
8074
8012
|
_meta: {
|
|
@@ -8099,6 +8037,8 @@ async function buildTxfStorageData(tokens, meta, options) {
|
|
|
8099
8037
|
if (txf) {
|
|
8100
8038
|
const actualTokenId = txf.genesis.data.tokenId;
|
|
8101
8039
|
storageData[keyFromTokenId(actualTokenId)] = txf;
|
|
8040
|
+
} else if (isV2TokenBlob(token.sdkData)) {
|
|
8041
|
+
storageData[keyFromTokenId(v2TokenId(token))] = token;
|
|
8102
8042
|
}
|
|
8103
8043
|
}
|
|
8104
8044
|
if (options?.archivedTokens && options.archivedTokens.size > 0) {
|
|
@@ -8196,6 +8136,8 @@ function parseTxfStorageData(data) {
|
|
|
8196
8136
|
if (txfToken?.genesis?.data?.tokenId) {
|
|
8197
8137
|
const token = txfToToken(tokenId, txfToken);
|
|
8198
8138
|
result.tokens.push(token);
|
|
8139
|
+
} else if (isV2TokenEntry(storageData[key])) {
|
|
8140
|
+
result.tokens.push(storageData[key]);
|
|
8199
8141
|
}
|
|
8200
8142
|
} catch (err) {
|
|
8201
8143
|
result.validationErrors.push(`Token ${tokenId}: ${err}`);
|
|
@@ -8471,12 +8413,12 @@ init_logger();
|
|
|
8471
8413
|
init_errors();
|
|
8472
8414
|
var import_Token4 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
8473
8415
|
var import_TokenId3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
8474
|
-
var
|
|
8416
|
+
var import_TokenState2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
8475
8417
|
var import_CoinId3 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
|
|
8476
8418
|
var import_TokenCoinData2 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData");
|
|
8477
8419
|
var import_TokenSplitBuilder2 = require("@unicitylabs/state-transition-sdk/lib/transaction/split/TokenSplitBuilder");
|
|
8478
8420
|
var import_HashAlgorithm3 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
|
|
8479
|
-
var
|
|
8421
|
+
var import_UnmaskedPredicate2 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
|
|
8480
8422
|
var import_UnmaskedPredicateReference2 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
8481
8423
|
var import_TransferCommitment2 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment");
|
|
8482
8424
|
var import_InclusionProofUtils3 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
|
|
@@ -8598,14 +8540,14 @@ var InstantSplitExecutor = class {
|
|
|
8598
8540
|
options?.message
|
|
8599
8541
|
// on-chain message (invoice memo bytes, or null)
|
|
8600
8542
|
);
|
|
8601
|
-
const mintedPredicate = await
|
|
8543
|
+
const mintedPredicate = await import_UnmaskedPredicate2.UnmaskedPredicate.create(
|
|
8602
8544
|
recipientTokenId,
|
|
8603
8545
|
tokenToSplit.type,
|
|
8604
8546
|
this.signingService,
|
|
8605
8547
|
import_HashAlgorithm3.HashAlgorithm.SHA256,
|
|
8606
8548
|
recipientSalt
|
|
8607
8549
|
);
|
|
8608
|
-
const mintedState = new
|
|
8550
|
+
const mintedState = new import_TokenState2.TokenState(mintedPredicate, null);
|
|
8609
8551
|
logger.debug("InstantSplit", "Step 5: Packaging V5 bundle...");
|
|
8610
8552
|
const senderPubkey = toHex2(this.signingService.publicKey);
|
|
8611
8553
|
let nametagTokenJson;
|
|
@@ -8720,14 +8662,14 @@ var InstantSplitExecutor = class {
|
|
|
8720
8662
|
* It does NOT need the genesis transaction or mint proof.
|
|
8721
8663
|
*/
|
|
8722
8664
|
async createTransferCommitmentFromMintData(mintData, recipientAddress, transferSalt, signingService, nametagTokens, message) {
|
|
8723
|
-
const predicate = await
|
|
8665
|
+
const predicate = await import_UnmaskedPredicate2.UnmaskedPredicate.create(
|
|
8724
8666
|
mintData.tokenId,
|
|
8725
8667
|
mintData.tokenType,
|
|
8726
8668
|
signingService,
|
|
8727
8669
|
import_HashAlgorithm3.HashAlgorithm.SHA256,
|
|
8728
8670
|
mintData.salt
|
|
8729
8671
|
);
|
|
8730
|
-
const state = new
|
|
8672
|
+
const state = new import_TokenState2.TokenState(predicate, null);
|
|
8731
8673
|
const minimalToken = {
|
|
8732
8674
|
state,
|
|
8733
8675
|
nametagTokens: nametagTokens || [],
|
|
@@ -8788,14 +8730,14 @@ var InstantSplitExecutor = class {
|
|
|
8788
8730
|
message: `Mint proof received in ${proofDuration.toFixed(0)}ms`
|
|
8789
8731
|
});
|
|
8790
8732
|
const mintTransaction = senderMintCommitment.toTransaction(senderMintProof);
|
|
8791
|
-
const predicate = await
|
|
8733
|
+
const predicate = await import_UnmaskedPredicate2.UnmaskedPredicate.create(
|
|
8792
8734
|
context.senderTokenId,
|
|
8793
8735
|
context.tokenType,
|
|
8794
8736
|
context.signingService,
|
|
8795
8737
|
import_HashAlgorithm3.HashAlgorithm.SHA256,
|
|
8796
8738
|
context.senderSalt
|
|
8797
8739
|
);
|
|
8798
|
-
const state = new
|
|
8740
|
+
const state = new import_TokenState2.TokenState(predicate, null);
|
|
8799
8741
|
const changeToken = await import_Token4.Token.mint(this.trustBase, state, mintTransaction);
|
|
8800
8742
|
if (!this.devMode) {
|
|
8801
8743
|
const verification = await changeToken.verify(this.trustBase);
|
|
@@ -8875,14 +8817,14 @@ var InstantSplitExecutor = class {
|
|
|
8875
8817
|
init_logger();
|
|
8876
8818
|
init_errors();
|
|
8877
8819
|
var import_Token5 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
8878
|
-
var
|
|
8820
|
+
var import_TokenState3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
8879
8821
|
var import_TokenType2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
8880
8822
|
var import_HashAlgorithm4 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
|
|
8881
|
-
var
|
|
8823
|
+
var import_UnmaskedPredicate3 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
|
|
8882
8824
|
var import_TransferCommitment3 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment");
|
|
8883
|
-
var
|
|
8884
|
-
var
|
|
8885
|
-
var
|
|
8825
|
+
var import_TransferTransaction2 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction");
|
|
8826
|
+
var import_MintCommitment = require("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment");
|
|
8827
|
+
var import_MintTransactionData = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
|
|
8886
8828
|
var import_InclusionProofUtils4 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
|
|
8887
8829
|
|
|
8888
8830
|
// types/instant-split.ts
|
|
@@ -8975,11 +8917,11 @@ var InstantSplitProcessor = class {
|
|
|
8975
8917
|
logger.warn("InstantSplit", "Sender pubkey mismatch (non-fatal)");
|
|
8976
8918
|
}
|
|
8977
8919
|
const burnTxJson = JSON.parse(bundle.burnTransaction);
|
|
8978
|
-
const _burnTransaction = await
|
|
8920
|
+
const _burnTransaction = await import_TransferTransaction2.TransferTransaction.fromJSON(burnTxJson);
|
|
8979
8921
|
logger.debug("InstantSplit", "Burn transaction validated");
|
|
8980
8922
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
8981
|
-
const mintData = await
|
|
8982
|
-
const mintCommitment = await
|
|
8923
|
+
const mintData = await import_MintTransactionData.MintTransactionData.fromJSON(mintDataJson);
|
|
8924
|
+
const mintCommitment = await import_MintCommitment.MintCommitment.create(mintData);
|
|
8983
8925
|
logger.debug("InstantSplit", "Mint commitment recreated");
|
|
8984
8926
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
8985
8927
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
@@ -9011,14 +8953,14 @@ var InstantSplitProcessor = class {
|
|
|
9011
8953
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
9012
8954
|
logger.debug("InstantSplit", "Transfer proof received");
|
|
9013
8955
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
9014
|
-
const finalRecipientPredicate = await
|
|
8956
|
+
const finalRecipientPredicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
|
|
9015
8957
|
mintData.tokenId,
|
|
9016
8958
|
tokenType,
|
|
9017
8959
|
signingService,
|
|
9018
8960
|
import_HashAlgorithm4.HashAlgorithm.SHA256,
|
|
9019
8961
|
transferSalt
|
|
9020
8962
|
);
|
|
9021
|
-
const finalRecipientState = new
|
|
8963
|
+
const finalRecipientState = new import_TokenState3.TokenState(finalRecipientPredicate, null);
|
|
9022
8964
|
logger.debug("InstantSplit", "Final recipient state created");
|
|
9023
8965
|
let nametagTokens = [];
|
|
9024
8966
|
const recipientAddressStr = bundle.recipientAddressJson;
|
|
@@ -9125,8 +9067,8 @@ var InstantSplitProcessor = class {
|
|
|
9125
9067
|
await this.waitInclusionProofWithDevBypass(burnCommitment, options?.proofTimeoutMs);
|
|
9126
9068
|
logger.debug("InstantSplit", "V4: Burn proof received");
|
|
9127
9069
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
9128
|
-
const mintData = await
|
|
9129
|
-
const mintCommitment = await
|
|
9070
|
+
const mintData = await import_MintTransactionData.MintTransactionData.fromJSON(mintDataJson);
|
|
9071
|
+
const mintCommitment = await import_MintCommitment.MintCommitment.create(mintData);
|
|
9130
9072
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
9131
9073
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
9132
9074
|
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
@@ -9139,14 +9081,14 @@ var InstantSplitProcessor = class {
|
|
|
9139
9081
|
logger.debug("InstantSplit", "V4: Mint proof received");
|
|
9140
9082
|
const tokenType = new import_TokenType2.TokenType(fromHex3(bundle.tokenTypeHex));
|
|
9141
9083
|
const recipientSalt = fromHex3(bundle.recipientSaltHex);
|
|
9142
|
-
const recipientPredicate = await
|
|
9084
|
+
const recipientPredicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
|
|
9143
9085
|
mintData.tokenId,
|
|
9144
9086
|
tokenType,
|
|
9145
9087
|
signingService,
|
|
9146
9088
|
import_HashAlgorithm4.HashAlgorithm.SHA256,
|
|
9147
9089
|
recipientSalt
|
|
9148
9090
|
);
|
|
9149
|
-
const recipientState = new
|
|
9091
|
+
const recipientState = new import_TokenState3.TokenState(recipientPredicate, null);
|
|
9150
9092
|
const tokenJson = {
|
|
9151
9093
|
version: "2.0",
|
|
9152
9094
|
state: recipientState.toJSON(),
|
|
@@ -9169,14 +9111,14 @@ var InstantSplitProcessor = class {
|
|
|
9169
9111
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
9170
9112
|
logger.debug("InstantSplit", "V4: Transfer proof received");
|
|
9171
9113
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
9172
|
-
const finalPredicate = await
|
|
9114
|
+
const finalPredicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
|
|
9173
9115
|
mintData.tokenId,
|
|
9174
9116
|
tokenType,
|
|
9175
9117
|
signingService,
|
|
9176
9118
|
import_HashAlgorithm4.HashAlgorithm.SHA256,
|
|
9177
9119
|
transferSalt
|
|
9178
9120
|
);
|
|
9179
|
-
const finalState = new
|
|
9121
|
+
const finalState = new import_TokenState3.TokenState(finalPredicate, null);
|
|
9180
9122
|
const finalTokenJson = mintedToken.toJSON();
|
|
9181
9123
|
finalTokenJson.state = finalState.toJSON();
|
|
9182
9124
|
finalTokenJson.transactions = [transferTransaction.toJSON()];
|
|
@@ -9227,17 +9169,17 @@ var InstantSplitProcessor = class {
|
|
|
9227
9169
|
var import_Token6 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
9228
9170
|
var import_CoinId4 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
|
|
9229
9171
|
var import_TransferCommitment4 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment");
|
|
9230
|
-
var
|
|
9231
|
-
var
|
|
9172
|
+
var import_TransferTransaction3 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction");
|
|
9173
|
+
var import_SigningService2 = require("@unicitylabs/state-transition-sdk/lib/sign/SigningService");
|
|
9232
9174
|
var import_AddressScheme = require("@unicitylabs/state-transition-sdk/lib/address/AddressScheme");
|
|
9233
|
-
var
|
|
9234
|
-
var
|
|
9175
|
+
var import_UnmaskedPredicate4 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
|
|
9176
|
+
var import_TokenState4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
9235
9177
|
var import_HashAlgorithm5 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
|
|
9236
9178
|
var import_TokenType3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
9237
|
-
var
|
|
9238
|
-
var
|
|
9179
|
+
var import_MintCommitment2 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment");
|
|
9180
|
+
var import_MintTransactionData2 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
|
|
9239
9181
|
var import_InclusionProofUtils5 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
|
|
9240
|
-
var
|
|
9182
|
+
var import_InclusionProof2 = require("@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof");
|
|
9241
9183
|
function computeHistoryDedupKey(type, tokenId, transferId) {
|
|
9242
9184
|
if (type === "SENT" && transferId) return `${type}_transfer_${transferId}`;
|
|
9243
9185
|
if (tokenId) return `${type}_${tokenId}`;
|
|
@@ -9258,7 +9200,7 @@ function enrichWithRegistry(info) {
|
|
|
9258
9200
|
}
|
|
9259
9201
|
return info;
|
|
9260
9202
|
}
|
|
9261
|
-
async function parseTokenInfo(tokenData) {
|
|
9203
|
+
async function parseTokenInfo(tokenData, engine) {
|
|
9262
9204
|
const defaultInfo = {
|
|
9263
9205
|
coinId: "ALPHA",
|
|
9264
9206
|
symbol: "ALPHA",
|
|
@@ -9266,6 +9208,25 @@ async function parseTokenInfo(tokenData) {
|
|
|
9266
9208
|
decimals: 0,
|
|
9267
9209
|
amount: "0"
|
|
9268
9210
|
};
|
|
9211
|
+
if (engine && typeof tokenData === "string" && looksLikeTokenBlob(tokenData)) {
|
|
9212
|
+
try {
|
|
9213
|
+
const token = await engine.decodeToken(decodeTokenBlob(hexToBytes2(tokenData)));
|
|
9214
|
+
const first = engine.readValue(token)?.assets[0];
|
|
9215
|
+
if (first) {
|
|
9216
|
+
return enrichWithRegistry({
|
|
9217
|
+
coinId: first.coinId,
|
|
9218
|
+
symbol: first.coinId.slice(0, 8),
|
|
9219
|
+
name: `Token ${first.coinId.slice(0, 8)}`,
|
|
9220
|
+
decimals: 0,
|
|
9221
|
+
amount: String(first.amount),
|
|
9222
|
+
tokenId: engine.tokenId(token)
|
|
9223
|
+
});
|
|
9224
|
+
}
|
|
9225
|
+
return { ...defaultInfo, tokenId: engine.tokenId(token) };
|
|
9226
|
+
} catch (error) {
|
|
9227
|
+
logger.warn("Payments", "Failed to parse token info via engine:", error);
|
|
9228
|
+
}
|
|
9229
|
+
}
|
|
9269
9230
|
try {
|
|
9270
9231
|
const data = typeof tokenData === "string" ? JSON.parse(tokenData) : tokenData;
|
|
9271
9232
|
try {
|
|
@@ -9404,27 +9365,41 @@ async function parseTokenInfo(tokenData) {
|
|
|
9404
9365
|
}
|
|
9405
9366
|
var sdkDataCache = /* @__PURE__ */ new Map();
|
|
9406
9367
|
var SDK_DATA_CACHE_MAX = 2e3;
|
|
9368
|
+
function looksLikeTokenBlob(sdkData) {
|
|
9369
|
+
return sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
9370
|
+
}
|
|
9371
|
+
function tryParseBlobKeys(sdkData) {
|
|
9372
|
+
try {
|
|
9373
|
+
const blob = decodeTokenBlob(hexToBytes2(sdkData));
|
|
9374
|
+
return { tokenId: blob.tokenId, stateHash: sha2562(bytesToHex3(blob.token), "hex") };
|
|
9375
|
+
} catch {
|
|
9376
|
+
return null;
|
|
9377
|
+
}
|
|
9378
|
+
}
|
|
9407
9379
|
function parseSdkDataCached(sdkData) {
|
|
9408
9380
|
const cached = sdkDataCache.get(sdkData);
|
|
9409
9381
|
if (cached) return cached;
|
|
9410
|
-
let
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9382
|
+
let entry = looksLikeTokenBlob(sdkData) ? tryParseBlobKeys(sdkData) : null;
|
|
9383
|
+
if (!entry) {
|
|
9384
|
+
let tokenId = null;
|
|
9385
|
+
let stateHash = "";
|
|
9386
|
+
try {
|
|
9387
|
+
const txf = JSON.parse(sdkData);
|
|
9388
|
+
tokenId = txf.genesis?.data?.tokenId || null;
|
|
9389
|
+
stateHash = getCurrentStateHash(txf) || "";
|
|
9390
|
+
if (!stateHash) {
|
|
9391
|
+
if (txf.state?.hash) {
|
|
9392
|
+
stateHash = txf.state.hash;
|
|
9393
|
+
} else if (txf.stateHash) {
|
|
9394
|
+
stateHash = txf.stateHash;
|
|
9395
|
+
} else if (txf.currentStateHash) {
|
|
9396
|
+
stateHash = txf.currentStateHash;
|
|
9397
|
+
}
|
|
9423
9398
|
}
|
|
9399
|
+
} catch {
|
|
9424
9400
|
}
|
|
9425
|
-
|
|
9401
|
+
entry = { tokenId, stateHash };
|
|
9426
9402
|
}
|
|
9427
|
-
const entry = { tokenId, stateHash };
|
|
9428
9403
|
if (sdkDataCache.size >= SDK_DATA_CACHE_MAX) {
|
|
9429
9404
|
sdkDataCache.clear();
|
|
9430
9405
|
}
|
|
@@ -9701,6 +9676,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
9701
9676
|
);
|
|
9702
9677
|
this.deps = deps;
|
|
9703
9678
|
this.priceProvider = deps.price ?? null;
|
|
9679
|
+
this.spendPlanner.setEngine(deps.tokenEngine);
|
|
9704
9680
|
if (this.l1) {
|
|
9705
9681
|
this.l1.initialize({
|
|
9706
9682
|
identity: deps.identity,
|
|
@@ -9925,7 +9901,59 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
9925
9901
|
request.invoiceRefundAddress,
|
|
9926
9902
|
request.invoiceContact
|
|
9927
9903
|
);
|
|
9928
|
-
if (
|
|
9904
|
+
if (this.deps?.tokenEngine && peerInfo?.chainPubkey) {
|
|
9905
|
+
const engine = this.deps.tokenEngine;
|
|
9906
|
+
const recipientChainPubkey = hexToBytes2(peerInfo.chainPubkey);
|
|
9907
|
+
const memoData = onChainMessage ?? void 0;
|
|
9908
|
+
const handToRecipient = async (finished) => {
|
|
9909
|
+
const tokenBlob = bytesToHex3(encodeTokenBlob(engine.encodeToken(finished)));
|
|
9910
|
+
await this.deps.transport.sendTokenTransfer(recipientPubkey, {
|
|
9911
|
+
type: "V2_TRANSFER",
|
|
9912
|
+
version: "2.0",
|
|
9913
|
+
tokenBlob,
|
|
9914
|
+
memo: request.memo
|
|
9915
|
+
});
|
|
9916
|
+
};
|
|
9917
|
+
for (const tw of splitPlan.tokensToTransferDirectly) {
|
|
9918
|
+
const finished = await engine.transfer({
|
|
9919
|
+
token: tw.sdkToken,
|
|
9920
|
+
recipientPubkey: recipientChainPubkey,
|
|
9921
|
+
data: memoData
|
|
9922
|
+
});
|
|
9923
|
+
await handToRecipient(finished);
|
|
9924
|
+
result.tokenTransfers.push({ sourceTokenId: tw.uiToken.id, method: "direct" });
|
|
9925
|
+
await this.removeToken(tw.uiToken.id, result.id);
|
|
9926
|
+
}
|
|
9927
|
+
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
9928
|
+
const selfChainPubkey = hexToBytes2(this.deps.identity.chainPubkey);
|
|
9929
|
+
const { outputs } = await engine.split({
|
|
9930
|
+
token: splitPlan.tokenToSplit.sdkToken,
|
|
9931
|
+
outputs: [
|
|
9932
|
+
{ recipientPubkey: recipientChainPubkey, coinId: request.coinId, amount: splitPlan.splitAmount, data: memoData },
|
|
9933
|
+
{ recipientPubkey: selfChainPubkey, coinId: request.coinId, amount: splitPlan.remainderAmount }
|
|
9934
|
+
]
|
|
9935
|
+
});
|
|
9936
|
+
await handToRecipient(outputs[0]);
|
|
9937
|
+
const changeToken = outputs[1];
|
|
9938
|
+
const changeBlob = bytesToHex3(encodeTokenBlob(engine.encodeToken(changeToken)));
|
|
9939
|
+
const changeInfo = await parseTokenInfo(changeBlob, engine);
|
|
9940
|
+
const registry = TokenRegistry.getInstance();
|
|
9941
|
+
await this.addToken({
|
|
9942
|
+
id: `v2_${engine.tokenId(changeToken)}`,
|
|
9943
|
+
coinId: changeInfo.coinId,
|
|
9944
|
+
symbol: registry.getSymbol(changeInfo.coinId) || changeInfo.symbol,
|
|
9945
|
+
name: registry.getName(changeInfo.coinId) || changeInfo.name,
|
|
9946
|
+
decimals: registry.getDecimals(changeInfo.coinId) ?? changeInfo.decimals,
|
|
9947
|
+
amount: changeInfo.amount,
|
|
9948
|
+
status: "confirmed",
|
|
9949
|
+
createdAt: Date.now(),
|
|
9950
|
+
updatedAt: Date.now(),
|
|
9951
|
+
sdkData: changeBlob
|
|
9952
|
+
});
|
|
9953
|
+
result.tokenTransfers.push({ sourceTokenId: splitPlan.tokenToSplit.uiToken.id, method: "split" });
|
|
9954
|
+
await this.removeToken(splitPlan.tokenToSplit.uiToken.id, result.id);
|
|
9955
|
+
}
|
|
9956
|
+
} else if (transferMode === "conservative") {
|
|
9929
9957
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
9930
9958
|
logger.debug("Payments", "Executing conservative split...");
|
|
9931
9959
|
const splitExecutor = new TokenSplitExecutor({
|
|
@@ -10521,7 +10549,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
10521
10549
|
* because bundle-level dedup protects against replays, and split children share genesis IDs.
|
|
10522
10550
|
*/
|
|
10523
10551
|
async saveCommitmentOnlyToken(sourceTokenInput, commitmentInput, senderPubkey, deferPersistence = false, skipGenesisDedup = false) {
|
|
10524
|
-
const tokenInfo = await parseTokenInfo(sourceTokenInput);
|
|
10552
|
+
const tokenInfo = await parseTokenInfo(sourceTokenInput, this.deps?.tokenEngine);
|
|
10525
10553
|
const sdkData = typeof sourceTokenInput === "string" ? sourceTokenInput : JSON.stringify(sourceTokenInput);
|
|
10526
10554
|
const nostrTokenId = extractTokenIdFromSdkData(sdkData);
|
|
10527
10555
|
const nostrStateHash = extractStateHashFromSdkData(sdkData);
|
|
@@ -11613,8 +11641,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11613
11641
|
if (pending2.stage === "RECEIVED") {
|
|
11614
11642
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: RECEIVED \u2192 submitting mint commitment...`);
|
|
11615
11643
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11616
|
-
const mintData = await
|
|
11617
|
-
const mintCommitment = await
|
|
11644
|
+
const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
|
|
11645
|
+
const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
|
|
11618
11646
|
const mintResponse = await stClient.submitMintCommitment(mintCommitment);
|
|
11619
11647
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint response status=${mintResponse.status}`);
|
|
11620
11648
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
@@ -11626,8 +11654,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11626
11654
|
if (pending2.stage === "MINT_SUBMITTED") {
|
|
11627
11655
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_SUBMITTED \u2192 checking mint proof...`);
|
|
11628
11656
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11629
|
-
const mintData = await
|
|
11630
|
-
const mintCommitment = await
|
|
11657
|
+
const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
|
|
11658
|
+
const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
|
|
11631
11659
|
const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);
|
|
11632
11660
|
if (!proof) {
|
|
11633
11661
|
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof not yet available, staying MINT_SUBMITTED`);
|
|
@@ -11724,10 +11752,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11724
11752
|
*/
|
|
11725
11753
|
async finalizeFromV5Bundle(bundle, pending2, signingService, stClient, trustBase) {
|
|
11726
11754
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
11727
|
-
const mintData = await
|
|
11728
|
-
const mintCommitment = await
|
|
11755
|
+
const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
|
|
11756
|
+
const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
|
|
11729
11757
|
const mintProofJson = JSON.parse(pending2.mintProofJson);
|
|
11730
|
-
const mintProof =
|
|
11758
|
+
const mintProof = import_InclusionProof2.InclusionProof.fromJSON(mintProofJson);
|
|
11731
11759
|
const mintTransaction = mintCommitment.toTransaction(mintProof);
|
|
11732
11760
|
const tokenType = new import_TokenType3.TokenType(fromHex4(bundle.tokenTypeHex));
|
|
11733
11761
|
const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);
|
|
@@ -11744,14 +11772,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11744
11772
|
const transferProof = await (0, import_InclusionProofUtils5.waitInclusionProof)(trustBase, stClient, transferCommitment);
|
|
11745
11773
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
11746
11774
|
const transferSalt = fromHex4(bundle.transferSaltHex);
|
|
11747
|
-
const recipientPredicate = await
|
|
11775
|
+
const recipientPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
|
|
11748
11776
|
mintData.tokenId,
|
|
11749
11777
|
tokenType,
|
|
11750
11778
|
signingService,
|
|
11751
11779
|
import_HashAlgorithm5.HashAlgorithm.SHA256,
|
|
11752
11780
|
transferSalt
|
|
11753
11781
|
);
|
|
11754
|
-
const recipientState = new
|
|
11782
|
+
const recipientState = new import_TokenState4.TokenState(recipientPredicate, null);
|
|
11755
11783
|
let nametagTokens = [];
|
|
11756
11784
|
const recipientAddressStr = bundle.recipientAddressJson;
|
|
11757
11785
|
if (recipientAddressStr.startsWith("PROXY://")) {
|
|
@@ -12488,68 +12516,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12488
12516
|
}
|
|
12489
12517
|
}
|
|
12490
12518
|
}
|
|
12491
|
-
/**
|
|
12492
|
-
* Mint a nametag token on-chain (like Sphere wallet and lottery)
|
|
12493
|
-
* This creates the nametag token required for receiving tokens via PROXY addresses
|
|
12494
|
-
*
|
|
12495
|
-
* @param nametag - The nametag to mint (e.g., "alice" or "@alice")
|
|
12496
|
-
* @returns MintNametagResult with success status and token if successful
|
|
12497
|
-
*/
|
|
12498
|
-
async mintNametag(nametag) {
|
|
12499
|
-
this.ensureInitialized();
|
|
12500
|
-
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
12501
|
-
if (!stClient) {
|
|
12502
|
-
return {
|
|
12503
|
-
success: false,
|
|
12504
|
-
error: "State transition client not available. Oracle provider must implement getStateTransitionClient()"
|
|
12505
|
-
};
|
|
12506
|
-
}
|
|
12507
|
-
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
12508
|
-
if (!trustBase) {
|
|
12509
|
-
return {
|
|
12510
|
-
success: false,
|
|
12511
|
-
error: "Trust base not available. Oracle provider must implement getTrustBase()"
|
|
12512
|
-
};
|
|
12513
|
-
}
|
|
12514
|
-
try {
|
|
12515
|
-
const signingService = await this.createSigningService();
|
|
12516
|
-
const { UnmaskedPredicateReference: UnmaskedPredicateReference4 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12517
|
-
const { TokenType: TokenType6 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
12518
|
-
const UNICITY_TOKEN_TYPE_HEX3 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
12519
|
-
const tokenType = new TokenType6(Buffer.from(UNICITY_TOKEN_TYPE_HEX3, "hex"));
|
|
12520
|
-
const addressRef = await UnmaskedPredicateReference4.create(
|
|
12521
|
-
tokenType,
|
|
12522
|
-
signingService.algorithm,
|
|
12523
|
-
signingService.publicKey,
|
|
12524
|
-
import_HashAlgorithm5.HashAlgorithm.SHA256
|
|
12525
|
-
);
|
|
12526
|
-
const ownerAddress = await addressRef.toAddress();
|
|
12527
|
-
const minter = new NametagMinter({
|
|
12528
|
-
stateTransitionClient: stClient,
|
|
12529
|
-
trustBase,
|
|
12530
|
-
signingService,
|
|
12531
|
-
debug: this.moduleConfig.debug
|
|
12532
|
-
});
|
|
12533
|
-
const result = await minter.mintNametag(nametag, ownerAddress);
|
|
12534
|
-
if (result.success && result.nametagData) {
|
|
12535
|
-
await this.setNametag(result.nametagData);
|
|
12536
|
-
logger.debug("Payments", `Unicity ID minted and saved: ${result.nametagData.name}`);
|
|
12537
|
-
this.deps.emitEvent("nametag:registered", {
|
|
12538
|
-
nametag: result.nametagData.name,
|
|
12539
|
-
addressIndex: 0
|
|
12540
|
-
// Primary address
|
|
12541
|
-
});
|
|
12542
|
-
}
|
|
12543
|
-
return result;
|
|
12544
|
-
} catch (error) {
|
|
12545
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
12546
|
-
logger.debug("Payments", "mintNametag failed:", errorMsg);
|
|
12547
|
-
return {
|
|
12548
|
-
success: false,
|
|
12549
|
-
error: errorMsg
|
|
12550
|
-
};
|
|
12551
|
-
}
|
|
12552
|
-
}
|
|
12553
12519
|
/**
|
|
12554
12520
|
* Mint a fungible token directly to this wallet (genesis mint).
|
|
12555
12521
|
*
|
|
@@ -12590,18 +12556,18 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12590
12556
|
}
|
|
12591
12557
|
try {
|
|
12592
12558
|
const signingService = await this.createSigningService();
|
|
12593
|
-
const { TokenId:
|
|
12559
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
12594
12560
|
const { TokenCoinData: TokenCoinData3 } = await import("@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData");
|
|
12595
|
-
const { UnmaskedPredicateReference:
|
|
12561
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12596
12562
|
const tokenTypeBytes = fromHex4("f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509");
|
|
12597
12563
|
const tokenType = new import_TokenType3.TokenType(tokenTypeBytes);
|
|
12598
12564
|
const tokenIdBytes = new Uint8Array(32);
|
|
12599
12565
|
crypto.getRandomValues(tokenIdBytes);
|
|
12600
|
-
const tokenId = new
|
|
12566
|
+
const tokenId = new TokenId4(tokenIdBytes);
|
|
12601
12567
|
const coinIdBytes = fromHex4(coinIdHex);
|
|
12602
12568
|
const coinId = new import_CoinId4.CoinId(coinIdBytes);
|
|
12603
12569
|
const coinData = TokenCoinData3.create([[coinId, amount]]);
|
|
12604
|
-
const addressRef = await
|
|
12570
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
12605
12571
|
tokenType,
|
|
12606
12572
|
signingService.algorithm,
|
|
12607
12573
|
signingService.publicKey,
|
|
@@ -12610,7 +12576,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12610
12576
|
const ownerAddress = await addressRef.toAddress();
|
|
12611
12577
|
const salt = new Uint8Array(32);
|
|
12612
12578
|
crypto.getRandomValues(salt);
|
|
12613
|
-
const mintData = await
|
|
12579
|
+
const mintData = await import_MintTransactionData2.MintTransactionData.create(
|
|
12614
12580
|
tokenId,
|
|
12615
12581
|
tokenType,
|
|
12616
12582
|
null,
|
|
@@ -12625,7 +12591,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12625
12591
|
null
|
|
12626
12592
|
// reason: null (genesis, no burn predecessor)
|
|
12627
12593
|
);
|
|
12628
|
-
const commitment = await
|
|
12594
|
+
const commitment = await import_MintCommitment2.MintCommitment.create(mintData);
|
|
12629
12595
|
const MAX_RETRIES = 3;
|
|
12630
12596
|
let lastStatus;
|
|
12631
12597
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
@@ -12642,14 +12608,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12642
12608
|
}
|
|
12643
12609
|
const inclusionProof = await (0, import_InclusionProofUtils5.waitInclusionProof)(trustBase, stClient, commitment);
|
|
12644
12610
|
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
12645
|
-
const predicate = await
|
|
12611
|
+
const predicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
|
|
12646
12612
|
tokenId,
|
|
12647
12613
|
tokenType,
|
|
12648
12614
|
signingService,
|
|
12649
12615
|
import_HashAlgorithm5.HashAlgorithm.SHA256,
|
|
12650
12616
|
salt
|
|
12651
12617
|
);
|
|
12652
|
-
const tokenState = new
|
|
12618
|
+
const tokenState = new import_TokenState4.TokenState(predicate, null);
|
|
12653
12619
|
const sdkToken = await import_Token6.Token.mint(trustBase, tokenState, genesisTransaction);
|
|
12654
12620
|
const tokenIdHex = tokenId.toJSON();
|
|
12655
12621
|
const symbol = this.getCoinSymbol(coinIdHex);
|
|
@@ -12676,29 +12642,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12676
12642
|
return { success: false, error: `Local mint failed: ${msg}` };
|
|
12677
12643
|
}
|
|
12678
12644
|
}
|
|
12679
|
-
/**
|
|
12680
|
-
* Check if a nametag is available for minting
|
|
12681
|
-
* @param nametag - The nametag to check (e.g., "alice" or "@alice")
|
|
12682
|
-
*/
|
|
12683
|
-
async isNametagAvailable(nametag) {
|
|
12684
|
-
this.ensureInitialized();
|
|
12685
|
-
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
12686
|
-
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
12687
|
-
if (!stClient || !trustBase) {
|
|
12688
|
-
return false;
|
|
12689
|
-
}
|
|
12690
|
-
try {
|
|
12691
|
-
const signingService = await this.createSigningService();
|
|
12692
|
-
const minter = new NametagMinter({
|
|
12693
|
-
stateTransitionClient: stClient,
|
|
12694
|
-
trustBase,
|
|
12695
|
-
signingService
|
|
12696
|
-
});
|
|
12697
|
-
return await minter.isNametagAvailable(nametag);
|
|
12698
|
-
} catch {
|
|
12699
|
-
return false;
|
|
12700
|
-
}
|
|
12701
|
-
}
|
|
12702
12645
|
// ===========================================================================
|
|
12703
12646
|
// Public API - Sync & Validate
|
|
12704
12647
|
// ===========================================================================
|
|
@@ -13014,7 +12957,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13014
12957
|
const privateKeyBytes = new Uint8Array(
|
|
13015
12958
|
privateKeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
|
13016
12959
|
);
|
|
13017
|
-
return
|
|
12960
|
+
return import_SigningService2.SigningService.createFromSecret(privateKeyBytes);
|
|
13018
12961
|
}
|
|
13019
12962
|
/**
|
|
13020
12963
|
* Get the wallet's signing public key (used for token ownership predicates).
|
|
@@ -13029,14 +12972,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13029
12972
|
* Create DirectAddress from a public key using UnmaskedPredicateReference
|
|
13030
12973
|
*/
|
|
13031
12974
|
async createDirectAddressFromPubkey(pubkeyHex) {
|
|
13032
|
-
const { UnmaskedPredicateReference:
|
|
13033
|
-
const { TokenType:
|
|
13034
|
-
const
|
|
13035
|
-
const tokenType = new
|
|
12975
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
12976
|
+
const { TokenType: TokenType4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
12977
|
+
const UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
12978
|
+
const tokenType = new TokenType4(Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex"));
|
|
13036
12979
|
const pubkeyBytes = new Uint8Array(
|
|
13037
12980
|
pubkeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
|
13038
12981
|
);
|
|
13039
|
-
const addressRef = await
|
|
12982
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
13040
12983
|
tokenType,
|
|
13041
12984
|
"secp256k1",
|
|
13042
12985
|
pubkeyBytes,
|
|
@@ -13048,10 +12991,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13048
12991
|
* Resolve recipient to IAddress for L3 transfers.
|
|
13049
12992
|
* Uses pre-resolved PeerInfo when available to avoid redundant network queries.
|
|
13050
12993
|
*/
|
|
13051
|
-
async resolveRecipientAddress(recipient,
|
|
12994
|
+
async resolveRecipientAddress(recipient, _addressMode = "auto", peerInfo) {
|
|
13052
12995
|
const { AddressFactory } = await import("@unicitylabs/state-transition-sdk/lib/address/AddressFactory");
|
|
13053
|
-
|
|
13054
|
-
if (recipient.startsWith("PROXY:") || recipient.startsWith("DIRECT:")) {
|
|
12996
|
+
if (recipient.startsWith("DIRECT:")) {
|
|
13055
12997
|
return AddressFactory.createAddress(recipient);
|
|
13056
12998
|
}
|
|
13057
12999
|
if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {
|
|
@@ -13061,28 +13003,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13061
13003
|
const info = peerInfo ?? await this.deps?.transport.resolve?.(recipient) ?? null;
|
|
13062
13004
|
if (!info) {
|
|
13063
13005
|
throw new SphereError(
|
|
13064
|
-
`Recipient "${recipient}" not found. Use @nametag, a
|
|
13006
|
+
`Recipient "${recipient}" not found. Use @nametag, a DIRECT: address, or a 33-byte hex pubkey.`,
|
|
13065
13007
|
"INVALID_RECIPIENT"
|
|
13066
13008
|
);
|
|
13067
13009
|
}
|
|
13068
13010
|
const nametag = recipient.startsWith("@") ? recipient.slice(1) : info.nametag || recipient;
|
|
13069
|
-
if (
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
if (!info.directAddress) {
|
|
13075
|
-
throw new SphereError(`"${nametag}" has no DirectAddress stored. It may be a legacy registration.`, "INVALID_RECIPIENT");
|
|
13076
|
-
}
|
|
13077
|
-
logger.debug("Payments", `Using DirectAddress for "${nametag}" (forced): ${info.directAddress.slice(0, 30)}...`);
|
|
13078
|
-
return AddressFactory.createAddress(info.directAddress);
|
|
13079
|
-
}
|
|
13080
|
-
if (info.directAddress) {
|
|
13081
|
-
logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
|
|
13082
|
-
return AddressFactory.createAddress(info.directAddress);
|
|
13011
|
+
if (!info.directAddress) {
|
|
13012
|
+
throw new SphereError(
|
|
13013
|
+
`"${nametag}" has no DirectAddress \u2014 the recipient must publish a key-based identity binding.`,
|
|
13014
|
+
"INVALID_RECIPIENT"
|
|
13015
|
+
);
|
|
13083
13016
|
}
|
|
13084
|
-
logger.debug("Payments", `Using
|
|
13085
|
-
return
|
|
13017
|
+
logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
|
|
13018
|
+
return AddressFactory.createAddress(info.directAddress);
|
|
13086
13019
|
}
|
|
13087
13020
|
/**
|
|
13088
13021
|
* Handle NOSTR-FIRST commitment-only transfer (recipient side)
|
|
@@ -13137,14 +13070,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13137
13070
|
const addressScheme = recipientAddress.scheme;
|
|
13138
13071
|
const signingService = await this.createSigningService();
|
|
13139
13072
|
const transferSalt = transferTx.data.salt;
|
|
13140
|
-
const recipientPredicate = await
|
|
13073
|
+
const recipientPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
|
|
13141
13074
|
sourceToken.id,
|
|
13142
13075
|
sourceToken.type,
|
|
13143
13076
|
signingService,
|
|
13144
13077
|
import_HashAlgorithm5.HashAlgorithm.SHA256,
|
|
13145
13078
|
transferSalt
|
|
13146
13079
|
);
|
|
13147
|
-
const recipientState = new
|
|
13080
|
+
const recipientState = new import_TokenState4.TokenState(recipientPredicate, null);
|
|
13148
13081
|
let nametagTokens = [];
|
|
13149
13082
|
if (addressScheme === import_AddressScheme.AddressScheme.PROXY) {
|
|
13150
13083
|
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
@@ -13241,6 +13174,67 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13241
13174
|
}
|
|
13242
13175
|
}
|
|
13243
13176
|
}
|
|
13177
|
+
/**
|
|
13178
|
+
* v2 engine transfer (sender-driven): the sender handed us a FINISHED token.
|
|
13179
|
+
* Decode the blob, dedup by the genesis-stable token id, store it as a
|
|
13180
|
+
* confirmed token, and emit/record the receipt. No commitment / inclusion-proof
|
|
13181
|
+
* / finalization round-trip (contrast the v1 sourceToken+transferTx path).
|
|
13182
|
+
*/
|
|
13183
|
+
async handleV2Transfer(payload, senderPubkey) {
|
|
13184
|
+
this.ensureInitialized();
|
|
13185
|
+
if (!this.loaded && this.loadedPromise) {
|
|
13186
|
+
await this.loadedPromise;
|
|
13187
|
+
}
|
|
13188
|
+
const engine = this.deps.tokenEngine;
|
|
13189
|
+
if (!engine) return;
|
|
13190
|
+
let token;
|
|
13191
|
+
try {
|
|
13192
|
+
token = await engine.decodeToken(decodeTokenBlob(hexToBytes2(payload.tokenBlob)));
|
|
13193
|
+
} catch (err) {
|
|
13194
|
+
logger.error("Payments", "V2 transfer: failed to decode token blob:", err);
|
|
13195
|
+
return;
|
|
13196
|
+
}
|
|
13197
|
+
const id = `v2_${engine.tokenId(token)}`;
|
|
13198
|
+
if (this.tokens.has(id)) {
|
|
13199
|
+
logger.debug("Payments", `V2 transfer ${id.slice(0, 16)}... already present, skipping`);
|
|
13200
|
+
return;
|
|
13201
|
+
}
|
|
13202
|
+
const info = await parseTokenInfo(payload.tokenBlob, engine);
|
|
13203
|
+
const registry = TokenRegistry.getInstance();
|
|
13204
|
+
const uiToken = {
|
|
13205
|
+
id,
|
|
13206
|
+
coinId: info.coinId,
|
|
13207
|
+
symbol: registry.getSymbol(info.coinId) || info.symbol,
|
|
13208
|
+
name: registry.getName(info.coinId) || info.name,
|
|
13209
|
+
decimals: registry.getDecimals(info.coinId) ?? info.decimals,
|
|
13210
|
+
amount: info.amount,
|
|
13211
|
+
status: "confirmed",
|
|
13212
|
+
createdAt: Date.now(),
|
|
13213
|
+
updatedAt: Date.now(),
|
|
13214
|
+
sdkData: payload.tokenBlob
|
|
13215
|
+
};
|
|
13216
|
+
await this.addToken(uiToken);
|
|
13217
|
+
const senderInfo = await this.resolveSenderInfo(senderPubkey);
|
|
13218
|
+
this.deps.emitEvent("transfer:incoming", {
|
|
13219
|
+
id,
|
|
13220
|
+
senderPubkey,
|
|
13221
|
+
senderNametag: senderInfo.senderNametag,
|
|
13222
|
+
tokens: [uiToken],
|
|
13223
|
+
memo: payload.memo,
|
|
13224
|
+
receivedAt: Date.now()
|
|
13225
|
+
});
|
|
13226
|
+
await this.addToHistory({
|
|
13227
|
+
type: "RECEIVED",
|
|
13228
|
+
amount: info.amount,
|
|
13229
|
+
coinId: info.coinId,
|
|
13230
|
+
symbol: uiToken.symbol,
|
|
13231
|
+
timestamp: Date.now(),
|
|
13232
|
+
senderPubkey,
|
|
13233
|
+
...senderInfo,
|
|
13234
|
+
memo: payload.memo,
|
|
13235
|
+
tokenId: id
|
|
13236
|
+
});
|
|
13237
|
+
}
|
|
13244
13238
|
async handleIncomingTransfer(transfer) {
|
|
13245
13239
|
if (!this.loaded && this.loadedPromise) {
|
|
13246
13240
|
await this.loadedPromise;
|
|
@@ -13248,6 +13242,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13248
13242
|
try {
|
|
13249
13243
|
const payload = transfer.payload;
|
|
13250
13244
|
logger.debug("Payments", "handleIncomingTransfer: keys=", Object.keys(payload).join(","));
|
|
13245
|
+
if (this.deps.tokenEngine && isV2TransferPayload(transfer.payload)) {
|
|
13246
|
+
await this.handleV2Transfer(transfer.payload, transfer.senderTransportPubkey);
|
|
13247
|
+
return;
|
|
13248
|
+
}
|
|
13251
13249
|
let combinedBundle = null;
|
|
13252
13250
|
if (isCombinedTransferBundleV6(payload)) {
|
|
13253
13251
|
combinedBundle = payload;
|
|
@@ -13329,7 +13327,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13329
13327
|
const hasTransactionData = transferTxInput.transactionData !== void 0;
|
|
13330
13328
|
const hasAuthenticator = transferTxInput.authenticator !== void 0;
|
|
13331
13329
|
if (hasData && hasInclusionProof) {
|
|
13332
|
-
transferTx = await
|
|
13330
|
+
transferTx = await import_TransferTransaction3.TransferTransaction.fromJSON(transferTxInput);
|
|
13333
13331
|
} else if (hasTransactionData && hasAuthenticator) {
|
|
13334
13332
|
const commitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferTxInput);
|
|
13335
13333
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
@@ -13350,7 +13348,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13350
13348
|
transferTx = commitment.toTransaction(inclusionProof);
|
|
13351
13349
|
} else {
|
|
13352
13350
|
try {
|
|
13353
|
-
transferTx = await
|
|
13351
|
+
transferTx = await import_TransferTransaction3.TransferTransaction.fromJSON(transferTxInput);
|
|
13354
13352
|
} catch {
|
|
13355
13353
|
const commitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferTxInput);
|
|
13356
13354
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
@@ -13392,7 +13390,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13392
13390
|
logger.warn("Payments", "Received invalid token");
|
|
13393
13391
|
return;
|
|
13394
13392
|
}
|
|
13395
|
-
const tokenInfo = await parseTokenInfo(tokenData);
|
|
13393
|
+
const tokenInfo = await parseTokenInfo(tokenData, this.deps?.tokenEngine);
|
|
13396
13394
|
const token = {
|
|
13397
13395
|
id: tokenInfo.tokenId ?? crypto.randomUUID(),
|
|
13398
13396
|
coinId: tokenInfo.coinId,
|
|
@@ -13698,25 +13696,7 @@ function createPaymentsModule(config) {
|
|
|
13698
13696
|
return new PaymentsModule(config);
|
|
13699
13697
|
}
|
|
13700
13698
|
|
|
13701
|
-
// modules/
|
|
13702
|
-
init_logger();
|
|
13703
|
-
var import_Token7 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
13704
|
-
var import_CoinId5 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
|
|
13705
|
-
|
|
13706
|
-
// modules/payments/BackgroundCommitmentService.ts
|
|
13707
|
-
init_logger();
|
|
13708
|
-
init_errors();
|
|
13709
|
-
|
|
13710
|
-
// modules/payments/TokenRecoveryService.ts
|
|
13711
|
-
init_logger();
|
|
13712
|
-
var import_TokenId4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
13713
|
-
var import_TokenState6 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
13714
|
-
var import_TokenType4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
13715
|
-
var import_CoinId6 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
|
|
13716
|
-
var import_HashAlgorithm6 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
|
|
13717
|
-
var import_UnmaskedPredicate6 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
|
|
13718
|
-
|
|
13719
|
-
// modules/communications/CommunicationsModule.ts
|
|
13699
|
+
// modules/communications/CommunicationsModule.ts
|
|
13720
13700
|
init_logger();
|
|
13721
13701
|
init_errors();
|
|
13722
13702
|
|
|
@@ -14397,7 +14377,7 @@ function createCommunicationsModule(config) {
|
|
|
14397
14377
|
}
|
|
14398
14378
|
|
|
14399
14379
|
// modules/groupchat/GroupChatModule.ts
|
|
14400
|
-
var
|
|
14380
|
+
var import_nostr_js_sdk3 = require("@unicitylabs/nostr-js-sdk");
|
|
14401
14381
|
init_logger();
|
|
14402
14382
|
init_errors();
|
|
14403
14383
|
init_constants();
|
|
@@ -14415,7 +14395,7 @@ var GroupVisibility = {
|
|
|
14415
14395
|
|
|
14416
14396
|
// modules/groupchat/GroupChatModule.ts
|
|
14417
14397
|
function createNip29Filter(data) {
|
|
14418
|
-
return new
|
|
14398
|
+
return new import_nostr_js_sdk3.Filter(data);
|
|
14419
14399
|
}
|
|
14420
14400
|
var GroupChatModule = class {
|
|
14421
14401
|
config;
|
|
@@ -14464,7 +14444,7 @@ var GroupChatModule = class {
|
|
|
14464
14444
|
}
|
|
14465
14445
|
this.deps = deps;
|
|
14466
14446
|
const secretKey = Buffer.from(deps.identity.privateKey, "hex");
|
|
14467
|
-
this.keyManager =
|
|
14447
|
+
this.keyManager = import_nostr_js_sdk3.NostrKeyManager.fromPrivateKey(secretKey);
|
|
14468
14448
|
}
|
|
14469
14449
|
async load() {
|
|
14470
14450
|
this.ensureInitialized();
|
|
@@ -14605,7 +14585,7 @@ var GroupChatModule = class {
|
|
|
14605
14585
|
}
|
|
14606
14586
|
this.subscriptionIds = [];
|
|
14607
14587
|
const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
|
|
14608
|
-
this.keyManager =
|
|
14588
|
+
this.keyManager = import_nostr_js_sdk3.NostrKeyManager.fromPrivateKey(secretKey);
|
|
14609
14589
|
if (this.groups.size === 0) {
|
|
14610
14590
|
await this.restoreJoinedGroups();
|
|
14611
14591
|
} else {
|
|
@@ -14617,13 +14597,13 @@ var GroupChatModule = class {
|
|
|
14617
14597
|
this.ensureInitialized();
|
|
14618
14598
|
if (!this.keyManager) {
|
|
14619
14599
|
const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
|
|
14620
|
-
this.keyManager =
|
|
14600
|
+
this.keyManager = import_nostr_js_sdk3.NostrKeyManager.fromPrivateKey(secretKey);
|
|
14621
14601
|
}
|
|
14622
14602
|
const primaryRelay = this.config.relays[0];
|
|
14623
14603
|
if (primaryRelay) {
|
|
14624
14604
|
await this.checkAndClearOnRelayChange(primaryRelay);
|
|
14625
14605
|
}
|
|
14626
|
-
this.client = new
|
|
14606
|
+
this.client = new import_nostr_js_sdk3.NostrClient(this.keyManager);
|
|
14627
14607
|
try {
|
|
14628
14608
|
await this.client.connect(...this.config.relays);
|
|
14629
14609
|
this.connected = true;
|
|
@@ -14910,7 +14890,7 @@ var GroupChatModule = class {
|
|
|
14910
14890
|
if (!this.client) return [];
|
|
14911
14891
|
const groupsMap = /* @__PURE__ */ new Map();
|
|
14912
14892
|
await this.oneshotSubscription(
|
|
14913
|
-
new
|
|
14893
|
+
new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_METADATA] }),
|
|
14914
14894
|
{
|
|
14915
14895
|
onEvent: (event) => {
|
|
14916
14896
|
const group = this.parseGroupMetadata(event);
|
|
@@ -15410,7 +15390,7 @@ var GroupChatModule = class {
|
|
|
15410
15390
|
if (!this.client) return /* @__PURE__ */ new Set();
|
|
15411
15391
|
const adminPubkeys = /* @__PURE__ */ new Set();
|
|
15412
15392
|
return this.oneshotSubscription(
|
|
15413
|
-
new
|
|
15393
|
+
new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": ["", "_"] }),
|
|
15414
15394
|
{
|
|
15415
15395
|
onEvent: (event) => {
|
|
15416
15396
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -15432,7 +15412,7 @@ var GroupChatModule = class {
|
|
|
15432
15412
|
if (!this.client) return null;
|
|
15433
15413
|
let result = null;
|
|
15434
15414
|
return this.oneshotSubscription(
|
|
15435
|
-
new
|
|
15415
|
+
new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_METADATA], "#d": [groupId] }),
|
|
15436
15416
|
{
|
|
15437
15417
|
onEvent: (event) => {
|
|
15438
15418
|
if (!result) result = this.parseGroupMetadata(event);
|
|
@@ -15469,7 +15449,7 @@ var GroupChatModule = class {
|
|
|
15469
15449
|
if (!this.client) return [];
|
|
15470
15450
|
const members = [];
|
|
15471
15451
|
return this.oneshotSubscription(
|
|
15472
|
-
new
|
|
15452
|
+
new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#d": [groupId] }),
|
|
15473
15453
|
{
|
|
15474
15454
|
onEvent: (event) => {
|
|
15475
15455
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -15490,7 +15470,7 @@ var GroupChatModule = class {
|
|
|
15490
15470
|
if (!this.client) return [];
|
|
15491
15471
|
const adminPubkeys = [];
|
|
15492
15472
|
return this.oneshotSubscription(
|
|
15493
|
-
new
|
|
15473
|
+
new import_nostr_js_sdk3.Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": [groupId] }),
|
|
15494
15474
|
{
|
|
15495
15475
|
onEvent: (event) => {
|
|
15496
15476
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -17353,7 +17333,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17353
17333
|
const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : void 0;
|
|
17354
17334
|
return abytes(bytes, sizer);
|
|
17355
17335
|
}
|
|
17356
|
-
class
|
|
17336
|
+
class Signature2 {
|
|
17357
17337
|
r;
|
|
17358
17338
|
s;
|
|
17359
17339
|
recovery;
|
|
@@ -17373,7 +17353,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17373
17353
|
let recid;
|
|
17374
17354
|
if (format === "der") {
|
|
17375
17355
|
const { r: r2, s: s2 } = DER.toSig(abytes(bytes));
|
|
17376
|
-
return new
|
|
17356
|
+
return new Signature2(r2, s2);
|
|
17377
17357
|
}
|
|
17378
17358
|
if (format === "recovered") {
|
|
17379
17359
|
recid = bytes[0];
|
|
@@ -17383,7 +17363,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17383
17363
|
const L = lengths.signature / 2;
|
|
17384
17364
|
const r = bytes.subarray(0, L);
|
|
17385
17365
|
const s = bytes.subarray(L, L * 2);
|
|
17386
|
-
return new
|
|
17366
|
+
return new Signature2(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
17387
17367
|
}
|
|
17388
17368
|
static fromHex(hex, format) {
|
|
17389
17369
|
return this.fromBytes(hexToBytes(hex), format);
|
|
@@ -17395,7 +17375,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17395
17375
|
return recovery;
|
|
17396
17376
|
}
|
|
17397
17377
|
addRecoveryBit(recovery) {
|
|
17398
|
-
return new
|
|
17378
|
+
return new Signature2(this.r, this.s, recovery);
|
|
17399
17379
|
}
|
|
17400
17380
|
recoverPublicKey(messageHash) {
|
|
17401
17381
|
const { r, s } = this;
|
|
@@ -17487,7 +17467,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17487
17467
|
normS = Fn.neg(s);
|
|
17488
17468
|
recovery ^= 1;
|
|
17489
17469
|
}
|
|
17490
|
-
return new
|
|
17470
|
+
return new Signature2(r, normS, hasLargeCofactor ? void 0 : recovery);
|
|
17491
17471
|
}
|
|
17492
17472
|
return { seed, k2sig };
|
|
17493
17473
|
}
|
|
@@ -17502,12 +17482,12 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17502
17482
|
publicKey = abytes(publicKey, void 0, "publicKey");
|
|
17503
17483
|
message = validateMsgAndHash(message, prehash);
|
|
17504
17484
|
if (!isBytes(signature)) {
|
|
17505
|
-
const end = signature instanceof
|
|
17485
|
+
const end = signature instanceof Signature2 ? ", use sig.toBytes()" : "";
|
|
17506
17486
|
throw new Error("verify expects Uint8Array signature" + end);
|
|
17507
17487
|
}
|
|
17508
17488
|
validateSigLength(signature, format);
|
|
17509
17489
|
try {
|
|
17510
|
-
const sig =
|
|
17490
|
+
const sig = Signature2.fromBytes(signature, format);
|
|
17511
17491
|
const P = Point.fromBytes(publicKey);
|
|
17512
17492
|
if (lowS && sig.hasHighS())
|
|
17513
17493
|
return false;
|
|
@@ -17528,7 +17508,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17528
17508
|
function recoverPublicKey(signature, message, opts = {}) {
|
|
17529
17509
|
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|
17530
17510
|
message = validateMsgAndHash(message, prehash);
|
|
17531
|
-
return
|
|
17511
|
+
return Signature2.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes();
|
|
17532
17512
|
}
|
|
17533
17513
|
return Object.freeze({
|
|
17534
17514
|
keygen,
|
|
@@ -17540,7 +17520,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
17540
17520
|
sign,
|
|
17541
17521
|
verify,
|
|
17542
17522
|
recoverPublicKey,
|
|
17543
|
-
Signature,
|
|
17523
|
+
Signature: Signature2,
|
|
17544
17524
|
hash
|
|
17545
17525
|
});
|
|
17546
17526
|
}
|
|
@@ -18812,7 +18792,7 @@ function freezeCoinAsset(coinAsset, state, latestSender) {
|
|
|
18812
18792
|
}
|
|
18813
18793
|
|
|
18814
18794
|
// modules/accounting/AccountingModule.ts
|
|
18815
|
-
var
|
|
18795
|
+
var import_Token7 = require("@unicitylabs/state-transition-sdk/lib/token/Token.js");
|
|
18816
18796
|
var LOG_TAG2 = "Accounting";
|
|
18817
18797
|
var INV_LEDGER_PREFIX = "inv_ledger:";
|
|
18818
18798
|
var AccountingModule = class _AccountingModule {
|
|
@@ -19465,129 +19445,146 @@ var AccountingModule = class _AccountingModule {
|
|
|
19465
19445
|
);
|
|
19466
19446
|
}
|
|
19467
19447
|
try {
|
|
19468
|
-
|
|
19469
|
-
|
|
19470
|
-
const
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19475
|
-
|
|
19476
|
-
|
|
19477
|
-
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
19485
|
-
|
|
19486
|
-
|
|
19448
|
+
let invoiceId;
|
|
19449
|
+
let sdkData;
|
|
19450
|
+
const engine = deps.tokenEngine;
|
|
19451
|
+
if (engine) {
|
|
19452
|
+
const invoiceToken = await engine.mintDataToken({
|
|
19453
|
+
recipientPubkey: hexToBytes(deps.identity.chainPubkey),
|
|
19454
|
+
data: invoiceBytesEncoded,
|
|
19455
|
+
tokenType: hexToBytes(INVOICE_TOKEN_TYPE_HEX),
|
|
19456
|
+
salt
|
|
19457
|
+
});
|
|
19458
|
+
invoiceId = engine.tokenId(invoiceToken);
|
|
19459
|
+
if (this.invoiceTermsCache.has(invoiceId)) {
|
|
19460
|
+
throw new SphereError(`Invoice already exists locally: ${invoiceId}`, "INVOICE_ALREADY_EXISTS");
|
|
19461
|
+
}
|
|
19462
|
+
sdkData = bytesToHex(encodeTokenBlob(engine.encodeToken(invoiceToken)));
|
|
19463
|
+
} else {
|
|
19464
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
|
|
19465
|
+
const { TokenType: TokenType4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType.js");
|
|
19466
|
+
const { MintTransactionData: MintTransactionData3 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData.js");
|
|
19467
|
+
const { MintCommitment: MintCommitment3 } = await import("@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment.js");
|
|
19468
|
+
const { SigningService: SigningService3 } = await import("@unicitylabs/state-transition-sdk/lib/sign/SigningService.js");
|
|
19469
|
+
const { HashAlgorithm: HashAlgorithm6 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
|
|
19470
|
+
const { DataHasher: DataHasher2 } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19471
|
+
const { UnmaskedPredicate: UnmaskedPredicate5 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate.js");
|
|
19472
|
+
const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference.js");
|
|
19473
|
+
const { TokenState: TokenState5 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenState.js");
|
|
19474
|
+
const { Token: SdkToken4 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token.js");
|
|
19475
|
+
const { waitInclusionProof: waitInclusionProof6 } = await import("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js");
|
|
19476
|
+
const hash = await new DataHasher2(HashAlgorithm6.SHA256).update(invoiceBytesEncoded).digest();
|
|
19477
|
+
const invoiceTokenId = new TokenId4(hash.imprint);
|
|
19478
|
+
invoiceId = invoiceTokenId.toJSON();
|
|
19479
|
+
if (this.invoiceTermsCache.has(invoiceId)) {
|
|
19480
|
+
throw new SphereError(
|
|
19481
|
+
`Invoice already exists locally: ${invoiceId}`,
|
|
19482
|
+
"INVOICE_ALREADY_EXISTS"
|
|
19483
|
+
);
|
|
19484
|
+
}
|
|
19485
|
+
const invoiceTokenType = new TokenType4(
|
|
19486
|
+
Buffer.from(INVOICE_TOKEN_TYPE_HEX, "hex")
|
|
19487
19487
|
);
|
|
19488
|
-
|
|
19489
|
-
|
|
19490
|
-
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
19494
|
-
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
|
|
19498
|
-
|
|
19499
|
-
|
|
19500
|
-
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
|
|
19515
|
-
|
|
19516
|
-
|
|
19517
|
-
|
|
19518
|
-
|
|
19519
|
-
|
|
19520
|
-
|
|
19521
|
-
const MAX_RETRIES = 3;
|
|
19522
|
-
let submitSuccess = false;
|
|
19523
|
-
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
19524
|
-
try {
|
|
19525
|
-
if (this.config.debug) {
|
|
19526
|
-
logger.debug(
|
|
19527
|
-
LOG_TAG2,
|
|
19528
|
-
`Submitting invoice commitment (attempt ${attempt}/${MAX_RETRIES})...`
|
|
19529
|
-
);
|
|
19530
|
-
}
|
|
19531
|
-
const response = await stClient.submitMintCommitment(commitment);
|
|
19532
|
-
if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
|
|
19488
|
+
const signingService = await SigningService3.createFromSecret(signingKeyBytes);
|
|
19489
|
+
const addressRef = await UnmaskedPredicateReference3.create(
|
|
19490
|
+
invoiceTokenType,
|
|
19491
|
+
signingService.algorithm,
|
|
19492
|
+
signingService.publicKey,
|
|
19493
|
+
HashAlgorithm6.SHA256
|
|
19494
|
+
);
|
|
19495
|
+
const ownerAddress = await addressRef.toAddress();
|
|
19496
|
+
const mintData = await MintTransactionData3.create(
|
|
19497
|
+
invoiceTokenId,
|
|
19498
|
+
invoiceTokenType,
|
|
19499
|
+
invoiceBytesEncoded,
|
|
19500
|
+
// tokenData: serialized InvoiceTerms (UTF-8 JSON)
|
|
19501
|
+
null,
|
|
19502
|
+
// coinData: null (non-fungible invoice token)
|
|
19503
|
+
ownerAddress,
|
|
19504
|
+
salt,
|
|
19505
|
+
null,
|
|
19506
|
+
// recipientDataHash: null
|
|
19507
|
+
null
|
|
19508
|
+
// reason: null
|
|
19509
|
+
);
|
|
19510
|
+
if (this.config.debug) {
|
|
19511
|
+
logger.debug(LOG_TAG2, `Created MintTransactionData for invoice ${invoiceId}`);
|
|
19512
|
+
}
|
|
19513
|
+
const commitment = await MintCommitment3.create(mintData);
|
|
19514
|
+
if (this.config.debug) {
|
|
19515
|
+
logger.debug(LOG_TAG2, "Created MintCommitment for invoice");
|
|
19516
|
+
}
|
|
19517
|
+
const MAX_RETRIES = 3;
|
|
19518
|
+
let submitSuccess = false;
|
|
19519
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
19520
|
+
try {
|
|
19533
19521
|
if (this.config.debug) {
|
|
19534
19522
|
logger.debug(
|
|
19535
19523
|
LOG_TAG2,
|
|
19536
|
-
|
|
19524
|
+
`Submitting invoice commitment (attempt ${attempt}/${MAX_RETRIES})...`
|
|
19537
19525
|
);
|
|
19538
19526
|
}
|
|
19539
|
-
|
|
19540
|
-
|
|
19541
|
-
|
|
19542
|
-
|
|
19527
|
+
const response = await stClient.submitMintCommitment(commitment);
|
|
19528
|
+
if (response.status === "SUCCESS" || response.status === "REQUEST_ID_EXISTS") {
|
|
19529
|
+
if (this.config.debug) {
|
|
19530
|
+
logger.debug(
|
|
19531
|
+
LOG_TAG2,
|
|
19532
|
+
response.status === "REQUEST_ID_EXISTS" ? "Invoice commitment already exists (idempotent re-mint)" : "Invoice commitment submitted successfully"
|
|
19533
|
+
);
|
|
19534
|
+
}
|
|
19535
|
+
submitSuccess = true;
|
|
19536
|
+
break;
|
|
19537
|
+
} else {
|
|
19538
|
+
logger.warn(LOG_TAG2, `Invoice commitment submission failed: ${response.status}`);
|
|
19539
|
+
if (attempt === MAX_RETRIES) {
|
|
19540
|
+
throw new SphereError(
|
|
19541
|
+
`Failed to mint invoice token: commitment rejected after ${MAX_RETRIES} attempts: ${response.status}`,
|
|
19542
|
+
"INVOICE_MINT_FAILED"
|
|
19543
|
+
);
|
|
19544
|
+
}
|
|
19545
|
+
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19546
|
+
}
|
|
19547
|
+
} catch (retryErr) {
|
|
19548
|
+
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;
|
|
19549
|
+
logger.warn(LOG_TAG2, `Invoice commitment attempt ${attempt} error:`, retryErr);
|
|
19543
19550
|
if (attempt === MAX_RETRIES) {
|
|
19544
19551
|
throw new SphereError(
|
|
19545
|
-
`Failed to mint invoice token:
|
|
19546
|
-
"INVOICE_MINT_FAILED"
|
|
19552
|
+
`Failed to mint invoice token: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
|
|
19553
|
+
"INVOICE_MINT_FAILED",
|
|
19554
|
+
retryErr
|
|
19547
19555
|
);
|
|
19548
19556
|
}
|
|
19549
19557
|
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19550
19558
|
}
|
|
19551
|
-
} catch (retryErr) {
|
|
19552
|
-
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;
|
|
19553
|
-
logger.warn(LOG_TAG2, `Invoice commitment attempt ${attempt} error:`, retryErr);
|
|
19554
|
-
if (attempt === MAX_RETRIES) {
|
|
19555
|
-
throw new SphereError(
|
|
19556
|
-
`Failed to mint invoice token: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
|
|
19557
|
-
"INVOICE_MINT_FAILED",
|
|
19558
|
-
retryErr
|
|
19559
|
-
);
|
|
19560
|
-
}
|
|
19561
|
-
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19562
19559
|
}
|
|
19563
|
-
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
|
|
19560
|
+
if (!submitSuccess) {
|
|
19561
|
+
throw new SphereError(
|
|
19562
|
+
"Failed to mint invoice token: commitment submission failed after retries",
|
|
19563
|
+
"INVOICE_MINT_FAILED"
|
|
19564
|
+
);
|
|
19565
|
+
}
|
|
19566
|
+
if (this.config.debug) {
|
|
19567
|
+
logger.debug(LOG_TAG2, "Waiting for invoice inclusion proof...");
|
|
19568
|
+
}
|
|
19569
|
+
const inclusionProof = await waitInclusionProof6(trustBase, stClient, commitment);
|
|
19570
|
+
if (this.config.debug) {
|
|
19571
|
+
logger.debug(LOG_TAG2, "Invoice inclusion proof received");
|
|
19572
|
+
}
|
|
19573
|
+
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
19574
|
+
const invoicePredicate = await UnmaskedPredicate5.create(
|
|
19575
|
+
invoiceTokenId,
|
|
19576
|
+
invoiceTokenType,
|
|
19577
|
+
signingService,
|
|
19578
|
+
HashAlgorithm6.SHA256,
|
|
19579
|
+
salt
|
|
19568
19580
|
);
|
|
19581
|
+
const tokenState = new TokenState5(invoicePredicate, null);
|
|
19582
|
+
const sdkToken = await SdkToken4.mint(trustBase, tokenState, genesisTransaction);
|
|
19583
|
+
if (this.config.debug) {
|
|
19584
|
+
logger.debug(LOG_TAG2, "Invoice token minted successfully");
|
|
19585
|
+
}
|
|
19586
|
+
sdkData = JSON.stringify(sdkToken.toJSON());
|
|
19569
19587
|
}
|
|
19570
|
-
if (this.config.debug) {
|
|
19571
|
-
logger.debug(LOG_TAG2, "Waiting for invoice inclusion proof...");
|
|
19572
|
-
}
|
|
19573
|
-
const inclusionProof = await waitInclusionProof6(trustBase, stClient, commitment);
|
|
19574
|
-
if (this.config.debug) {
|
|
19575
|
-
logger.debug(LOG_TAG2, "Invoice inclusion proof received");
|
|
19576
|
-
}
|
|
19577
|
-
const genesisTransaction = commitment.toTransaction(inclusionProof);
|
|
19578
|
-
const invoicePredicate = await UnmaskedPredicate7.create(
|
|
19579
|
-
invoiceTokenId,
|
|
19580
|
-
invoiceTokenType,
|
|
19581
|
-
signingService,
|
|
19582
|
-
HashAlgorithm8.SHA256,
|
|
19583
|
-
salt
|
|
19584
|
-
);
|
|
19585
|
-
const tokenState = new TokenState7(invoicePredicate, null);
|
|
19586
|
-
const sdkToken = await SdkToken5.mint(trustBase, tokenState, genesisTransaction);
|
|
19587
|
-
if (this.config.debug) {
|
|
19588
|
-
logger.debug(LOG_TAG2, "Invoice token minted successfully");
|
|
19589
|
-
}
|
|
19590
|
-
const sdkTokenJson = sdkToken.toJSON();
|
|
19591
19588
|
const uiToken = {
|
|
19592
19589
|
id: invoiceId,
|
|
19593
19590
|
coinId: INVOICE_TOKEN_TYPE_HEX,
|
|
@@ -19598,7 +19595,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19598
19595
|
status: "confirmed",
|
|
19599
19596
|
createdAt: terms.createdAt,
|
|
19600
19597
|
updatedAt: terms.createdAt,
|
|
19601
|
-
sdkData
|
|
19598
|
+
sdkData
|
|
19602
19599
|
};
|
|
19603
19600
|
await deps.payments.addToken(uiToken);
|
|
19604
19601
|
this.invoiceTermsCache.set(invoiceId, this._normalizeInvoiceTerms(terms));
|
|
@@ -19609,17 +19606,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19609
19606
|
const allTokens = deps.payments.getTokens();
|
|
19610
19607
|
let anyScanDirty = false;
|
|
19611
19608
|
for (const token of allTokens) {
|
|
19612
|
-
if (
|
|
19613
|
-
let txf;
|
|
19614
|
-
try {
|
|
19615
|
-
txf = JSON.parse(token.sdkData);
|
|
19616
|
-
} catch {
|
|
19617
|
-
continue;
|
|
19618
|
-
}
|
|
19619
|
-
const txCount = txf.transactions?.length ?? 0;
|
|
19620
|
-
if (txCount === 0) continue;
|
|
19621
|
-
this._processTokenTransactions(token.id, txf, 0);
|
|
19622
|
-
anyScanDirty = true;
|
|
19609
|
+
if (await this._scanTokenForAttribution(token, 0)) anyScanDirty = true;
|
|
19623
19610
|
}
|
|
19624
19611
|
const archivedTokensForScan = deps.payments.getArchivedTokens();
|
|
19625
19612
|
for (const [archivedId, txf] of archivedTokensForScan) {
|
|
@@ -19635,7 +19622,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19635
19622
|
if (this.config.debug) {
|
|
19636
19623
|
logger.debug(LOG_TAG2, `Invoice created and stored: ${invoiceId}`);
|
|
19637
19624
|
}
|
|
19638
|
-
const txfToken =
|
|
19625
|
+
const txfToken = engine ? sdkData : JSON.parse(sdkData);
|
|
19639
19626
|
return {
|
|
19640
19627
|
success: true,
|
|
19641
19628
|
invoiceId,
|
|
@@ -19676,36 +19663,60 @@ var AccountingModule = class _AccountingModule {
|
|
|
19676
19663
|
this.ensureNotDestroyed();
|
|
19677
19664
|
this.ensureInitialized();
|
|
19678
19665
|
const deps = this.deps;
|
|
19679
|
-
|
|
19680
|
-
|
|
19681
|
-
|
|
19682
|
-
|
|
19683
|
-
|
|
19684
|
-
|
|
19685
|
-
|
|
19686
|
-
|
|
19687
|
-
|
|
19688
|
-
|
|
19689
|
-
|
|
19690
|
-
|
|
19691
|
-
);
|
|
19692
|
-
|
|
19693
|
-
|
|
19694
|
-
|
|
19666
|
+
let terms;
|
|
19667
|
+
let tokenId;
|
|
19668
|
+
let sdkDataForStore;
|
|
19669
|
+
const engine = deps.tokenEngine;
|
|
19670
|
+
const isV2 = !!engine && typeof token === "string";
|
|
19671
|
+
if (isV2) {
|
|
19672
|
+
const sphereToken = await engine.decodeToken(decodeTokenBlob(hexToBytes(token)));
|
|
19673
|
+
const verifyResult = await engine.verify(sphereToken);
|
|
19674
|
+
if (!verifyResult.ok) {
|
|
19675
|
+
throw new SphereError("Invoice import failed: inclusion proof is invalid.", "INVOICE_INVALID_PROOF");
|
|
19676
|
+
}
|
|
19677
|
+
tokenId = engine.tokenId(sphereToken);
|
|
19678
|
+
const data = engine.readTokenData(sphereToken);
|
|
19679
|
+
if (!data) {
|
|
19680
|
+
throw new SphereError("Invoice import failed: missing or invalid tokenData field.", "INVOICE_INVALID_DATA");
|
|
19681
|
+
}
|
|
19695
19682
|
try {
|
|
19696
|
-
|
|
19697
|
-
jsonString = new TextDecoder().decode(bytes);
|
|
19683
|
+
terms = JSON.parse(new TextDecoder().decode(data));
|
|
19698
19684
|
} catch {
|
|
19685
|
+
throw new SphereError("Invoice import failed: tokenData is not valid JSON.", "INVOICE_INVALID_DATA");
|
|
19699
19686
|
}
|
|
19700
|
-
|
|
19701
|
-
|
|
19702
|
-
|
|
19703
|
-
|
|
19704
|
-
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19708
|
-
|
|
19687
|
+
sdkDataForStore = token;
|
|
19688
|
+
} else {
|
|
19689
|
+
const tokenType = token.genesis?.data?.tokenType;
|
|
19690
|
+
if (tokenType !== INVOICE_TOKEN_TYPE_HEX) {
|
|
19691
|
+
throw new SphereError(
|
|
19692
|
+
`Invoice import failed: token type "${tokenType}" is not the expected invoice type.`,
|
|
19693
|
+
"INVOICE_WRONG_TOKEN_TYPE"
|
|
19694
|
+
);
|
|
19695
|
+
}
|
|
19696
|
+
const tokenData = token.genesis?.data?.tokenData;
|
|
19697
|
+
if (!tokenData || typeof tokenData !== "string") {
|
|
19698
|
+
throw new SphereError(
|
|
19699
|
+
"Invoice import failed: missing or invalid tokenData field.",
|
|
19700
|
+
"INVOICE_INVALID_DATA"
|
|
19701
|
+
);
|
|
19702
|
+
}
|
|
19703
|
+
let jsonString = tokenData;
|
|
19704
|
+
if (!/^\s*[[{"]/.test(tokenData)) {
|
|
19705
|
+
try {
|
|
19706
|
+
const bytes = hexToBytes(tokenData);
|
|
19707
|
+
jsonString = new TextDecoder().decode(bytes);
|
|
19708
|
+
} catch {
|
|
19709
|
+
}
|
|
19710
|
+
}
|
|
19711
|
+
try {
|
|
19712
|
+
terms = JSON.parse(jsonString);
|
|
19713
|
+
} catch {
|
|
19714
|
+
throw new SphereError(
|
|
19715
|
+
"Invoice import failed: tokenData is not valid JSON.",
|
|
19716
|
+
"INVOICE_INVALID_DATA"
|
|
19717
|
+
);
|
|
19718
|
+
}
|
|
19719
|
+
sdkDataForStore = JSON.stringify(token);
|
|
19709
19720
|
}
|
|
19710
19721
|
if (!terms || typeof terms !== "object") {
|
|
19711
19722
|
throw new SphereError(
|
|
@@ -19792,7 +19803,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19792
19803
|
}
|
|
19793
19804
|
}
|
|
19794
19805
|
}
|
|
19795
|
-
|
|
19806
|
+
if (!isV2) tokenId = token.genesis?.data?.tokenId;
|
|
19796
19807
|
if (!tokenId || typeof tokenId !== "string") {
|
|
19797
19808
|
throw new SphereError(
|
|
19798
19809
|
"Invoice import failed: missing tokenId in genesis data.",
|
|
@@ -19805,13 +19816,13 @@ var AccountingModule = class _AccountingModule {
|
|
|
19805
19816
|
"INVOICE_ALREADY_EXISTS"
|
|
19806
19817
|
);
|
|
19807
19818
|
}
|
|
19808
|
-
{
|
|
19809
|
-
const { DataHasher } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19810
|
-
const { HashAlgorithm:
|
|
19811
|
-
const { TokenId:
|
|
19819
|
+
if (!isV2) {
|
|
19820
|
+
const { DataHasher: DataHasher2 } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHasher.js");
|
|
19821
|
+
const { HashAlgorithm: HashAlgorithm6 } = await import("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm.js");
|
|
19822
|
+
const { TokenId: TokenId4 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenId.js");
|
|
19812
19823
|
const reSerializedBytes = new TextEncoder().encode(canonicalSerialize(terms));
|
|
19813
|
-
const hash = await new
|
|
19814
|
-
const reTokenId = new
|
|
19824
|
+
const hash = await new DataHasher2(HashAlgorithm6.SHA256).update(reSerializedBytes).digest();
|
|
19825
|
+
const reTokenId = new TokenId4(hash.imprint).toJSON();
|
|
19815
19826
|
if (reTokenId !== tokenId) {
|
|
19816
19827
|
throw new SphereError(
|
|
19817
19828
|
"Invoice import failed: parsed terms do not match on-chain token ID (canonical hash mismatch).",
|
|
@@ -19819,35 +19830,37 @@ var AccountingModule = class _AccountingModule {
|
|
|
19819
19830
|
);
|
|
19820
19831
|
}
|
|
19821
19832
|
}
|
|
19822
|
-
if (!
|
|
19823
|
-
|
|
19824
|
-
"Trust base unavailable \u2014 cannot verify invoice proof. Ensure oracle supports getTrustBase().",
|
|
19825
|
-
"INVOICE_INVALID_PROOF"
|
|
19826
|
-
);
|
|
19827
|
-
}
|
|
19828
|
-
try {
|
|
19829
|
-
const sdkToken = await import_Token8.Token.fromJSON(token);
|
|
19830
|
-
const verifyResult = await sdkToken.verify(deps.trustBase);
|
|
19831
|
-
const verifyOk = verifyResult.isSuccessful === true;
|
|
19832
|
-
if (!verifyOk) {
|
|
19833
|
+
if (!isV2) {
|
|
19834
|
+
if (!deps.trustBase || deps.trustBase instanceof Uint8Array && deps.trustBase.length === 0) {
|
|
19833
19835
|
throw new SphereError(
|
|
19834
|
-
"
|
|
19836
|
+
"Trust base unavailable \u2014 cannot verify invoice proof. Ensure oracle supports getTrustBase().",
|
|
19835
19837
|
"INVOICE_INVALID_PROOF"
|
|
19836
19838
|
);
|
|
19837
19839
|
}
|
|
19838
|
-
|
|
19839
|
-
|
|
19840
|
+
try {
|
|
19841
|
+
const sdkToken = await import_Token7.Token.fromJSON(token);
|
|
19842
|
+
const verifyResult = await sdkToken.verify(deps.trustBase);
|
|
19843
|
+
const verifyOk = verifyResult.isSuccessful === true;
|
|
19844
|
+
if (!verifyOk) {
|
|
19845
|
+
throw new SphereError(
|
|
19846
|
+
"Invoice import failed: inclusion proof is invalid.",
|
|
19847
|
+
"INVOICE_INVALID_PROOF"
|
|
19848
|
+
);
|
|
19849
|
+
}
|
|
19850
|
+
const canonicalTokenId = sdkToken.id?.toJSON?.() ?? null;
|
|
19851
|
+
if (!canonicalTokenId || canonicalTokenId !== tokenId) {
|
|
19852
|
+
throw new SphereError(
|
|
19853
|
+
`Invoice import failed: tokenId mismatch or unverifiable \u2014 JSON claims ${tokenId}, cryptographic identity is ${canonicalTokenId ?? "unknown"}`,
|
|
19854
|
+
"INVOICE_INVALID_DATA"
|
|
19855
|
+
);
|
|
19856
|
+
}
|
|
19857
|
+
} catch (err) {
|
|
19858
|
+
if (err instanceof SphereError) throw err;
|
|
19840
19859
|
throw new SphereError(
|
|
19841
|
-
`Invoice import failed:
|
|
19842
|
-
"
|
|
19860
|
+
`Invoice import failed: proof verification error \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
19861
|
+
"INVOICE_INVALID_PROOF"
|
|
19843
19862
|
);
|
|
19844
19863
|
}
|
|
19845
|
-
} catch (err) {
|
|
19846
|
-
if (err instanceof SphereError) throw err;
|
|
19847
|
-
throw new SphereError(
|
|
19848
|
-
`Invoice import failed: proof verification error \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
19849
|
-
"INVOICE_INVALID_PROOF"
|
|
19850
|
-
);
|
|
19851
19864
|
}
|
|
19852
19865
|
try {
|
|
19853
19866
|
const uiToken = {
|
|
@@ -19860,7 +19873,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
19860
19873
|
status: "confirmed",
|
|
19861
19874
|
createdAt: terms.createdAt,
|
|
19862
19875
|
updatedAt: terms.createdAt,
|
|
19863
|
-
sdkData:
|
|
19876
|
+
sdkData: sdkDataForStore
|
|
19864
19877
|
};
|
|
19865
19878
|
await deps.payments.addToken(uiToken);
|
|
19866
19879
|
} catch (err) {
|
|
@@ -19915,17 +19928,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
19915
19928
|
const allTokens = deps.payments.getTokens();
|
|
19916
19929
|
let anyDirty = false;
|
|
19917
19930
|
for (const existingToken of allTokens) {
|
|
19918
|
-
if (!existingToken.sdkData) continue;
|
|
19919
|
-
let txf;
|
|
19920
|
-
try {
|
|
19921
|
-
txf = JSON.parse(existingToken.sdkData);
|
|
19922
|
-
} catch {
|
|
19923
|
-
continue;
|
|
19924
|
-
}
|
|
19925
|
-
const transactions = txf.transactions ?? [];
|
|
19926
19931
|
const startIndex = this.tokenScanState.get(existingToken.id) ?? 0;
|
|
19927
|
-
if (
|
|
19928
|
-
this._processTokenTransactions(existingToken.id, txf, startIndex);
|
|
19932
|
+
if (await this._scanTokenForAttribution(existingToken, startIndex)) {
|
|
19929
19933
|
anyDirty = true;
|
|
19930
19934
|
}
|
|
19931
19935
|
}
|
|
@@ -21815,17 +21819,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
21815
21819
|
const allTokens = deps.payments.getTokens();
|
|
21816
21820
|
let anyDirty = false;
|
|
21817
21821
|
for (const token of allTokens) {
|
|
21818
|
-
if (!token.sdkData) continue;
|
|
21819
|
-
let txf;
|
|
21820
|
-
try {
|
|
21821
|
-
txf = JSON.parse(token.sdkData);
|
|
21822
|
-
} catch {
|
|
21823
|
-
continue;
|
|
21824
|
-
}
|
|
21825
|
-
const transactions = txf.transactions ?? [];
|
|
21826
21822
|
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
21827
|
-
if (
|
|
21828
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21823
|
+
if (await this._scanTokenForAttribution(token, startIndex)) {
|
|
21829
21824
|
anyDirty = true;
|
|
21830
21825
|
}
|
|
21831
21826
|
}
|
|
@@ -21880,6 +21875,53 @@ var AccountingModule = class _AccountingModule {
|
|
|
21880
21875
|
* @param txf - Parsed TxfToken.
|
|
21881
21876
|
* @param startIndex - First unprocessed transaction index.
|
|
21882
21877
|
*/
|
|
21878
|
+
/**
|
|
21879
|
+
* Attribute one payment token's invoice memo(s) to the ledger.
|
|
21880
|
+
*
|
|
21881
|
+
* v2 (engine blob): the token carries a single on-chain memo. We decode it and
|
|
21882
|
+
* shim the token into a v1-shaped `txf` (coinData ← engine.readValue, the memo
|
|
21883
|
+
* ← engine.readMemo) so the battle-hardened `_processTokenTransactions` runs
|
|
21884
|
+
* UNCHANGED — same dedup, direction, provisional/synthetic/orphan handling.
|
|
21885
|
+
* v1 (TXF JSON): parse and scan transactions directly.
|
|
21886
|
+
*
|
|
21887
|
+
* `startIndex` is the per-token watermark (0 for a full retroactive scan).
|
|
21888
|
+
* Returns true when the token was scanned. Async because engine.decodeToken is.
|
|
21889
|
+
*/
|
|
21890
|
+
async _scanTokenForAttribution(token, startIndex) {
|
|
21891
|
+
if (!token.sdkData) return false;
|
|
21892
|
+
const engine = this.deps?.tokenEngine;
|
|
21893
|
+
const isBlob = token.sdkData.length >= 2 && token.sdkData.length % 2 === 0 && token.sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(token.sdkData);
|
|
21894
|
+
if (engine && isBlob) {
|
|
21895
|
+
let syntheticTxf;
|
|
21896
|
+
try {
|
|
21897
|
+
const sphereToken = await engine.decodeToken(decodeTokenBlob(hexToBytes(token.sdkData)));
|
|
21898
|
+
const memo = engine.readMemo(sphereToken);
|
|
21899
|
+
if (!memo) return false;
|
|
21900
|
+
const coinData = (engine.readValue(sphereToken)?.assets ?? []).map(
|
|
21901
|
+
(a) => [a.coinId, a.amount.toString()]
|
|
21902
|
+
);
|
|
21903
|
+
syntheticTxf = {
|
|
21904
|
+
genesis: { data: { coinData } },
|
|
21905
|
+
transactions: [{ data: { message: bytesToHex(memo) }, inclusionProof: {} }]
|
|
21906
|
+
};
|
|
21907
|
+
} catch {
|
|
21908
|
+
return false;
|
|
21909
|
+
}
|
|
21910
|
+
this._processTokenTransactions(token.id, syntheticTxf, startIndex);
|
|
21911
|
+
return true;
|
|
21912
|
+
}
|
|
21913
|
+
let txf;
|
|
21914
|
+
try {
|
|
21915
|
+
txf = JSON.parse(token.sdkData);
|
|
21916
|
+
} catch {
|
|
21917
|
+
return false;
|
|
21918
|
+
}
|
|
21919
|
+
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
21920
|
+
this._processTokenTransactions(token.id, txf, startIndex);
|
|
21921
|
+
return true;
|
|
21922
|
+
}
|
|
21923
|
+
return false;
|
|
21924
|
+
}
|
|
21883
21925
|
_processTokenTransactions(tokenId, txf, startIndex) {
|
|
21884
21926
|
const transactions = txf.transactions ?? [];
|
|
21885
21927
|
let lastSuccessIdx = startIndex;
|
|
@@ -22159,17 +22201,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
22159
22201
|
async _handleIncomingTransfer(transfer) {
|
|
22160
22202
|
if (this.destroyed) return;
|
|
22161
22203
|
for (const token of transfer.tokens) {
|
|
22162
|
-
|
|
22163
|
-
let txf;
|
|
22164
|
-
try {
|
|
22165
|
-
txf = JSON.parse(token.sdkData);
|
|
22166
|
-
} catch {
|
|
22167
|
-
continue;
|
|
22168
|
-
}
|
|
22169
|
-
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
22170
|
-
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
22171
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
22172
|
-
}
|
|
22204
|
+
await this._scanTokenForAttribution(token, this.tokenScanState.get(token.id) ?? 0);
|
|
22173
22205
|
}
|
|
22174
22206
|
if (this.destroyed) return;
|
|
22175
22207
|
await this._flushDirtyLedgerEntries();
|
|
@@ -22307,16 +22339,7 @@ var AccountingModule = class _AccountingModule {
|
|
|
22307
22339
|
if (this.destroyed) return;
|
|
22308
22340
|
for (const token of result.tokens) {
|
|
22309
22341
|
if (!token.sdkData) continue;
|
|
22310
|
-
|
|
22311
|
-
try {
|
|
22312
|
-
txf = JSON.parse(token.sdkData);
|
|
22313
|
-
} catch {
|
|
22314
|
-
continue;
|
|
22315
|
-
}
|
|
22316
|
-
const startIndex = this.tokenScanState.get(token.id) ?? 0;
|
|
22317
|
-
if ((txf.transactions?.length ?? 0) > startIndex) {
|
|
22318
|
-
this._processTokenTransactions(token.id, txf, startIndex);
|
|
22319
|
-
}
|
|
22342
|
+
await this._scanTokenForAttribution(token, this.tokenScanState.get(token.id) ?? 0);
|
|
22320
22343
|
const relatedInvoices = this.tokenInvoiceMap.get(token.id);
|
|
22321
22344
|
if (relatedInvoices) {
|
|
22322
22345
|
for (const invoiceId of relatedInvoices) {
|
|
@@ -22387,15 +22410,8 @@ var AccountingModule = class _AccountingModule {
|
|
|
22387
22410
|
const tokens = this.deps.payments.getTokens();
|
|
22388
22411
|
const token = tokens.find((t) => t.id === tokenId);
|
|
22389
22412
|
if (!token?.sdkData) return;
|
|
22390
|
-
let txf;
|
|
22391
|
-
try {
|
|
22392
|
-
txf = JSON.parse(token.sdkData);
|
|
22393
|
-
} catch {
|
|
22394
|
-
return;
|
|
22395
|
-
}
|
|
22396
22413
|
const startIndex = this.tokenScanState.get(tokenId) ?? 0;
|
|
22397
|
-
if ((
|
|
22398
|
-
this._processTokenTransactions(tokenId, txf, startIndex);
|
|
22414
|
+
if (await this._scanTokenForAttribution(token, startIndex)) {
|
|
22399
22415
|
if (this.destroyed) return;
|
|
22400
22416
|
await this._flushDirtyLedgerEntries();
|
|
22401
22417
|
}
|
|
@@ -27828,29 +27844,421 @@ async function parseAndDecryptWalletDat(data, password, onProgress) {
|
|
|
27828
27844
|
};
|
|
27829
27845
|
}
|
|
27830
27846
|
|
|
27847
|
+
// token-engine/identity.ts
|
|
27848
|
+
var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
27849
|
+
var SIGNING_ALGORITHM = "secp256k1";
|
|
27850
|
+
var EMBEDDED_PREDICATE_UNMASKED = 0;
|
|
27851
|
+
var HASH_ALGORITHM_SHA256 = 0n;
|
|
27852
|
+
var SHA256_IMPRINT_PREFIX = new Uint8Array([0, 0]);
|
|
27853
|
+
function hexToBytes4(hex) {
|
|
27854
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
27855
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
27856
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
27857
|
+
}
|
|
27858
|
+
return bytes;
|
|
27859
|
+
}
|
|
27860
|
+
function sha2565(data) {
|
|
27861
|
+
return new import_DataHasher.DataHasher(import_HashAlgorithm2.HashAlgorithm.SHA256).update(data).digest().then((h) => h.data);
|
|
27862
|
+
}
|
|
27863
|
+
async function deriveDirectAddress(publicKey) {
|
|
27864
|
+
const tokenTypeCbor = import_CborSerializer.CborSerializer.encodeByteString(hexToBytes4(UNICITY_TOKEN_TYPE_HEX));
|
|
27865
|
+
const reference = import_CborSerializer.CborSerializer.encodeArray(
|
|
27866
|
+
import_CborSerializer.CborSerializer.encodeByteString(new Uint8Array([EMBEDDED_PREDICATE_UNMASKED])),
|
|
27867
|
+
import_CborSerializer.CborSerializer.encodeByteString(tokenTypeCbor),
|
|
27868
|
+
import_CborSerializer.CborSerializer.encodeTextString(SIGNING_ALGORITHM),
|
|
27869
|
+
import_CborSerializer.CborSerializer.encodeUnsignedInteger(HASH_ALGORITHM_SHA256),
|
|
27870
|
+
import_CborSerializer.CborSerializer.encodeByteString(publicKey)
|
|
27871
|
+
);
|
|
27872
|
+
const refHash = await sha2565(reference);
|
|
27873
|
+
const imprint = new Uint8Array([...SHA256_IMPRINT_PREFIX, ...refHash]);
|
|
27874
|
+
const checksum = (await sha2565(imprint)).slice(0, 4);
|
|
27875
|
+
return `DIRECT://${import_HexConverter.HexConverter.encode(imprint)}${import_HexConverter.HexConverter.encode(checksum)}`;
|
|
27876
|
+
}
|
|
27877
|
+
|
|
27878
|
+
// token-engine/factory.ts
|
|
27879
|
+
init_errors();
|
|
27880
|
+
|
|
27881
|
+
// token-engine/SpherePaymentData.ts
|
|
27882
|
+
init_errors();
|
|
27883
|
+
var COIN_ID_PATTERN = /^([0-9a-f]{2})+$/;
|
|
27884
|
+
function assertAsset(coinId, amount) {
|
|
27885
|
+
if (!COIN_ID_PATTERN.test(coinId)) {
|
|
27886
|
+
throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
|
|
27887
|
+
}
|
|
27888
|
+
if (amount < 0n) {
|
|
27889
|
+
throw new SphereError(`Asset amount must be non-negative: ${amount.toString()}`, "VALIDATION_ERROR");
|
|
27890
|
+
}
|
|
27891
|
+
}
|
|
27892
|
+
function sphereAssetToSdk(coinId, amount) {
|
|
27893
|
+
assertAsset(coinId, amount);
|
|
27894
|
+
return new import_Asset.Asset(new import_AssetId.AssetId(import_HexConverter.HexConverter.decode(coinId)), amount);
|
|
27895
|
+
}
|
|
27896
|
+
var SpherePaymentData = class _SpherePaymentData {
|
|
27897
|
+
constructor(assets, _memo = null) {
|
|
27898
|
+
this.assets = assets;
|
|
27899
|
+
this._memo = _memo;
|
|
27900
|
+
}
|
|
27901
|
+
/** Sphere-private CBOR tag (verified free in the v2 SDK tag space). */
|
|
27902
|
+
static CBOR_TAG = 39050n;
|
|
27903
|
+
/** Envelope version; bump when the structure changes. */
|
|
27904
|
+
static VERSION = 1n;
|
|
27905
|
+
/** Opaque, app-defined memo carried alongside the value (e.g. invoice attribution). */
|
|
27906
|
+
get memo() {
|
|
27907
|
+
return this._memo ? new Uint8Array(this._memo) : null;
|
|
27908
|
+
}
|
|
27909
|
+
/** Wrap an existing SDK asset collection (+ optional opaque memo). */
|
|
27910
|
+
static create(assets, memo = null) {
|
|
27911
|
+
return new _SpherePaymentData(assets, memo);
|
|
27912
|
+
}
|
|
27913
|
+
/** Build from a sphere-domain value (hex coin id → bigint amount) + optional opaque memo. */
|
|
27914
|
+
static fromValue(value, memo = null) {
|
|
27915
|
+
const assets = value.assets.map((a) => sphereAssetToSdk(a.coinId, a.amount));
|
|
27916
|
+
return new _SpherePaymentData(import_PaymentAssetCollection.PaymentAssetCollection.create(...assets), memo);
|
|
27917
|
+
}
|
|
27918
|
+
/** Decode from the CBOR envelope produced by {@link encode}. */
|
|
27919
|
+
static fromCBOR(bytes) {
|
|
27920
|
+
const tag = import_CborDeserializer.CborDeserializer.decodeTag(bytes);
|
|
27921
|
+
if (tag.tag !== _SpherePaymentData.CBOR_TAG) {
|
|
27922
|
+
throw new import_CborError.CborError(`Invalid SpherePaymentData tag: ${tag.tag}`);
|
|
27923
|
+
}
|
|
27924
|
+
const fields = import_CborDeserializer.CborDeserializer.decodeArray(tag.data, 3);
|
|
27925
|
+
const version = import_CborDeserializer.CborDeserializer.decodeUnsignedInteger(fields[0]);
|
|
27926
|
+
if (version !== _SpherePaymentData.VERSION) {
|
|
27927
|
+
throw new import_CborError.CborError(`Unsupported SpherePaymentData version: ${version}`);
|
|
27928
|
+
}
|
|
27929
|
+
const memo = import_CborDeserializer.CborDeserializer.decodeNullable(fields[2], import_CborDeserializer.CborDeserializer.decodeByteString);
|
|
27930
|
+
return new _SpherePaymentData(import_PaymentAssetCollection.PaymentAssetCollection.fromCBOR(fields[1]), memo);
|
|
27931
|
+
}
|
|
27932
|
+
/** Deterministic, versioned, tagged CBOR: `tag(39050)[ version, assets, memo? ]`. */
|
|
27933
|
+
encode() {
|
|
27934
|
+
return Promise.resolve(
|
|
27935
|
+
import_CborSerializer.CborSerializer.encodeTag(
|
|
27936
|
+
_SpherePaymentData.CBOR_TAG,
|
|
27937
|
+
import_CborSerializer.CborSerializer.encodeArray(
|
|
27938
|
+
import_CborSerializer.CborSerializer.encodeUnsignedInteger(_SpherePaymentData.VERSION),
|
|
27939
|
+
this.assets.toCBOR(),
|
|
27940
|
+
import_CborSerializer.CborSerializer.encodeNullable(this._memo, import_CborSerializer.CborSerializer.encodeByteString)
|
|
27941
|
+
)
|
|
27942
|
+
)
|
|
27943
|
+
);
|
|
27944
|
+
}
|
|
27945
|
+
/** Project to a sphere-domain value (hex coin id + bigint amount), preserving order. */
|
|
27946
|
+
toValue() {
|
|
27947
|
+
return {
|
|
27948
|
+
assets: this.assets.toArray().map((a) => ({
|
|
27949
|
+
coinId: import_HexConverter.HexConverter.encode(a.id.bytes),
|
|
27950
|
+
amount: a.value
|
|
27951
|
+
}))
|
|
27952
|
+
};
|
|
27953
|
+
}
|
|
27954
|
+
/** Balance of a single coin within this payload (0n when absent). */
|
|
27955
|
+
balanceOf(coinId) {
|
|
27956
|
+
if (!COIN_ID_PATTERN.test(coinId)) {
|
|
27957
|
+
throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
|
|
27958
|
+
}
|
|
27959
|
+
const asset = this.assets.get(new import_AssetId.AssetId(import_HexConverter.HexConverter.decode(coinId)));
|
|
27960
|
+
return asset ? asset.value : 0n;
|
|
27961
|
+
}
|
|
27962
|
+
};
|
|
27963
|
+
function decodeSpherePaymentData(bytes) {
|
|
27964
|
+
return Promise.resolve(SpherePaymentData.fromCBOR(bytes));
|
|
27965
|
+
}
|
|
27966
|
+
|
|
27967
|
+
// token-engine/SphereTokenEngine.ts
|
|
27968
|
+
init_errors();
|
|
27969
|
+
var SphereTokenEngine = class {
|
|
27970
|
+
constructor(deps) {
|
|
27971
|
+
this.deps = deps;
|
|
27972
|
+
}
|
|
27973
|
+
// ── identity ────────────────────────────────────────────────────────────────
|
|
27974
|
+
getIdentity() {
|
|
27975
|
+
return { chainPubkey: new Uint8Array(this.deps.signingService.publicKey) };
|
|
27976
|
+
}
|
|
27977
|
+
/** Legacy DIRECT:// address (Path A). Async: the derivation hashes via the SDK. */
|
|
27978
|
+
deriveIdentityAddress(pubkey) {
|
|
27979
|
+
return deriveDirectAddress(pubkey ?? this.deps.signingService.publicKey);
|
|
27980
|
+
}
|
|
27981
|
+
// ── value (read) ─────────────────────────────────────────────────────────────
|
|
27982
|
+
readValue(token) {
|
|
27983
|
+
return token.value;
|
|
27984
|
+
}
|
|
27985
|
+
balanceOf(token, coinId) {
|
|
27986
|
+
let sum = 0n;
|
|
27987
|
+
for (const asset of token.value?.assets ?? []) {
|
|
27988
|
+
if (asset.coinId === coinId) sum += asset.amount;
|
|
27989
|
+
}
|
|
27990
|
+
return sum;
|
|
27991
|
+
}
|
|
27992
|
+
tokenId(token) {
|
|
27993
|
+
return token.blob.tokenId;
|
|
27994
|
+
}
|
|
27995
|
+
readMemo(token) {
|
|
27996
|
+
const sdkToken = token.sdkToken;
|
|
27997
|
+
if (sdkToken.transactions.length > 0) {
|
|
27998
|
+
return sdkToken.latestTransaction.data;
|
|
27999
|
+
}
|
|
28000
|
+
const data = sdkToken.genesis.data;
|
|
28001
|
+
if (data && this.isSpherePaymentData(data)) {
|
|
28002
|
+
return SpherePaymentData.fromCBOR(data).memo;
|
|
28003
|
+
}
|
|
28004
|
+
return null;
|
|
28005
|
+
}
|
|
28006
|
+
readTokenData(token) {
|
|
28007
|
+
const data = token.sdkToken.genesis.data;
|
|
28008
|
+
return data ? new Uint8Array(data) : null;
|
|
28009
|
+
}
|
|
28010
|
+
// ── lifecycle ────────────────────────────────────────────────────────────────
|
|
28011
|
+
async mint(params, options) {
|
|
28012
|
+
const recipient = import_SignaturePredicate.SignaturePredicate.create(params.recipientPubkey);
|
|
28013
|
+
const data = params.value ? await SpherePaymentData.fromValue(params.value).encode() : null;
|
|
28014
|
+
const mintTx = await import_MintTransaction.MintTransaction.create(this.deps.networkId, recipient, data);
|
|
28015
|
+
const certificationData = await import_CertificationData.CertificationData.fromMintTransaction(mintTx);
|
|
28016
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
28017
|
+
if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
|
|
28018
|
+
throw new SphereError(`Mint certification failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
28019
|
+
}
|
|
28020
|
+
const proof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
|
|
28021
|
+
this.deps.client,
|
|
28022
|
+
this.deps.trustBase,
|
|
28023
|
+
this.deps.predicateVerifier,
|
|
28024
|
+
mintTx,
|
|
28025
|
+
options?.signal
|
|
28026
|
+
);
|
|
28027
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
28028
|
+
const token = await import_Token2.Token.mint(
|
|
28029
|
+
this.deps.trustBase,
|
|
28030
|
+
this.deps.predicateVerifier,
|
|
28031
|
+
this.deps.mintJustificationVerifier,
|
|
28032
|
+
certified
|
|
28033
|
+
);
|
|
28034
|
+
return this.wrapToken(token);
|
|
28035
|
+
}
|
|
28036
|
+
async mintDataToken(params, options) {
|
|
28037
|
+
const recipient = import_SignaturePredicate.SignaturePredicate.create(params.recipientPubkey);
|
|
28038
|
+
const tokenType = params.tokenType ? new import_TokenType.TokenType(params.tokenType) : import_TokenType.TokenType.generate();
|
|
28039
|
+
const salt = params.salt ? import_TokenSalt.TokenSalt.fromBytes(params.salt) : import_TokenSalt.TokenSalt.generate();
|
|
28040
|
+
const mintTx = await import_MintTransaction.MintTransaction.create(this.deps.networkId, recipient, params.data, tokenType, salt);
|
|
28041
|
+
const certificationData = await import_CertificationData.CertificationData.fromMintTransaction(mintTx);
|
|
28042
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
28043
|
+
if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
|
|
28044
|
+
throw new SphereError(`Data-token mint failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
28045
|
+
}
|
|
28046
|
+
const proof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
|
|
28047
|
+
this.deps.client,
|
|
28048
|
+
this.deps.trustBase,
|
|
28049
|
+
this.deps.predicateVerifier,
|
|
28050
|
+
mintTx,
|
|
28051
|
+
options?.signal
|
|
28052
|
+
);
|
|
28053
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
28054
|
+
const token = await import_Token2.Token.mint(
|
|
28055
|
+
this.deps.trustBase,
|
|
28056
|
+
this.deps.predicateVerifier,
|
|
28057
|
+
this.deps.mintJustificationVerifier,
|
|
28058
|
+
certified
|
|
28059
|
+
);
|
|
28060
|
+
return this.wrapToken(token);
|
|
28061
|
+
}
|
|
28062
|
+
async transfer(params, options) {
|
|
28063
|
+
this.assertOwned(params.token);
|
|
28064
|
+
const recipient = import_SignaturePredicate.SignaturePredicate.create(params.recipientPubkey);
|
|
28065
|
+
const stateMask = crypto.getRandomValues(new Uint8Array(32));
|
|
28066
|
+
const transferTx = await import_TransferTransaction.TransferTransaction.create(params.token.sdkToken, recipient, stateMask, params.data ?? null);
|
|
28067
|
+
const unlockScript = await import_SignaturePredicateUnlockScript.SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);
|
|
28068
|
+
const certificationData = await import_CertificationData.CertificationData.fromTransaction(transferTx, unlockScript);
|
|
28069
|
+
const response = await this.deps.client.submitCertificationRequest(certificationData);
|
|
28070
|
+
if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
|
|
28071
|
+
throw new SphereError(`Transfer certification failed: ${response.status}`, "TRANSFER_FAILED");
|
|
28072
|
+
}
|
|
28073
|
+
const proof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
|
|
28074
|
+
this.deps.client,
|
|
28075
|
+
this.deps.trustBase,
|
|
28076
|
+
this.deps.predicateVerifier,
|
|
28077
|
+
transferTx,
|
|
28078
|
+
options?.signal
|
|
28079
|
+
);
|
|
28080
|
+
const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
28081
|
+
const transferred = await params.token.sdkToken.transfer(this.deps.trustBase, this.deps.predicateVerifier, certified);
|
|
28082
|
+
return this.wrapToken(transferred);
|
|
28083
|
+
}
|
|
28084
|
+
async split(params, options) {
|
|
28085
|
+
this.assertOwned(params.token);
|
|
28086
|
+
if (params.outputs.length === 0) {
|
|
28087
|
+
throw new SphereError("Split requires at least one output", "VALIDATION_ERROR");
|
|
28088
|
+
}
|
|
28089
|
+
const requests = params.outputs.map(
|
|
28090
|
+
(o) => import_SplitTokenRequest.SplitTokenRequest.create(
|
|
28091
|
+
import_SignaturePredicate.SignaturePredicate.create(o.recipientPubkey),
|
|
28092
|
+
import_PaymentAssetCollection.PaymentAssetCollection.create(sphereAssetToSdk(o.coinId, o.amount))
|
|
28093
|
+
)
|
|
28094
|
+
);
|
|
28095
|
+
const split = await import_TokenSplit.TokenSplit.split(params.token.sdkToken, decodeSpherePaymentData, requests);
|
|
28096
|
+
const burnUnlock = await import_SignaturePredicateUnlockScript.SignaturePredicateUnlockScript.create(split.burn.transaction, this.deps.signingService);
|
|
28097
|
+
const burnCert = await import_CertificationData.CertificationData.fromTransaction(split.burn.transaction, burnUnlock);
|
|
28098
|
+
const burnResponse = await this.deps.client.submitCertificationRequest(burnCert);
|
|
28099
|
+
if (burnResponse.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
|
|
28100
|
+
throw new SphereError(`Split burn failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
28101
|
+
}
|
|
28102
|
+
const burnProof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
|
|
28103
|
+
this.deps.client,
|
|
28104
|
+
this.deps.trustBase,
|
|
28105
|
+
this.deps.predicateVerifier,
|
|
28106
|
+
split.burn.transaction,
|
|
28107
|
+
options?.signal
|
|
28108
|
+
);
|
|
28109
|
+
const burnCertified = await split.burn.transaction.toCertifiedTransaction(
|
|
28110
|
+
this.deps.trustBase,
|
|
28111
|
+
this.deps.predicateVerifier,
|
|
28112
|
+
burnProof
|
|
28113
|
+
);
|
|
28114
|
+
const burntToken = await params.token.sdkToken.transfer(
|
|
28115
|
+
this.deps.trustBase,
|
|
28116
|
+
this.deps.predicateVerifier,
|
|
28117
|
+
burnCertified
|
|
28118
|
+
);
|
|
28119
|
+
const outputs = [];
|
|
28120
|
+
for (let i = 0; i < split.tokens.length; i++) {
|
|
28121
|
+
const splitToken = split.tokens[i];
|
|
28122
|
+
const data = await SpherePaymentData.create(splitToken.assets, params.outputs[i].data ?? null).encode();
|
|
28123
|
+
const justification = import_SplitMintJustification.SplitMintJustification.create(burntToken, splitToken.proofs).toCBOR();
|
|
28124
|
+
const mintTx = await import_MintTransaction.MintTransaction.create(
|
|
28125
|
+
splitToken.networkId,
|
|
28126
|
+
splitToken.recipient,
|
|
28127
|
+
data,
|
|
28128
|
+
splitToken.tokenType,
|
|
28129
|
+
splitToken.salt,
|
|
28130
|
+
justification
|
|
28131
|
+
);
|
|
28132
|
+
const certData = await import_CertificationData.CertificationData.fromMintTransaction(mintTx);
|
|
28133
|
+
const response = await this.deps.client.submitCertificationRequest(certData);
|
|
28134
|
+
if (response.status !== import_CertificationResponse.CertificationStatus.SUCCESS) {
|
|
28135
|
+
throw new SphereError(`Split mint failed: ${response.status}`, "AGGREGATOR_ERROR");
|
|
28136
|
+
}
|
|
28137
|
+
const proof = await (0, import_InclusionProofUtils2.waitInclusionProof)(
|
|
28138
|
+
this.deps.client,
|
|
28139
|
+
this.deps.trustBase,
|
|
28140
|
+
this.deps.predicateVerifier,
|
|
28141
|
+
mintTx,
|
|
28142
|
+
options?.signal
|
|
28143
|
+
);
|
|
28144
|
+
const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
|
|
28145
|
+
const token = await import_Token2.Token.mint(
|
|
28146
|
+
this.deps.trustBase,
|
|
28147
|
+
this.deps.predicateVerifier,
|
|
28148
|
+
this.deps.mintJustificationVerifier,
|
|
28149
|
+
certified
|
|
28150
|
+
);
|
|
28151
|
+
outputs.push(this.wrapToken(token));
|
|
28152
|
+
}
|
|
28153
|
+
return { outputs };
|
|
28154
|
+
}
|
|
28155
|
+
// ── verification ─────────────────────────────────────────────────────────────
|
|
28156
|
+
async verify(token, _options) {
|
|
28157
|
+
const result = await token.sdkToken.verify(
|
|
28158
|
+
this.deps.trustBase,
|
|
28159
|
+
this.deps.predicateVerifier,
|
|
28160
|
+
this.deps.mintJustificationVerifier
|
|
28161
|
+
);
|
|
28162
|
+
return result.status === import_VerificationStatus.VerificationStatus.OK ? { ok: true } : { ok: false, reason: String(result.status) };
|
|
28163
|
+
}
|
|
28164
|
+
async isSpent(token, _options) {
|
|
28165
|
+
const probe = await import_TransferTransaction.TransferTransaction.create(
|
|
28166
|
+
token.sdkToken,
|
|
28167
|
+
import_SignaturePredicate.SignaturePredicate.create(this.deps.signingService.publicKey),
|
|
28168
|
+
new Uint8Array(32)
|
|
28169
|
+
);
|
|
28170
|
+
const stateId = await import_StateId.StateId.fromTransaction(probe);
|
|
28171
|
+
const response = await this.deps.client.getInclusionProof(stateId);
|
|
28172
|
+
return response.inclusionProof.inclusionCertificate !== null;
|
|
28173
|
+
}
|
|
28174
|
+
// ── serialization ────────────────────────────────────────────────────────────
|
|
28175
|
+
encodeToken(token) {
|
|
28176
|
+
return token.blob;
|
|
28177
|
+
}
|
|
28178
|
+
async decodeToken(blob) {
|
|
28179
|
+
const sdkToken = await import_Token2.Token.fromCBOR(blob.token);
|
|
28180
|
+
if (sdkToken.genesis.networkId.id !== this.deps.networkId.id) {
|
|
28181
|
+
throw new SphereError(
|
|
28182
|
+
`Token network mismatch: token is on network ${sdkToken.genesis.networkId.id}, engine on ${this.deps.networkId.id}`,
|
|
28183
|
+
"VALIDATION_ERROR"
|
|
28184
|
+
);
|
|
28185
|
+
}
|
|
28186
|
+
return this.wrapToken(sdkToken);
|
|
28187
|
+
}
|
|
28188
|
+
// ── internals ────────────────────────────────────────────────────────────────
|
|
28189
|
+
/** Fail fast if this engine's key does not own the token's current state. */
|
|
28190
|
+
assertOwned(token) {
|
|
28191
|
+
const owner = token.sdkToken.latestTransaction.recipient;
|
|
28192
|
+
const mine = import_EncodedPredicate.EncodedPredicate.fromPredicate(import_SignaturePredicate.SignaturePredicate.create(this.deps.signingService.publicKey));
|
|
28193
|
+
if (!import_EncodedPredicate.EncodedPredicate.equals(owner, mine)) {
|
|
28194
|
+
throw new SphereError("Cannot transfer a token not owned by this engine identity", "VALIDATION_ERROR");
|
|
28195
|
+
}
|
|
28196
|
+
}
|
|
28197
|
+
/** Wrap an SDK token into a SphereToken: cache its blob (incl. stable tokenId) + decoded value. */
|
|
28198
|
+
wrapToken(sdkToken) {
|
|
28199
|
+
const data = sdkToken.genesis.data;
|
|
28200
|
+
let value = null;
|
|
28201
|
+
if (data && this.isSpherePaymentData(data)) {
|
|
28202
|
+
try {
|
|
28203
|
+
value = SpherePaymentData.fromCBOR(data).toValue();
|
|
28204
|
+
} catch (err) {
|
|
28205
|
+
throw new SphereError(
|
|
28206
|
+
`Failed to decode token payment data: ${err instanceof Error ? err.message : String(err)}`,
|
|
28207
|
+
"VALIDATION_ERROR"
|
|
28208
|
+
);
|
|
28209
|
+
}
|
|
28210
|
+
}
|
|
28211
|
+
const blob = {
|
|
28212
|
+
v: TOKEN_BLOB_VERSION,
|
|
28213
|
+
network: sdkToken.genesis.networkId.id,
|
|
28214
|
+
tokenId: import_HexConverter.HexConverter.encode(sdkToken.id.bytes),
|
|
28215
|
+
token: sdkToken.toCBOR()
|
|
28216
|
+
};
|
|
28217
|
+
return { sdkToken, blob, value };
|
|
28218
|
+
}
|
|
28219
|
+
/** True if the bytes are a SpherePaymentData envelope (value token) vs a raw data token. */
|
|
28220
|
+
isSpherePaymentData(data) {
|
|
28221
|
+
try {
|
|
28222
|
+
return import_CborDeserializer.CborDeserializer.decodeTag(data).tag === SpherePaymentData.CBOR_TAG;
|
|
28223
|
+
} catch {
|
|
28224
|
+
return false;
|
|
28225
|
+
}
|
|
28226
|
+
}
|
|
28227
|
+
};
|
|
28228
|
+
|
|
28229
|
+
// token-engine/factory.ts
|
|
28230
|
+
async function createSphereTokenEngine(config) {
|
|
28231
|
+
if (config.trustBaseJson == null) {
|
|
28232
|
+
throw new SphereError("Engine config requires a trust base (trustBaseJson)", "INVALID_CONFIG");
|
|
28233
|
+
}
|
|
28234
|
+
const trustBase = import_RootTrustBase.RootTrustBase.fromJSON(config.trustBaseJson);
|
|
28235
|
+
const predicateVerifier = import_PredicateVerifierService.PredicateVerifierService.create();
|
|
28236
|
+
const mintJustificationVerifier = new import_MintJustificationVerifierService.MintJustificationVerifierService();
|
|
28237
|
+
mintJustificationVerifier.register(
|
|
28238
|
+
new import_SplitMintJustificationVerifier.SplitMintJustificationVerifier(trustBase, predicateVerifier, decodeSpherePaymentData)
|
|
28239
|
+
);
|
|
28240
|
+
const deps = {
|
|
28241
|
+
client: new import_StateTransitionClient.StateTransitionClient(new import_AggregatorClient.AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
|
|
28242
|
+
trustBase,
|
|
28243
|
+
predicateVerifier,
|
|
28244
|
+
mintJustificationVerifier,
|
|
28245
|
+
signingService: new import_SigningService.SigningService(config.privateKey),
|
|
28246
|
+
// The trust base is the single source of truth for the network id (it carries
|
|
28247
|
+
// NetworkId.fromId, so any id works — e.g. testnet2 = 4 — with no enum entry).
|
|
28248
|
+
networkId: trustBase.networkId
|
|
28249
|
+
};
|
|
28250
|
+
return new SphereTokenEngine(deps);
|
|
28251
|
+
}
|
|
28252
|
+
|
|
27831
28253
|
// core/Sphere.ts
|
|
27832
|
-
var
|
|
27833
|
-
var import_TokenType5 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
27834
|
-
var import_HashAlgorithm7 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
|
|
27835
|
-
var import_UnmaskedPredicateReference3 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
27836
|
-
var import_nostr_js_sdk5 = require("@unicitylabs/nostr-js-sdk");
|
|
28254
|
+
var import_nostr_js_sdk4 = require("@unicitylabs/nostr-js-sdk");
|
|
27837
28255
|
function isValidNametag2(nametag) {
|
|
27838
|
-
if ((0,
|
|
28256
|
+
if ((0, import_nostr_js_sdk4.isPhoneNumber)(nametag)) return true;
|
|
27839
28257
|
return /^[a-z0-9_-]{3,20}$/.test(nametag);
|
|
27840
28258
|
}
|
|
27841
|
-
var UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
27842
28259
|
async function deriveL3PredicateAddress(privateKey) {
|
|
27843
|
-
const
|
|
27844
|
-
|
|
27845
|
-
const tokenTypeBytes = Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex");
|
|
27846
|
-
const tokenType = new import_TokenType5.TokenType(tokenTypeBytes);
|
|
27847
|
-
const predicateRef = import_UnmaskedPredicateReference3.UnmaskedPredicateReference.create(
|
|
27848
|
-
tokenType,
|
|
27849
|
-
signingService.algorithm,
|
|
27850
|
-
signingService.publicKey,
|
|
27851
|
-
import_HashAlgorithm7.HashAlgorithm.SHA256
|
|
27852
|
-
);
|
|
27853
|
-
return (await (await predicateRef).toAddress()).toString();
|
|
28260
|
+
const prehashedPublicKey = getPublicKey(sha2562(privateKey, "hex"));
|
|
28261
|
+
return deriveDirectAddress(hexToBytes2(prehashedPublicKey));
|
|
27854
28262
|
}
|
|
27855
28263
|
var Sphere = class _Sphere {
|
|
27856
28264
|
// Singleton
|
|
@@ -27872,14 +28280,14 @@ var Sphere = class _Sphere {
|
|
|
27872
28280
|
_addressIdToIndex = /* @__PURE__ */ new Map();
|
|
27873
28281
|
/** Nametag cache: addressId -> (nametagIndex -> nametag). Separate from tracked addresses. */
|
|
27874
28282
|
_addressNametags = /* @__PURE__ */ new Map();
|
|
27875
|
-
/** Cached PROXY address (computed once when nametag is set) */
|
|
27876
|
-
_cachedProxyAddress = void 0;
|
|
27877
28283
|
// Providers
|
|
27878
28284
|
_storage;
|
|
27879
28285
|
_tokenStorageProviders = /* @__PURE__ */ new Map();
|
|
27880
28286
|
_transport;
|
|
27881
28287
|
_oracle;
|
|
27882
28288
|
_priceProvider;
|
|
28289
|
+
/** v2 token engine (built per active address from the oracle); injected into modules. */
|
|
28290
|
+
_tokenEngine;
|
|
27883
28291
|
// Modules (single-instance — backward compat, delegates to active address)
|
|
27884
28292
|
_payments;
|
|
27885
28293
|
_communications;
|
|
@@ -28220,20 +28628,6 @@ var Sphere = class _Sphere {
|
|
|
28220
28628
|
await sphere.syncIdentityWithTransport();
|
|
28221
28629
|
sphere._initialized = true;
|
|
28222
28630
|
_Sphere.instance = sphere;
|
|
28223
|
-
if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {
|
|
28224
|
-
progress?.({ step: "registering_nametag", message: "Restoring nametag token..." });
|
|
28225
|
-
logger.debug("Sphere", `Unicity ID @${sphere._identity.nametag} has no token, attempting to mint...`);
|
|
28226
|
-
try {
|
|
28227
|
-
const result = await sphere.mintNametag(sphere._identity.nametag);
|
|
28228
|
-
if (result.success) {
|
|
28229
|
-
logger.debug("Sphere", `Nametag token minted successfully on load`);
|
|
28230
|
-
} else {
|
|
28231
|
-
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
28232
|
-
}
|
|
28233
|
-
} catch (err) {
|
|
28234
|
-
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
28235
|
-
}
|
|
28236
|
-
}
|
|
28237
28631
|
if (options.discoverAddresses !== false && sphere._transport.discoverAddresses && sphere._masterKey) {
|
|
28238
28632
|
progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
|
|
28239
28633
|
try {
|
|
@@ -29285,13 +29679,13 @@ var Sphere = class _Sphere {
|
|
|
29285
29679
|
oracle: this._oracle,
|
|
29286
29680
|
emitEvent: this.emitEvent.bind(this),
|
|
29287
29681
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
29288
|
-
price: this._priceProvider ?? void 0
|
|
29682
|
+
price: this._priceProvider ?? void 0,
|
|
29683
|
+
tokenEngine: moduleSet.tokenEngine
|
|
29289
29684
|
});
|
|
29290
29685
|
}
|
|
29291
29686
|
}
|
|
29292
29687
|
this._identity = newIdentity;
|
|
29293
29688
|
this._currentAddressIndex = index;
|
|
29294
|
-
await this._updateCachedProxyAddress();
|
|
29295
29689
|
const activeModules = this._addressModules.get(index);
|
|
29296
29690
|
this._payments = activeModules.payments;
|
|
29297
29691
|
this._communications = activeModules.communications;
|
|
@@ -29329,35 +29723,10 @@ var Sphere = class _Sphere {
|
|
|
29329
29723
|
}
|
|
29330
29724
|
if (newNametag) {
|
|
29331
29725
|
await this.persistAddressNametags();
|
|
29332
|
-
if (!this._payments.hasNametag()) {
|
|
29333
|
-
logger.debug("Sphere", `Minting nametag token for @${newNametag}...`);
|
|
29334
|
-
try {
|
|
29335
|
-
const result = await this.mintNametag(newNametag);
|
|
29336
|
-
if (result.success) {
|
|
29337
|
-
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
29338
|
-
} else {
|
|
29339
|
-
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
29340
|
-
}
|
|
29341
|
-
} catch (err) {
|
|
29342
|
-
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
29343
|
-
}
|
|
29344
|
-
}
|
|
29345
29726
|
this.emitEvent("nametag:registered", {
|
|
29346
29727
|
nametag: newNametag,
|
|
29347
29728
|
addressIndex: index
|
|
29348
29729
|
});
|
|
29349
|
-
} else if (this._identity?.nametag && !this._payments.hasNametag()) {
|
|
29350
|
-
logger.debug("Sphere", `Unicity ID @${this._identity.nametag} has no token after switch, minting...`);
|
|
29351
|
-
try {
|
|
29352
|
-
const result = await this.mintNametag(this._identity.nametag);
|
|
29353
|
-
if (result.success) {
|
|
29354
|
-
logger.debug("Sphere", `Nametag token minted successfully after switch`);
|
|
29355
|
-
} else {
|
|
29356
|
-
logger.warn("Sphere", `Could not mint nametag token after switch: ${result.error}`);
|
|
29357
|
-
}
|
|
29358
|
-
} catch (err) {
|
|
29359
|
-
logger.warn("Sphere", `Nametag token mint failed after switch:`, err);
|
|
29360
|
-
}
|
|
29361
29730
|
}
|
|
29362
29731
|
}
|
|
29363
29732
|
/**
|
|
@@ -29387,6 +29756,7 @@ var Sphere = class _Sphere {
|
|
|
29387
29756
|
const communications = createCommunicationsModule(this._communicationsConfig);
|
|
29388
29757
|
const groupChat = this._groupChatConfig ? createGroupChatModule(this._groupChatConfig) : null;
|
|
29389
29758
|
const market = this._marketConfig ? createMarketModule(this._marketConfig) : null;
|
|
29759
|
+
const tokenEngine = await this.buildTokenEngine(identity);
|
|
29390
29760
|
payments.initialize({
|
|
29391
29761
|
identity,
|
|
29392
29762
|
storage: this._storage,
|
|
@@ -29395,7 +29765,8 @@ var Sphere = class _Sphere {
|
|
|
29395
29765
|
oracle: this._oracle,
|
|
29396
29766
|
emitEvent,
|
|
29397
29767
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
29398
|
-
price: this._priceProvider ?? void 0
|
|
29768
|
+
price: this._priceProvider ?? void 0,
|
|
29769
|
+
tokenEngine
|
|
29399
29770
|
});
|
|
29400
29771
|
communications.initialize({
|
|
29401
29772
|
identity,
|
|
@@ -29431,7 +29802,8 @@ var Sphere = class _Sphere {
|
|
|
29431
29802
|
emitEvent,
|
|
29432
29803
|
on: this.on.bind(this),
|
|
29433
29804
|
storage: this._storage,
|
|
29434
|
-
communications
|
|
29805
|
+
communications,
|
|
29806
|
+
tokenEngine
|
|
29435
29807
|
});
|
|
29436
29808
|
} else {
|
|
29437
29809
|
logger.warn("Sphere", "Accounting module enabled but no token storage available \u2014 disabling");
|
|
@@ -29491,6 +29863,7 @@ var Sphere = class _Sphere {
|
|
|
29491
29863
|
market,
|
|
29492
29864
|
transportAdapter: adapter,
|
|
29493
29865
|
tokenStorageProviders: new Map(tokenStorageProviders),
|
|
29866
|
+
tokenEngine,
|
|
29494
29867
|
initialized: true
|
|
29495
29868
|
};
|
|
29496
29869
|
this._addressModules.set(index, moduleSet);
|
|
@@ -30046,15 +30419,6 @@ var Sphere = class _Sphere {
|
|
|
30046
30419
|
hasNametag() {
|
|
30047
30420
|
return !!this._identity?.nametag;
|
|
30048
30421
|
}
|
|
30049
|
-
/**
|
|
30050
|
-
* Get the PROXY address for the current nametag
|
|
30051
|
-
* PROXY addresses are derived from the nametag hash and require
|
|
30052
|
-
* the nametag token to claim funds sent to them
|
|
30053
|
-
* @returns PROXY address string or undefined if no nametag
|
|
30054
|
-
*/
|
|
30055
|
-
getProxyAddress() {
|
|
30056
|
-
return this._cachedProxyAddress;
|
|
30057
|
-
}
|
|
30058
30422
|
/**
|
|
30059
30423
|
* Resolve any identifier to full peer information.
|
|
30060
30424
|
* Accepts @nametag, bare nametag, DIRECT://, PROXY://, L1 address, or transport pubkey.
|
|
@@ -30089,17 +30453,6 @@ var Sphere = class _Sphere {
|
|
|
30089
30453
|
throw new SphereError(`Cannot resolve address: ${address.slice(0, 30)}`, "INVALID_RECIPIENT");
|
|
30090
30454
|
}
|
|
30091
30455
|
}
|
|
30092
|
-
/** Compute and cache the PROXY address from the current nametag */
|
|
30093
|
-
async _updateCachedProxyAddress() {
|
|
30094
|
-
const nametag = this._identity?.nametag;
|
|
30095
|
-
if (!nametag) {
|
|
30096
|
-
this._cachedProxyAddress = void 0;
|
|
30097
|
-
return;
|
|
30098
|
-
}
|
|
30099
|
-
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
30100
|
-
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
30101
|
-
this._cachedProxyAddress = proxyAddr.toString();
|
|
30102
|
-
}
|
|
30103
30456
|
/**
|
|
30104
30457
|
* Register a nametag for the current active address
|
|
30105
30458
|
* Each address can have its own independent nametag
|
|
@@ -30127,17 +30480,6 @@ var Sphere = class _Sphere {
|
|
|
30127
30480
|
if (this._identity?.nametag) {
|
|
30128
30481
|
throw new SphereError(`Unicity ID already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`, "ALREADY_INITIALIZED");
|
|
30129
30482
|
}
|
|
30130
|
-
if (!this._payments.hasNametag()) {
|
|
30131
|
-
logger.debug("Sphere", `Minting nametag token for @${cleanNametag}...`);
|
|
30132
|
-
const result = await this.mintNametag(cleanNametag);
|
|
30133
|
-
if (!result.success) {
|
|
30134
|
-
throw new SphereError(
|
|
30135
|
-
`Failed to mint nametag token: ${result.error}`,
|
|
30136
|
-
"AGGREGATOR_ERROR"
|
|
30137
|
-
);
|
|
30138
|
-
}
|
|
30139
|
-
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
30140
|
-
}
|
|
30141
30483
|
if (this._transport.publishIdentityBinding) {
|
|
30142
30484
|
const success = await this._transport.publishIdentityBinding(
|
|
30143
30485
|
this._identity.chainPubkey,
|
|
@@ -30150,7 +30492,6 @@ var Sphere = class _Sphere {
|
|
|
30150
30492
|
}
|
|
30151
30493
|
}
|
|
30152
30494
|
this._identity.nametag = cleanNametag;
|
|
30153
|
-
await this._updateCachedProxyAddress();
|
|
30154
30495
|
const currentAddressId = this._trackedAddresses.get(this._currentAddressIndex)?.addressId;
|
|
30155
30496
|
if (currentAddressId) {
|
|
30156
30497
|
let nametags = this._addressNametags.get(currentAddressId);
|
|
@@ -30183,35 +30524,19 @@ var Sphere = class _Sphere {
|
|
|
30183
30524
|
await this._storage.saveTrackedAddresses(entries);
|
|
30184
30525
|
}
|
|
30185
30526
|
/**
|
|
30186
|
-
*
|
|
30187
|
-
* This creates the nametag token required for receiving tokens via PROXY addresses (@nametag)
|
|
30527
|
+
* Check whether a nametag is available to register.
|
|
30188
30528
|
*
|
|
30189
|
-
*
|
|
30190
|
-
*
|
|
30529
|
+
* D5: nametags are Nostr bindings (name ↔ chainPubkey), not on-chain tokens. Availability is
|
|
30530
|
+
* first-seen-wins — a name is available iff no binding resolves for it.
|
|
30191
30531
|
*
|
|
30192
|
-
* @example
|
|
30193
|
-
* ```typescript
|
|
30194
|
-
* // Mint nametag token for receiving via @alice
|
|
30195
|
-
* const result = await sphere.mintNametag('alice');
|
|
30196
|
-
* if (result.success) {
|
|
30197
|
-
* console.log('Nametag minted:', result.nametagData?.name);
|
|
30198
|
-
* } else {
|
|
30199
|
-
* console.error('Mint failed:', result.error);
|
|
30200
|
-
* }
|
|
30201
|
-
* ```
|
|
30202
|
-
*/
|
|
30203
|
-
async mintNametag(nametag) {
|
|
30204
|
-
this.ensureReady();
|
|
30205
|
-
return this._payments.mintNametag(nametag);
|
|
30206
|
-
}
|
|
30207
|
-
/**
|
|
30208
|
-
* Check if a nametag is available for minting
|
|
30209
30532
|
* @param nametag - The nametag to check (e.g., "alice" or "@alice")
|
|
30210
|
-
* @returns true if available, false if taken
|
|
30533
|
+
* @returns true if available, false if already taken
|
|
30211
30534
|
*/
|
|
30212
30535
|
async isNametagAvailable(nametag) {
|
|
30213
30536
|
this.ensureReady();
|
|
30214
|
-
|
|
30537
|
+
if (!this._transport.resolveNametag) return true;
|
|
30538
|
+
const bound = await this._transport.resolveNametag(this.cleanNametag(nametag));
|
|
30539
|
+
return bound == null;
|
|
30215
30540
|
}
|
|
30216
30541
|
/**
|
|
30217
30542
|
* Load tracked addresses from storage.
|
|
@@ -30386,7 +30711,6 @@ var Sphere = class _Sphere {
|
|
|
30386
30711
|
}
|
|
30387
30712
|
if (recoveredNametag && !this._identity?.nametag) {
|
|
30388
30713
|
this._identity.nametag = recoveredNametag;
|
|
30389
|
-
await this._updateCachedProxyAddress();
|
|
30390
30714
|
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
30391
30715
|
let nametags = this._addressNametags.get(entry.addressId);
|
|
30392
30716
|
if (!nametags) {
|
|
@@ -30475,7 +30799,6 @@ var Sphere = class _Sphere {
|
|
|
30475
30799
|
try {
|
|
30476
30800
|
if (this._identity) {
|
|
30477
30801
|
this._identity.nametag = recoveredNametag;
|
|
30478
|
-
await this._updateCachedProxyAddress();
|
|
30479
30802
|
}
|
|
30480
30803
|
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
30481
30804
|
let nametags = this._addressNametags.get(entry.addressId);
|
|
@@ -30495,7 +30818,7 @@ var Sphere = class _Sphere {
|
|
|
30495
30818
|
*/
|
|
30496
30819
|
cleanNametag(raw) {
|
|
30497
30820
|
const stripped = raw.startsWith("@") ? raw.slice(1) : raw;
|
|
30498
|
-
return (0,
|
|
30821
|
+
return (0, import_nostr_js_sdk4.normalizeNametag)(stripped);
|
|
30499
30822
|
}
|
|
30500
30823
|
// ===========================================================================
|
|
30501
30824
|
// Public Methods - Lifecycle
|
|
@@ -30675,7 +30998,6 @@ var Sphere = class _Sphere {
|
|
|
30675
30998
|
} else if (this._identity && nametag) {
|
|
30676
30999
|
this._identity.nametag = nametag;
|
|
30677
31000
|
}
|
|
30678
|
-
await this._updateCachedProxyAddress();
|
|
30679
31001
|
}
|
|
30680
31002
|
async initializeIdentityFromMnemonic(mnemonic, derivationPath) {
|
|
30681
31003
|
const basePath = derivationPath ?? DEFAULT_BASE_PATH;
|
|
@@ -30826,10 +31148,45 @@ var Sphere = class _Sphere {
|
|
|
30826
31148
|
this._providerEventCleanups = [];
|
|
30827
31149
|
this._lastProviderConnected.clear();
|
|
30828
31150
|
}
|
|
31151
|
+
/**
|
|
31152
|
+
* Construct the v2 token engine for a given address identity (defaults to the
|
|
31153
|
+
* active one) from the oracle's gateway URL + trust base and that address's
|
|
31154
|
+
* signing key. The engine is per-address — each address signs with its own key.
|
|
31155
|
+
* The trust base is the single source of truth for the network id (so any id
|
|
31156
|
+
* works — e.g. testnet2 = 4 — with no enum entry). Returns undefined (modules
|
|
31157
|
+
* keep their legacy path) when the oracle can't supply a trust base / url, or
|
|
31158
|
+
* construction fails — a misconfigured oracle never breaks initialization.
|
|
31159
|
+
*/
|
|
31160
|
+
async buildTokenEngine(identity) {
|
|
31161
|
+
const oracle = this._oracle;
|
|
31162
|
+
const privateKey = (identity ?? this._identity)?.privateKey;
|
|
31163
|
+
const trustBaseJson = oracle.getTrustBaseJson?.() ?? null;
|
|
31164
|
+
const aggregatorUrl = oracle.getAggregatorUrl?.();
|
|
31165
|
+
if (!trustBaseJson || !aggregatorUrl || !privateKey) {
|
|
31166
|
+
logger.warn("Sphere", "v2 token engine not constructed (oracle has no trust base / url, or no identity) \u2014 legacy path");
|
|
31167
|
+
return void 0;
|
|
31168
|
+
}
|
|
31169
|
+
try {
|
|
31170
|
+
return await createSphereTokenEngine({
|
|
31171
|
+
aggregatorUrl,
|
|
31172
|
+
apiKey: oracle.getApiKey?.(),
|
|
31173
|
+
privateKey: hexToBytes2(privateKey),
|
|
31174
|
+
trustBaseJson
|
|
31175
|
+
});
|
|
31176
|
+
} catch (err) {
|
|
31177
|
+
logger.warn(
|
|
31178
|
+
"Sphere",
|
|
31179
|
+
`Failed to construct v2 token engine \u2014 modules use the legacy path: ${err instanceof Error ? err.message : String(err)}`
|
|
31180
|
+
);
|
|
31181
|
+
return void 0;
|
|
31182
|
+
}
|
|
31183
|
+
}
|
|
30829
31184
|
async initializeModules() {
|
|
30830
31185
|
const emitEvent = this.emitEvent.bind(this);
|
|
30831
31186
|
const adapter = await this.ensureTransportMux(this._currentAddressIndex, this._identity);
|
|
30832
31187
|
const moduleTransport = adapter ?? this._transport;
|
|
31188
|
+
this._tokenEngine = await this.buildTokenEngine();
|
|
31189
|
+
const tokenEngine = this._tokenEngine;
|
|
30833
31190
|
this._payments.initialize({
|
|
30834
31191
|
identity: this._identity,
|
|
30835
31192
|
storage: this._storage,
|
|
@@ -30840,7 +31197,8 @@ var Sphere = class _Sphere {
|
|
|
30840
31197
|
// Pass chain code for L1 HD derivation
|
|
30841
31198
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
30842
31199
|
price: this._priceProvider ?? void 0,
|
|
30843
|
-
disabledProviderIds: this._disabledProviders
|
|
31200
|
+
disabledProviderIds: this._disabledProviders,
|
|
31201
|
+
tokenEngine
|
|
30844
31202
|
});
|
|
30845
31203
|
this._communications.initialize({
|
|
30846
31204
|
identity: this._identity,
|
|
@@ -30876,7 +31234,8 @@ var Sphere = class _Sphere {
|
|
|
30876
31234
|
emitEvent,
|
|
30877
31235
|
on: this.on.bind(this),
|
|
30878
31236
|
storage: this._storage,
|
|
30879
|
-
communications: this._communications
|
|
31237
|
+
communications: this._communications,
|
|
31238
|
+
tokenEngine
|
|
30880
31239
|
});
|
|
30881
31240
|
} else {
|
|
30882
31241
|
logger.warn("Sphere", "Accounting module enabled but no token storage available \u2014 disabling");
|
|
@@ -30938,6 +31297,7 @@ var Sphere = class _Sphere {
|
|
|
30938
31297
|
market: this._market,
|
|
30939
31298
|
transportAdapter: adapter,
|
|
30940
31299
|
tokenStorageProviders: new Map(this._tokenStorageProviders),
|
|
31300
|
+
tokenEngine: this._tokenEngine,
|
|
30941
31301
|
initialized: true
|
|
30942
31302
|
});
|
|
30943
31303
|
}
|
|
@@ -31288,37 +31648,22 @@ init_constants();
|
|
|
31288
31648
|
|
|
31289
31649
|
// validation/token-validator.ts
|
|
31290
31650
|
init_logger();
|
|
31651
|
+
function stateIdOf(token) {
|
|
31652
|
+
return sha2562(bytesToHex3(token.blob.token), "hex");
|
|
31653
|
+
}
|
|
31291
31654
|
var TokenValidator = class {
|
|
31292
|
-
|
|
31293
|
-
|
|
31294
|
-
skipVerification;
|
|
31295
|
-
// Cache for spent state verification
|
|
31655
|
+
engine;
|
|
31656
|
+
// Spent-status cache: SPENT is permanent (immutable), UNSPENT expires after a TTL.
|
|
31296
31657
|
spentStateCache = /* @__PURE__ */ new Map();
|
|
31297
31658
|
UNSPENT_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
31298
31659
|
// 5 minutes
|
|
31299
|
-
constructor(
|
|
31300
|
-
this.
|
|
31301
|
-
this.trustBase = options.trustBase || null;
|
|
31302
|
-
this.skipVerification = options.skipVerification || false;
|
|
31660
|
+
constructor(engine) {
|
|
31661
|
+
this.engine = engine;
|
|
31303
31662
|
}
|
|
31304
|
-
|
|
31305
|
-
* Set the aggregator client
|
|
31306
|
-
*/
|
|
31307
|
-
setAggregatorClient(client) {
|
|
31308
|
-
this.aggregatorClient = client;
|
|
31309
|
-
}
|
|
31310
|
-
/**
|
|
31311
|
-
* Set the trust base
|
|
31312
|
-
*/
|
|
31313
|
-
setTrustBase(trustBase) {
|
|
31314
|
-
this.trustBase = trustBase;
|
|
31315
|
-
}
|
|
31316
|
-
// =============================================================================
|
|
31663
|
+
// ===========================================================================
|
|
31317
31664
|
// Public API
|
|
31318
|
-
//
|
|
31319
|
-
/**
|
|
31320
|
-
* Validate all tokens (parallel with batch limit)
|
|
31321
|
-
*/
|
|
31665
|
+
// ===========================================================================
|
|
31666
|
+
/** Validate all tokens (parallel, with a batch limit). */
|
|
31322
31667
|
async validateAllTokens(tokens, options) {
|
|
31323
31668
|
const validTokens = [];
|
|
31324
31669
|
const issues = [];
|
|
@@ -31327,293 +31672,88 @@ var TokenValidator = class {
|
|
|
31327
31672
|
let completed = 0;
|
|
31328
31673
|
for (let i = 0; i < tokens.length; i += batchSize) {
|
|
31329
31674
|
const batch = tokens.slice(i, i + batchSize);
|
|
31330
|
-
const batchResults = await Promise.
|
|
31331
|
-
batch.map(async (token) => {
|
|
31332
|
-
try {
|
|
31333
|
-
const result = await this.validateToken(token);
|
|
31334
|
-
return { token, result };
|
|
31335
|
-
} catch (err) {
|
|
31336
|
-
return {
|
|
31337
|
-
token,
|
|
31338
|
-
result: {
|
|
31339
|
-
isValid: false,
|
|
31340
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
31341
|
-
}
|
|
31342
|
-
};
|
|
31343
|
-
}
|
|
31344
|
-
})
|
|
31675
|
+
const batchResults = await Promise.all(
|
|
31676
|
+
batch.map(async (token) => ({ token, result: await this.validateToken(token) }))
|
|
31345
31677
|
);
|
|
31346
|
-
for (const
|
|
31678
|
+
for (const { token, result } of batchResults) {
|
|
31347
31679
|
completed++;
|
|
31348
|
-
if (
|
|
31349
|
-
|
|
31350
|
-
if (result.isValid) {
|
|
31351
|
-
validTokens.push(token);
|
|
31352
|
-
} else {
|
|
31353
|
-
issues.push({
|
|
31354
|
-
tokenId: token.id,
|
|
31355
|
-
reason: result.reason || "Unknown validation error",
|
|
31356
|
-
recoverable: false
|
|
31357
|
-
});
|
|
31358
|
-
}
|
|
31680
|
+
if (result.isValid) {
|
|
31681
|
+
validTokens.push(token);
|
|
31359
31682
|
} else {
|
|
31360
31683
|
issues.push({
|
|
31361
|
-
tokenId:
|
|
31362
|
-
reason:
|
|
31684
|
+
tokenId: stateIdOf(token),
|
|
31685
|
+
reason: result.reason || "Unknown validation error",
|
|
31363
31686
|
recoverable: false
|
|
31364
31687
|
});
|
|
31365
31688
|
}
|
|
31366
31689
|
}
|
|
31367
|
-
|
|
31368
|
-
options.onProgress(completed, total);
|
|
31369
|
-
}
|
|
31690
|
+
options?.onProgress?.(completed, total);
|
|
31370
31691
|
}
|
|
31371
31692
|
return { validTokens, issues };
|
|
31372
31693
|
}
|
|
31373
|
-
/**
|
|
31374
|
-
* Validate a single token
|
|
31375
|
-
*/
|
|
31694
|
+
/** Validate a single token's structural integrity against the trust base. */
|
|
31376
31695
|
async validateToken(token) {
|
|
31377
|
-
|
|
31378
|
-
|
|
31379
|
-
isValid: false,
|
|
31380
|
-
reason: "Token has no SDK data"
|
|
31381
|
-
};
|
|
31382
|
-
}
|
|
31383
|
-
let txfToken;
|
|
31384
|
-
try {
|
|
31385
|
-
txfToken = JSON.parse(token.sdkData);
|
|
31386
|
-
} catch {
|
|
31387
|
-
return {
|
|
31388
|
-
isValid: false,
|
|
31389
|
-
reason: "Failed to parse token SDK data as JSON"
|
|
31390
|
-
};
|
|
31391
|
-
}
|
|
31392
|
-
if (!this.hasValidTxfStructure(txfToken)) {
|
|
31393
|
-
return {
|
|
31394
|
-
isValid: false,
|
|
31395
|
-
reason: "Token data missing required TXF fields (genesis, state)"
|
|
31396
|
-
};
|
|
31397
|
-
}
|
|
31398
|
-
const uncommitted = this.getUncommittedTransactions(txfToken);
|
|
31399
|
-
if (uncommitted.length > 0) {
|
|
31400
|
-
return {
|
|
31401
|
-
isValid: false,
|
|
31402
|
-
reason: `${uncommitted.length} uncommitted transaction(s)`
|
|
31403
|
-
};
|
|
31404
|
-
}
|
|
31405
|
-
if (this.trustBase && !this.skipVerification) {
|
|
31406
|
-
try {
|
|
31407
|
-
const verificationResult = await this.verifyWithSdk(txfToken);
|
|
31408
|
-
if (!verificationResult.success) {
|
|
31409
|
-
return {
|
|
31410
|
-
isValid: false,
|
|
31411
|
-
reason: verificationResult.error || "SDK verification failed"
|
|
31412
|
-
};
|
|
31413
|
-
}
|
|
31414
|
-
} catch (err) {
|
|
31415
|
-
logger.warn("Validation", "SDK verification skipped:", err instanceof Error ? err.message : err);
|
|
31416
|
-
}
|
|
31417
|
-
}
|
|
31418
|
-
return { isValid: true };
|
|
31696
|
+
const result = await this.engine.verify(token);
|
|
31697
|
+
return result.ok ? { isValid: true } : { isValid: false, reason: result.reason || "Verification failed" };
|
|
31419
31698
|
}
|
|
31420
31699
|
/**
|
|
31421
|
-
*
|
|
31700
|
+
* Whether a token's current state has been spent on the network. Cached:
|
|
31701
|
+
* SPENT permanently, UNSPENT for `UNSPENT_CACHE_TTL_MS`. Graceful — an engine
|
|
31702
|
+
* error is treated as unspent (and not cached).
|
|
31422
31703
|
*/
|
|
31423
|
-
async
|
|
31424
|
-
|
|
31425
|
-
|
|
31426
|
-
|
|
31427
|
-
|
|
31428
|
-
|
|
31429
|
-
if (cached !== void 0) {
|
|
31430
|
-
if (cached.isSpent) {
|
|
31431
|
-
return true;
|
|
31432
|
-
}
|
|
31433
|
-
if (Date.now() - cached.timestamp < this.UNSPENT_CACHE_TTL_MS) {
|
|
31434
|
-
return false;
|
|
31435
|
-
}
|
|
31704
|
+
async isSpent(token) {
|
|
31705
|
+
const key = stateIdOf(token);
|
|
31706
|
+
const cached = this.spentStateCache.get(key);
|
|
31707
|
+
if (cached) {
|
|
31708
|
+
if (cached.isSpent) return true;
|
|
31709
|
+
if (Date.now() - cached.timestamp < this.UNSPENT_CACHE_TTL_MS) return false;
|
|
31436
31710
|
}
|
|
31711
|
+
let spent;
|
|
31437
31712
|
try {
|
|
31438
|
-
|
|
31439
|
-
const { DataHash } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHash");
|
|
31440
|
-
const pubKeyBytes = Buffer.from(publicKey, "hex");
|
|
31441
|
-
const stateHashObj = DataHash.fromJSON(stateHash);
|
|
31442
|
-
const requestId2 = await RequestId.create(pubKeyBytes, stateHashObj);
|
|
31443
|
-
const response = await this.aggregatorClient.getInclusionProof(requestId2);
|
|
31444
|
-
let isSpent = false;
|
|
31445
|
-
if (response.inclusionProof) {
|
|
31446
|
-
const proof = response.inclusionProof;
|
|
31447
|
-
const pathResult = await proof.merkleTreePath.verify(
|
|
31448
|
-
requestId2.toBitString().toBigInt()
|
|
31449
|
-
);
|
|
31450
|
-
if (pathResult.isPathValid && pathResult.isPathIncluded && proof.authenticator !== null) {
|
|
31451
|
-
isSpent = true;
|
|
31452
|
-
}
|
|
31453
|
-
}
|
|
31454
|
-
this.spentStateCache.set(cacheKey, {
|
|
31455
|
-
isSpent,
|
|
31456
|
-
timestamp: Date.now()
|
|
31457
|
-
});
|
|
31458
|
-
return isSpent;
|
|
31713
|
+
spent = await this.engine.isSpent(token);
|
|
31459
31714
|
} catch (err) {
|
|
31460
|
-
logger.warn("Validation", "Error checking
|
|
31715
|
+
logger.warn("Validation", "Error checking spent status:", err);
|
|
31461
31716
|
return false;
|
|
31462
31717
|
}
|
|
31718
|
+
this.spentStateCache.set(key, { isSpent: spent, timestamp: Date.now() });
|
|
31719
|
+
return spent;
|
|
31463
31720
|
}
|
|
31464
|
-
/**
|
|
31465
|
-
|
|
31466
|
-
*
|
|
31467
|
-
* Follows the same approach as the Sphere webgui TokenValidationService:
|
|
31468
|
-
* 1. Parse TXF using SDK's Token.fromJSON()
|
|
31469
|
-
* 2. Calculate CURRENT state hash via sdkToken.state.calculateHash()
|
|
31470
|
-
* 3. Create RequestId via RequestId.create(walletPubKey, calculatedHash)
|
|
31471
|
-
*
|
|
31472
|
-
* Uses wallet's own pubkey (not source state predicate key) because "spent" means
|
|
31473
|
-
* the CURRENT OWNER committed this state. Using the source state key would falsely
|
|
31474
|
-
* detect received tokens as "spent" (sender's commitment matches source state).
|
|
31475
|
-
*/
|
|
31476
|
-
async checkSpentTokens(tokens, publicKey, options) {
|
|
31721
|
+
/** Check which of the given tokens are spent, returning them by per-state id. */
|
|
31722
|
+
async checkSpentTokens(tokens, options) {
|
|
31477
31723
|
const spentTokens = [];
|
|
31478
31724
|
const errors = [];
|
|
31479
|
-
if (!this.aggregatorClient) {
|
|
31480
|
-
errors.push("Aggregator client not available");
|
|
31481
|
-
return { spentTokens, errors };
|
|
31482
|
-
}
|
|
31483
31725
|
const batchSize = options?.batchSize ?? 3;
|
|
31484
31726
|
const total = tokens.length;
|
|
31485
31727
|
let completed = 0;
|
|
31486
|
-
const { Token: SdkToken5 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
31487
|
-
const { RequestId } = await import("@unicitylabs/state-transition-sdk/lib/api/RequestId");
|
|
31488
|
-
const pubKeyBytes = Buffer.from(publicKey, "hex");
|
|
31489
31728
|
for (let i = 0; i < tokens.length; i += batchSize) {
|
|
31490
31729
|
const batch = tokens.slice(i, i + batchSize);
|
|
31491
31730
|
const batchResults = await Promise.allSettled(
|
|
31492
|
-
batch.map(async (token) => {
|
|
31493
|
-
try {
|
|
31494
|
-
const txf = tokenToTxf(token);
|
|
31495
|
-
if (!txf) {
|
|
31496
|
-
return { tokenId: token.id, localId: token.id, stateHash: "", spent: false, error: "Invalid TXF" };
|
|
31497
|
-
}
|
|
31498
|
-
const tokenId = txf.genesis?.data?.tokenId || token.id;
|
|
31499
|
-
const sdkToken = await SdkToken5.fromJSON(txf);
|
|
31500
|
-
const calculatedStateHash = await sdkToken.state.calculateHash();
|
|
31501
|
-
const calculatedStateHashStr = calculatedStateHash.toJSON();
|
|
31502
|
-
const cacheKey = `${tokenId}:${calculatedStateHashStr}:${publicKey}`;
|
|
31503
|
-
const cached = this.spentStateCache.get(cacheKey);
|
|
31504
|
-
if (cached !== void 0) {
|
|
31505
|
-
if (cached.isSpent) {
|
|
31506
|
-
return { tokenId, localId: token.id, stateHash: calculatedStateHashStr, spent: true };
|
|
31507
|
-
}
|
|
31508
|
-
if (Date.now() - cached.timestamp < this.UNSPENT_CACHE_TTL_MS) {
|
|
31509
|
-
return { tokenId, localId: token.id, stateHash: calculatedStateHashStr, spent: false };
|
|
31510
|
-
}
|
|
31511
|
-
}
|
|
31512
|
-
const { DataHash } = await import("@unicitylabs/state-transition-sdk/lib/hash/DataHash");
|
|
31513
|
-
const stateHashObj = DataHash.fromJSON(calculatedStateHashStr);
|
|
31514
|
-
const requestId2 = await RequestId.create(pubKeyBytes, stateHashObj);
|
|
31515
|
-
const response = await this.aggregatorClient.getInclusionProof(requestId2);
|
|
31516
|
-
let isSpent = false;
|
|
31517
|
-
if (response.inclusionProof) {
|
|
31518
|
-
const proof = response.inclusionProof;
|
|
31519
|
-
const pathResult = await proof.merkleTreePath.verify(
|
|
31520
|
-
requestId2.toBitString().toBigInt()
|
|
31521
|
-
);
|
|
31522
|
-
if (pathResult.isPathValid && pathResult.isPathIncluded && proof.authenticator !== null) {
|
|
31523
|
-
isSpent = true;
|
|
31524
|
-
}
|
|
31525
|
-
}
|
|
31526
|
-
this.spentStateCache.set(cacheKey, {
|
|
31527
|
-
isSpent,
|
|
31528
|
-
timestamp: Date.now()
|
|
31529
|
-
});
|
|
31530
|
-
return { tokenId, localId: token.id, stateHash: calculatedStateHashStr, spent: isSpent };
|
|
31531
|
-
} catch (err) {
|
|
31532
|
-
return {
|
|
31533
|
-
tokenId: token.id,
|
|
31534
|
-
localId: token.id,
|
|
31535
|
-
stateHash: "",
|
|
31536
|
-
spent: false,
|
|
31537
|
-
error: err instanceof Error ? err.message : String(err)
|
|
31538
|
-
};
|
|
31539
|
-
}
|
|
31540
|
-
})
|
|
31731
|
+
batch.map(async (token) => ({ stateId: stateIdOf(token), spent: await this.isSpent(token) }))
|
|
31541
31732
|
);
|
|
31542
31733
|
for (const result of batchResults) {
|
|
31543
31734
|
completed++;
|
|
31544
31735
|
if (result.status === "fulfilled") {
|
|
31545
|
-
if (result.value.spent) {
|
|
31546
|
-
spentTokens.push({
|
|
31547
|
-
tokenId: result.value.tokenId,
|
|
31548
|
-
localId: result.value.localId,
|
|
31549
|
-
stateHash: result.value.stateHash
|
|
31550
|
-
});
|
|
31551
|
-
}
|
|
31552
|
-
if (result.value.error) {
|
|
31553
|
-
errors.push(`Token ${result.value.tokenId}: ${result.value.error}`);
|
|
31554
|
-
}
|
|
31736
|
+
if (result.value.spent) spentTokens.push({ stateId: result.value.stateId });
|
|
31555
31737
|
} else {
|
|
31556
31738
|
errors.push(String(result.reason));
|
|
31557
31739
|
}
|
|
31558
31740
|
}
|
|
31559
|
-
|
|
31560
|
-
options.onProgress(completed, total);
|
|
31561
|
-
}
|
|
31741
|
+
options?.onProgress?.(completed, total);
|
|
31562
31742
|
}
|
|
31563
31743
|
return { spentTokens, errors };
|
|
31564
31744
|
}
|
|
31565
|
-
/**
|
|
31566
|
-
* Clear the spent state cache
|
|
31567
|
-
*/
|
|
31745
|
+
/** Clear the spent-status cache. */
|
|
31568
31746
|
clearSpentStateCache() {
|
|
31569
31747
|
this.spentStateCache.clear();
|
|
31570
31748
|
}
|
|
31571
|
-
// =============================================================================
|
|
31572
|
-
// Private Helpers
|
|
31573
|
-
// =============================================================================
|
|
31574
|
-
hasValidTxfStructure(obj) {
|
|
31575
|
-
if (!obj || typeof obj !== "object") return false;
|
|
31576
|
-
const txf = obj;
|
|
31577
|
-
return !!(txf.genesis && typeof txf.genesis === "object" && txf.state && typeof txf.state === "object");
|
|
31578
|
-
}
|
|
31579
|
-
getUncommittedTransactions(txfToken) {
|
|
31580
|
-
const txf = txfToken;
|
|
31581
|
-
const transactions = txf.transactions;
|
|
31582
|
-
if (!transactions || !Array.isArray(transactions)) {
|
|
31583
|
-
return [];
|
|
31584
|
-
}
|
|
31585
|
-
return transactions.filter((tx) => tx.inclusionProof === null);
|
|
31586
|
-
}
|
|
31587
|
-
async verifyWithSdk(txfToken) {
|
|
31588
|
-
try {
|
|
31589
|
-
const { Token: Token5 } = await import("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
31590
|
-
const sdkToken = await Token5.fromJSON(txfToken);
|
|
31591
|
-
if (!this.trustBase) {
|
|
31592
|
-
return { success: true };
|
|
31593
|
-
}
|
|
31594
|
-
const result = await sdkToken.verify(this.trustBase);
|
|
31595
|
-
if (!result.isSuccessful) {
|
|
31596
|
-
return {
|
|
31597
|
-
success: false,
|
|
31598
|
-
error: String(result) || "Verification failed"
|
|
31599
|
-
};
|
|
31600
|
-
}
|
|
31601
|
-
return { success: true };
|
|
31602
|
-
} catch (err) {
|
|
31603
|
-
return {
|
|
31604
|
-
success: false,
|
|
31605
|
-
error: err instanceof Error ? err.message : String(err)
|
|
31606
|
-
};
|
|
31607
|
-
}
|
|
31608
|
-
}
|
|
31609
31749
|
};
|
|
31610
|
-
function createTokenValidator(
|
|
31611
|
-
return new TokenValidator(
|
|
31750
|
+
function createTokenValidator(engine) {
|
|
31751
|
+
return new TokenValidator(engine);
|
|
31612
31752
|
}
|
|
31613
31753
|
|
|
31614
31754
|
// index.ts
|
|
31755
|
+
var import_nostr_js_sdk5 = require("@unicitylabs/nostr-js-sdk");
|
|
31615
31756
|
var import_nostr_js_sdk6 = require("@unicitylabs/nostr-js-sdk");
|
|
31616
|
-
var import_nostr_js_sdk7 = require("@unicitylabs/nostr-js-sdk");
|
|
31617
31757
|
|
|
31618
31758
|
// price/CoinGeckoPriceProvider.ts
|
|
31619
31759
|
init_logger();
|