@veridex/sdk 1.0.0-beta.16 → 1.0.0-beta.18

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.
Files changed (37) hide show
  1. package/README.md +163 -111
  2. package/dist/chains/aptos/index.d.mts +1 -1
  3. package/dist/chains/aptos/index.d.ts +1 -1
  4. package/dist/chains/evm/index.d.mts +3 -3
  5. package/dist/chains/evm/index.d.ts +3 -3
  6. package/dist/chains/solana/index.d.mts +1 -1
  7. package/dist/chains/solana/index.d.ts +1 -1
  8. package/dist/chains/stacks/index.d.mts +559 -0
  9. package/dist/chains/stacks/index.d.ts +559 -0
  10. package/dist/chains/stacks/index.js +1207 -0
  11. package/dist/chains/stacks/index.js.map +1 -0
  12. package/dist/chains/stacks/index.mjs +1149 -0
  13. package/dist/chains/stacks/index.mjs.map +1 -0
  14. package/dist/chains/starknet/index.d.mts +2 -2
  15. package/dist/chains/starknet/index.d.ts +2 -2
  16. package/dist/chains/sui/index.d.mts +2 -2
  17. package/dist/chains/sui/index.d.ts +2 -2
  18. package/dist/{index-eadz7SCP.d.mts → index-Dy29mvBf.d.mts} +28 -10
  19. package/dist/{index-ruSjoF2m.d.ts → index-eXXqodd0.d.ts} +28 -10
  20. package/dist/index.d.mts +165 -11
  21. package/dist/index.d.ts +165 -11
  22. package/dist/index.js +1582 -134
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +1536 -134
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/queries/index.js +36 -1
  27. package/dist/queries/index.js.map +1 -1
  28. package/dist/queries/index.mjs +36 -1
  29. package/dist/queries/index.mjs.map +1 -1
  30. package/dist/{types-ChIsqCiw.d.mts → types-DakHNZIP.d.mts} +7 -1
  31. package/dist/{types-ChIsqCiw.d.ts → types-DakHNZIP.d.ts} +7 -1
  32. package/dist/{types-FJL7j6gQ.d.mts → types-DvFRnIBd.d.mts} +1 -1
  33. package/dist/{types-FJL7j6gQ.d.ts → types-DvFRnIBd.d.ts} +1 -1
  34. package/dist/types.d.mts +6 -0
  35. package/dist/types.d.ts +6 -0
  36. package/dist/types.js.map +1 -1
  37. package/package.json +7 -1
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ __export(src_exports, {
37
37
  ACTION_TYPES: () => ACTION_TYPES,
38
38
  ARBITRUM_SEPOLIA_TOKENS: () => ARBITRUM_SEPOLIA_TOKENS,
39
39
  AUTH_MESSAGE_TYPES: () => AUTH_MESSAGE_TYPES,
40
+ AptosClient: () => AptosClient,
40
41
  BASE_SEPOLIA_TOKENS: () => BASE_SEPOLIA_TOKENS,
41
42
  BalanceManager: () => BalanceManager,
42
43
  CHAIN_DISPLAY_INFO: () => CHAIN_DISPLAY_INFO,
@@ -49,35 +50,53 @@ __export(src_exports, {
49
50
  CrossOriginAuth: () => CrossOriginAuth,
50
51
  DEFAULT_AUTH_PORTAL_URL: () => DEFAULT_AUTH_PORTAL_URL,
51
52
  DEFAULT_REFRESH_BUFFER: () => DEFAULT_REFRESH_BUFFER,
53
+ DEFAULT_RELAYER_URL: () => DEFAULT_RELAYER_URL,
52
54
  DEFAULT_SESSION_DURATION: () => DEFAULT_SESSION_DURATION,
55
+ ERC8004_CHAINS: () => ERC8004_CHAINS,
56
+ ERC8004_MAINNET_IDENTITY: () => ERC8004_MAINNET_IDENTITY,
57
+ ERC8004_MAINNET_REPUTATION: () => ERC8004_MAINNET_REPUTATION,
58
+ ERC8004_TESTNET_IDENTITY: () => ERC8004_TESTNET_IDENTITY,
59
+ ERC8004_TESTNET_REPUTATION: () => ERC8004_TESTNET_REPUTATION,
53
60
  ERROR_MESSAGES: () => ERROR_MESSAGES,
54
61
  ERROR_RANGES: () => ERROR_RANGES,
55
62
  ETHEREUM_SEPOLIA_TOKENS: () => ETHEREUM_SEPOLIA_TOKENS,
63
+ EVMClient: () => EVMClient,
56
64
  EVMHubClientAdapter: () => EVMHubClientAdapter,
57
65
  EVM_ZERO_ADDRESS: () => EVM_ZERO_ADDRESS,
58
66
  GUARDIAN_CONFIG: () => GUARDIAN_CONFIG,
59
67
  GasSponsor: () => GasSponsor,
60
68
  HUB_ABI: () => HUB_ABI,
69
+ IDENTITY_REGISTRY_ABI: () => IDENTITY_REGISTRY_ABI,
70
+ IDENTITY_REGISTRY_READ_ABI: () => IDENTITY_REGISTRY_READ_ABI,
61
71
  IndexedDBSessionStorage: () => IndexedDBSessionStorage,
62
72
  LIMIT_PRESETS: () => LIMIT_PRESETS,
63
73
  LocalStorageSessionStorage: () => LocalStorageSessionStorage,
64
74
  MAINNET_CHAINS: () => MAINNET_CHAINS,
65
75
  MAX_SESSION_DURATION: () => MAX_SESSION_DURATION,
66
76
  MIN_SESSION_DURATION: () => MIN_SESSION_DURATION,
77
+ MONAD_TESTNET_TOKENS: () => MONAD_TESTNET_TOKENS,
67
78
  NATIVE_TOKEN_ADDRESS: () => NATIVE_TOKEN_ADDRESS,
68
79
  OPTIMISM_SEPOLIA_TOKENS: () => OPTIMISM_SEPOLIA_TOKENS,
69
80
  PROTOCOL_VERSION: () => PROTOCOL_VERSION,
70
81
  PasskeyManager: () => PasskeyManager,
71
82
  QueryHubStateError: () => QueryHubStateError,
72
83
  QueryPortfolioError: () => QueryPortfolioError,
84
+ REPUTATION_REGISTRY_ABI: () => REPUTATION_REGISTRY_ABI,
85
+ REPUTATION_REGISTRY_READ_ABI: () => REPUTATION_REGISTRY_READ_ABI,
73
86
  RelayerClient: () => RelayerClient,
87
+ STACKS_ACTION_TYPES: () => STACKS_ACTION_TYPES,
74
88
  SessionError: () => SessionError,
75
89
  SessionManager: () => SessionManager,
90
+ SolanaClient: () => SolanaClient,
76
91
  SpendingLimitsManager: () => SpendingLimitsManager,
92
+ StacksClient: () => StacksClient,
93
+ StarknetClient: () => StarknetClient,
94
+ SuiClient: () => SuiClient,
77
95
  TESTNET_CHAINS: () => TESTNET_CHAINS,
78
96
  TOKEN_REGISTRY: () => TOKEN_REGISTRY,
79
97
  TransactionParser: () => TransactionParser,
80
98
  TransactionTracker: () => TransactionTracker,
99
+ VALIDATION_REGISTRY_ABI: () => VALIDATION_REGISTRY_ABI,
81
100
  VAULT_ABI: () => VAULT_ABI,
82
101
  VAULT_FACTORY_ABI: () => VAULT_FACTORY_ABI,
83
102
  VERIDEX_ERRORS: () => VERIDEX_ERRORS,
@@ -95,6 +114,10 @@ __export(src_exports, {
95
114
  base64URLEncode: () => base64URLEncode,
96
115
  buildChallenge: () => buildChallenge,
97
116
  buildGaslessChallenge: () => buildGaslessChallenge,
117
+ buildSbtcWithdrawalPostConditions: () => buildSbtcWithdrawalPostConditions,
118
+ buildStacksExecutePostConditions: () => buildExecutePostConditions,
119
+ buildStxDepositPostConditions: () => buildStxDepositPostConditions,
120
+ buildStxWithdrawalPostConditions: () => buildStxWithdrawalPostConditions,
98
121
  calculatePercentage: () => calculatePercentage,
99
122
  computeKeyHash: () => computeKeyHash,
100
123
  computeSessionKeyHash: () => computeSessionKeyHash,
@@ -155,11 +178,16 @@ __export(src_exports, {
155
178
  getChainPreset: () => getChainPreset,
156
179
  getConfigTypeName: () => getConfigTypeName,
157
180
  getDefaultHub: () => getDefaultHub,
181
+ getERC8004Addresses: () => getERC8004Addresses,
158
182
  getErrorCategory: () => getErrorCategory,
159
183
  getErrorMessage: () => getErrorMessage,
160
184
  getExplorerUrl: () => getExplorerUrl,
161
185
  getHubChains: () => getHubChains,
162
186
  getSequenceFromTxReceipt: () => getSequenceFromTxReceipt,
187
+ getStacksContractPrincipal: () => getContractPrincipal,
188
+ getStacksExplorerAddressUrl: () => getStacksExplorerAddressUrl,
189
+ getStacksExplorerTxUrl: () => getStacksExplorerTxUrl,
190
+ getStacksNetworkFromAddress: () => getNetworkFromAddress,
163
191
  getSuggestedAction: () => getSuggestedAction,
164
192
  getSupportedChainIds: () => getSupportedChainIds,
165
193
  getSupportedChains: () => getSupportedChains,
@@ -175,20 +203,26 @@ __export(src_exports, {
175
203
  isAbiError: () => isAbiError,
176
204
  isChainSupported: () => isChainSupported,
177
205
  isCoreError: () => isCoreError,
206
+ isERC8004Chain: () => isERC8004Chain,
178
207
  isEvmChain: () => isEvmChain,
179
208
  isNativeToken: () => isNativeToken,
180
209
  isQueryError: () => isQueryError,
181
210
  isQueryExecutionError: () => isQueryExecutionError,
182
211
  isQueryParsingError: () => isQueryParsingError,
183
212
  isRetryableError: () => isRetryableError,
213
+ isStacksContractPrincipal: () => isContractPrincipal,
184
214
  isValidBytes32: () => isValidBytes32,
185
215
  isValidEvmAddress: () => isValidEvmAddress,
216
+ isValidStacksContractName: () => isValidContractName,
217
+ isValidStacksPrincipal: () => isValidStacksPrincipal,
218
+ isValidStacksStandardPrincipal: () => isValidStandardPrincipal,
186
219
  isValidWormholeChainId: () => isValidWormholeChainId,
187
220
  logTransactionSummary: () => logTransactionSummary,
188
221
  normalizeEmitterAddress: () => normalizeEmitterAddress,
189
222
  padTo32Bytes: () => padTo32Bytes,
190
223
  parseAmount: () => parseAmount,
191
224
  parseDERSignature: () => parseDERSignature,
225
+ parseStacksContractPrincipal: () => parseContractPrincipal,
192
226
  parseVAA: () => parseVAA,
193
227
  parseVAABytes: () => parseVAABytes,
194
228
  parseVeridexError: () => parseVeridexError,
@@ -200,10 +234,22 @@ __export(src_exports, {
200
234
  sendAuthResponse: () => sendAuthResponse,
201
235
  signWithSessionKey: () => signWithSessionKey,
202
236
  solanaAddressToBytes32: () => solanaAddressToBytes32,
237
+ stacksBuildExecuteHash: () => buildExecuteHash,
238
+ stacksBuildRegistrationHash: () => buildRegistrationHash,
239
+ stacksBuildRevocationHash: () => buildRevocationHash,
240
+ stacksBuildSessionRegistrationHash: () => buildSessionRegistrationHash,
241
+ stacksBuildWithdrawalHash: () => buildWithdrawalHash,
242
+ stacksCompressPublicKey: () => compressPublicKey,
243
+ stacksComputeKeyHash: () => computeKeyHash2,
244
+ stacksComputeKeyHashFromCoords: () => computeKeyHashFromCoords,
245
+ stacksDerToCompactSignature: () => derToCompactSignature,
246
+ stacksRsToCompactSignature: () => rsToCompactSignature,
247
+ supportsRelatedOrigins: () => supportsRelatedOrigins,
203
248
  supportsRelayer: () => supportsRelayer,
204
249
  trimTo20Bytes: () => trimTo20Bytes,
205
250
  validateEmitter: () => validateEmitter,
206
251
  validateSessionConfig: () => validateSessionConfig,
252
+ validateStacksPostConditions: () => validatePostConditions,
207
253
  verifySessionSignature: () => verifySessionSignature,
208
254
  waitForGuardianSignatures: () => waitForGuardianSignatures
209
255
  });
@@ -753,17 +799,36 @@ function sleep(ms) {
753
799
  }
754
800
 
755
801
  // src/core/PasskeyManager.ts
756
- function detectRpId() {
802
+ var VERIDEX_RP_ID = "veridex.network";
803
+ function detectRpId(forceLocal) {
757
804
  if (typeof window === "undefined") return "localhost";
758
805
  const hostname = window.location.hostname;
759
806
  if (hostname === "localhost" || hostname === "127.0.0.1" || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
760
807
  return hostname;
761
808
  }
762
- const parts = hostname.split(".");
763
- if (parts.length <= 2) {
764
- return hostname;
809
+ if (forceLocal) {
810
+ const parts = hostname.split(".");
811
+ if (parts.length <= 2) {
812
+ return hostname;
813
+ }
814
+ return parts.slice(-2).join(".");
815
+ }
816
+ return VERIDEX_RP_ID;
817
+ }
818
+ async function supportsRelatedOrigins() {
819
+ if (typeof window === "undefined" || !window.PublicKeyCredential) {
820
+ return false;
821
+ }
822
+ if ("getClientCapabilities" in PublicKeyCredential) {
823
+ try {
824
+ const getCapabilities = PublicKeyCredential.getClientCapabilities;
825
+ const capabilities = await getCapabilities();
826
+ return capabilities?.relatedOrigins === true;
827
+ } catch {
828
+ return false;
829
+ }
765
830
  }
766
- return parts.slice(-2).join(".");
831
+ return false;
767
832
  }
768
833
  var PasskeyManager = class _PasskeyManager {
769
834
  config;
@@ -1874,11 +1939,46 @@ var ETHEREUM_SEPOLIA_TOKENS = {
1874
1939
  }
1875
1940
  ]
1876
1941
  };
1942
+ var MONAD_TESTNET_TOKENS = {
1943
+ wormholeChainId: 10048,
1944
+ chainName: "Monad Testnet",
1945
+ nativeToken: {
1946
+ symbol: "MON",
1947
+ name: "Monad",
1948
+ decimals: 18,
1949
+ address: NATIVE_TOKEN_ADDRESS,
1950
+ isNative: true
1951
+ },
1952
+ tokens: [
1953
+ {
1954
+ symbol: "USDC",
1955
+ name: "USD Coin",
1956
+ decimals: 6,
1957
+ address: "0x754704Bc059F8C67012fEd69BC8A327a5aafb603",
1958
+ isNative: false
1959
+ },
1960
+ {
1961
+ symbol: "AUSD",
1962
+ name: "Agora Dollar",
1963
+ decimals: 18,
1964
+ address: "0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a",
1965
+ isNative: false
1966
+ },
1967
+ {
1968
+ symbol: "WMON",
1969
+ name: "Wrapped MON",
1970
+ decimals: 18,
1971
+ address: "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A",
1972
+ isNative: false
1973
+ }
1974
+ ]
1975
+ };
1877
1976
  var TOKEN_REGISTRY = {
1878
1977
  10004: BASE_SEPOLIA_TOKENS,
1879
1978
  10005: OPTIMISM_SEPOLIA_TOKENS,
1880
1979
  10003: ARBITRUM_SEPOLIA_TOKENS,
1881
- 10002: ETHEREUM_SEPOLIA_TOKENS
1980
+ 10002: ETHEREUM_SEPOLIA_TOKENS,
1981
+ 10048: MONAD_TESTNET_TOKENS
1882
1982
  };
1883
1983
  function getTokenList(wormholeChainId) {
1884
1984
  return TOKEN_REGISTRY[wormholeChainId] ?? null;
@@ -1909,9 +2009,10 @@ function getChainName(wormholeChainId) {
1909
2009
 
1910
2010
  // src/core/BalanceManager.ts
1911
2011
  var DEFAULT_RPC_URLS = {
2012
+ 10002: "https://ethereum-sepolia-rpc.publicnode.com",
2013
+ 10003: "https://sepolia-rollup.arbitrum.io/rpc",
1912
2014
  10004: "https://sepolia.base.org",
1913
- 10005: "https://sepolia.optimism.io",
1914
- 10003: "https://sepolia-rollup.arbitrum.io/rpc"
2015
+ 10005: "https://sepolia.optimism.io"
1915
2016
  };
1916
2017
  var TESTNET_TOKEN_PRICES = {
1917
2018
  ETH: 2500,
@@ -2146,9 +2247,10 @@ var DEFAULT_POLLING_INTERVAL = 2e3;
2146
2247
  var DEFAULT_REQUIRED_CONFIRMATIONS = 1;
2147
2248
  var DEFAULT_TIMEOUT = 3e5;
2148
2249
  var DEFAULT_RPC_URLS2 = {
2250
+ 10002: "https://ethereum-sepolia-rpc.publicnode.com",
2251
+ 10003: "https://sepolia-rollup.arbitrum.io/rpc",
2149
2252
  10004: "https://sepolia.base.org",
2150
- 10005: "https://sepolia.optimism.io",
2151
- 10003: "https://sepolia-rollup.arbitrum.io/rpc"
2253
+ 10005: "https://sepolia.optimism.io"
2152
2254
  };
2153
2255
  var TransactionTracker = class {
2154
2256
  config;
@@ -11238,136 +11340,1107 @@ var EVMClient = class {
11238
11340
  }
11239
11341
  };
11240
11342
 
11241
- // src/presets.ts
11242
- var CHAIN_NAMES = {
11243
- // EVM L2s (Hub-capable)
11244
- BASE: "base",
11245
- OPTIMISM: "optimism",
11246
- ARBITRUM: "arbitrum",
11247
- SCROLL: "scroll",
11248
- BLAST: "blast",
11249
- MANTLE: "mantle",
11250
- // EVM L1s
11251
- ETHEREUM: "ethereum",
11252
- POLYGON: "polygon",
11253
- BSC: "bsc",
11254
- AVALANCHE: "avalanche",
11255
- FANTOM: "fantom",
11256
- CELO: "celo",
11257
- MOONBEAM: "moonbeam",
11258
- // Non-EVM
11259
- SOLANA: "solana",
11260
- APTOS: "aptos",
11261
- SUI: "sui",
11262
- STARKNET: "starknet",
11263
- NEAR: "near",
11264
- SEI: "sei"
11343
+ // src/chains/stacks/StacksSigner.ts
11344
+ var P256_ORDER = BigInt(
11345
+ "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"
11346
+ );
11347
+ var P256_HALF_ORDER = P256_ORDER / 2n;
11348
+ function compressPublicKey(x, y) {
11349
+ const prefix = y % 2n === 0n ? 2 : 3;
11350
+ const xBytes = bigintToBytes(x, 32);
11351
+ const compressed = new Uint8Array(33);
11352
+ compressed[0] = prefix;
11353
+ compressed.set(xBytes, 1);
11354
+ return compressed;
11355
+ }
11356
+ function rsToCompactSignature(r, s) {
11357
+ const normalizedS = s > P256_HALF_ORDER ? P256_ORDER - s : s;
11358
+ const compact = new Uint8Array(64);
11359
+ compact.set(bigintToBytes(r, 32), 0);
11360
+ compact.set(bigintToBytes(normalizedS, 32), 32);
11361
+ return compact;
11362
+ }
11363
+ function parseDERSignature2(der) {
11364
+ if (der[0] !== 48) {
11365
+ throw new Error("Invalid DER signature: expected SEQUENCE tag 0x30");
11366
+ }
11367
+ let offset = 2;
11368
+ if (der[offset] !== 2) {
11369
+ throw new Error("Invalid DER signature: expected INTEGER tag 0x02 for r");
11370
+ }
11371
+ offset++;
11372
+ const rLen = der[offset];
11373
+ offset++;
11374
+ const rBytes = der.slice(offset, offset + rLen);
11375
+ offset += rLen;
11376
+ if (der[offset] !== 2) {
11377
+ throw new Error("Invalid DER signature: expected INTEGER tag 0x02 for s");
11378
+ }
11379
+ offset++;
11380
+ const sLen = der[offset];
11381
+ offset++;
11382
+ const sBytes = der.slice(offset, offset + sLen);
11383
+ return {
11384
+ r: bytesToBigint(rBytes),
11385
+ s: bytesToBigint(sBytes)
11386
+ };
11387
+ }
11388
+ function derToCompactSignature(der) {
11389
+ const { r, s } = parseDERSignature2(der);
11390
+ return rsToCompactSignature(r, s);
11391
+ }
11392
+ async function computeKeyHash2(compressedPubkey) {
11393
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", compressedPubkey.buffer);
11394
+ const hashArray = new Uint8Array(hashBuffer);
11395
+ return "0x" + bytesToHex(hashArray);
11396
+ }
11397
+ async function computeKeyHashFromCoords(x, y) {
11398
+ const compressed = compressPublicKey(x, y);
11399
+ return computeKeyHash2(compressed);
11400
+ }
11401
+ async function buildRegistrationHash(nonce) {
11402
+ const message = `veridex:register:${nonce}`;
11403
+ const encoded = new TextEncoder().encode(message);
11404
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11405
+ return new Uint8Array(hashBuffer);
11406
+ }
11407
+ async function buildSessionRegistrationHash(sessionKeyHash, duration, maxValue, nonce) {
11408
+ const cleanHash = sessionKeyHash.replace("0x", "");
11409
+ const message = `veridex:session:${cleanHash}:${duration}:${maxValue}:${nonce}`;
11410
+ const encoded = new TextEncoder().encode(message);
11411
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11412
+ return new Uint8Array(hashBuffer);
11413
+ }
11414
+ async function buildRevocationHash(sessionHash, nonce) {
11415
+ const cleanHash = sessionHash.replace("0x", "");
11416
+ const message = `veridex:revoke:${cleanHash}:${nonce}`;
11417
+ const encoded = new TextEncoder().encode(message);
11418
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11419
+ return new Uint8Array(hashBuffer);
11420
+ }
11421
+ async function buildExecuteHash(actionType, amount, recipient, nonce) {
11422
+ const message = `veridex:execute:${actionType}:${amount}:${recipient}:${nonce}`;
11423
+ const encoded = new TextEncoder().encode(message);
11424
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11425
+ return new Uint8Array(hashBuffer);
11426
+ }
11427
+ async function buildWithdrawalHash(amount, recipient, nonce) {
11428
+ const message = `veridex:withdraw:${amount}:${recipient}:${nonce}`;
11429
+ const encoded = new TextEncoder().encode(message);
11430
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11431
+ return new Uint8Array(hashBuffer);
11432
+ }
11433
+ function bigintToBytes(value, length) {
11434
+ const hex = value.toString(16).padStart(length * 2, "0");
11435
+ const bytes = new Uint8Array(length);
11436
+ for (let i = 0; i < length; i++) {
11437
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
11438
+ }
11439
+ return bytes;
11440
+ }
11441
+ function bytesToBigint(bytes) {
11442
+ let start = 0;
11443
+ while (start < bytes.length - 1 && bytes[start] === 0) {
11444
+ start++;
11445
+ }
11446
+ let result = 0n;
11447
+ for (let i = start; i < bytes.length; i++) {
11448
+ result = result << 8n | BigInt(bytes[i]);
11449
+ }
11450
+ return result;
11451
+ }
11452
+ function bytesToHex(bytes) {
11453
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
11454
+ }
11455
+ function hexToBytes(hex) {
11456
+ const clean = hex.replace("0x", "");
11457
+ const bytes = new Uint8Array(clean.length / 2);
11458
+ for (let i = 0; i < bytes.length; i++) {
11459
+ bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
11460
+ }
11461
+ return bytes;
11462
+ }
11463
+
11464
+ // src/chains/stacks/StacksAddressUtils.ts
11465
+ var STACKS_MAINNET_PREFIX = "SP";
11466
+ var STACKS_TESTNET_PREFIX = "ST";
11467
+ var C32_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
11468
+ function isValidStacksPrincipal(address) {
11469
+ if (!address || typeof address !== "string") {
11470
+ return false;
11471
+ }
11472
+ const parts = address.split(".");
11473
+ if (parts.length > 2) {
11474
+ return false;
11475
+ }
11476
+ const standardPart = parts[0];
11477
+ const contractName = parts[1];
11478
+ if (!isValidStandardPrincipal(standardPart)) {
11479
+ return false;
11480
+ }
11481
+ if (contractName !== void 0) {
11482
+ if (!isValidContractName(contractName)) {
11483
+ return false;
11484
+ }
11485
+ }
11486
+ return true;
11487
+ }
11488
+ function isValidStandardPrincipal(address) {
11489
+ if (!address || address.length < 5) {
11490
+ return false;
11491
+ }
11492
+ const prefix = address.slice(0, 2);
11493
+ if (prefix !== STACKS_MAINNET_PREFIX && prefix !== STACKS_TESTNET_PREFIX) {
11494
+ return false;
11495
+ }
11496
+ if (address.length < 38 || address.length > 42) {
11497
+ return false;
11498
+ }
11499
+ const body = address.slice(1).toUpperCase();
11500
+ for (const char of body) {
11501
+ if (!C32_ALPHABET.includes(char)) {
11502
+ return false;
11503
+ }
11504
+ }
11505
+ return true;
11506
+ }
11507
+ function isValidContractName(name) {
11508
+ if (!name || name.length === 0 || name.length > 128) {
11509
+ return false;
11510
+ }
11511
+ if (!/^[a-zA-Z]/.test(name)) {
11512
+ return false;
11513
+ }
11514
+ if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) {
11515
+ return false;
11516
+ }
11517
+ return true;
11518
+ }
11519
+ function getNetworkFromAddress(address) {
11520
+ const prefix = address.slice(0, 2);
11521
+ if (prefix === STACKS_MAINNET_PREFIX) {
11522
+ return "mainnet";
11523
+ }
11524
+ return "testnet";
11525
+ }
11526
+ function getContractPrincipal(deployerAddress, contractName) {
11527
+ if (!isValidStandardPrincipal(deployerAddress)) {
11528
+ throw new Error(`Invalid deployer address: ${deployerAddress}`);
11529
+ }
11530
+ if (!isValidContractName(contractName)) {
11531
+ throw new Error(`Invalid contract name: ${contractName}`);
11532
+ }
11533
+ return `${deployerAddress}.${contractName}`;
11534
+ }
11535
+ function parseContractPrincipal(contractPrincipal) {
11536
+ const dotIndex = contractPrincipal.indexOf(".");
11537
+ if (dotIndex === -1) {
11538
+ throw new Error(
11539
+ `Not a contract principal: ${contractPrincipal}. Expected format: address.contract-name`
11540
+ );
11541
+ }
11542
+ return {
11543
+ address: contractPrincipal.slice(0, dotIndex),
11544
+ contractName: contractPrincipal.slice(dotIndex + 1)
11545
+ };
11546
+ }
11547
+ function isContractPrincipal(address) {
11548
+ return address.includes(".");
11549
+ }
11550
+ function getStacksExplorerTxUrl(txId, network = "testnet") {
11551
+ const cleanTxId = txId.startsWith("0x") ? txId : `0x${txId}`;
11552
+ const chainParam = network === "testnet" ? "&chain=testnet" : "";
11553
+ return `https://explorer.hiro.so/txid/${cleanTxId}?${chainParam}`;
11554
+ }
11555
+ function getStacksExplorerAddressUrl(address, network = "testnet") {
11556
+ const chainParam = network === "testnet" ? "?chain=testnet" : "";
11557
+ return `https://explorer.hiro.so/address/${address}${chainParam}`;
11558
+ }
11559
+
11560
+ // src/chains/stacks/StacksClient.ts
11561
+ var STACKS_ACTION_TYPES = {
11562
+ TRANSFER_STX: 1,
11563
+ TRANSFER_SBTC: 2,
11564
+ CONTRACT_CALL: 3
11265
11565
  };
11266
- var CHAIN_PRESETS = {
11267
- // ────────────────────────────────────────────────────────────────────────
11268
- // BASE - Primary Hub Chain
11269
- // ────────────────────────────────────────────────────────────────────────
11270
- base: {
11271
- displayName: "Base",
11272
- type: "evm",
11273
- canBeHub: true,
11274
- testnet: {
11275
- name: "Base Sepolia",
11276
- chainId: 84532,
11277
- wormholeChainId: 10004,
11278
- rpcUrl: "https://sepolia.base.org",
11279
- explorerUrl: "https://sepolia.basescan.org",
11280
- isEvm: true,
11281
- contracts: {
11282
- hub: "0x66D87dE68327f48A099c5B9bE97020Feab9a7c82",
11283
- vaultFactory: "0xCFaEb5652aa2Ee60b2229dC8895B4159749C7e53",
11284
- vaultImplementation: "0x0d13367C16c6f0B24eD275CC67C7D9f42878285c",
11285
- wormholeCoreBridge: "0x79A1027a6A159502049F10906D333EC57E95F083",
11286
- tokenBridge: "0x86F55A04690fd7815A3D802bD587e83eA888B239"
11287
- }
11288
- },
11289
- mainnet: {
11290
- name: "Base",
11291
- chainId: 8453,
11292
- wormholeChainId: 30,
11293
- rpcUrl: "https://mainnet.base.org",
11294
- explorerUrl: "https://basescan.org",
11295
- isEvm: true,
11296
- contracts: {
11297
- // TODO: Deploy mainnet contracts
11298
- wormholeCoreBridge: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6",
11299
- tokenBridge: "0x8d2de8d2f73F1F4cAB472AC9A881C9b123C79627"
11300
- }
11566
+ var HIRO_API = {
11567
+ testnet: "https://api.testnet.hiro.so",
11568
+ mainnet: "https://api.hiro.so"
11569
+ };
11570
+ var StacksClient = class {
11571
+ config;
11572
+ rpcUrl;
11573
+ spokeContract;
11574
+ vaultContract;
11575
+ wormholeVerifierContract;
11576
+ vaultVaaContract;
11577
+ networkType;
11578
+ constructor(clientConfig) {
11579
+ this.networkType = clientConfig.network || "testnet";
11580
+ this.rpcUrl = clientConfig.rpcUrl || HIRO_API[this.networkType];
11581
+ if (clientConfig.spokeContractAddress && isContractPrincipal(clientConfig.spokeContractAddress)) {
11582
+ const parsed = parseContractPrincipal(clientConfig.spokeContractAddress);
11583
+ this.spokeContract = { address: parsed.address, name: parsed.contractName };
11584
+ } else {
11585
+ this.spokeContract = null;
11301
11586
  }
11302
- },
11303
- // ────────────────────────────────────────────────────────────────────────
11304
- // OPTIMISM - Secondary Hub / Spoke
11305
- // ────────────────────────────────────────────────────────────────────────
11306
- optimism: {
11307
- displayName: "Optimism",
11308
- type: "evm",
11309
- canBeHub: true,
11310
- testnet: {
11311
- name: "Optimism Sepolia",
11312
- chainId: 11155420,
11313
- wormholeChainId: 10005,
11314
- rpcUrl: "https://sepolia.optimism.io",
11315
- explorerUrl: "https://sepolia-optimism.etherscan.io",
11316
- isEvm: true,
11317
- contracts: {
11318
- vaultFactory: "0xA5653d54079ABeCe780F8d9597B2bc4B09fe464A",
11319
- vaultImplementation: "0x8099b1406485d2255ff89Ce5Ea18520802AFC150",
11320
- wormholeCoreBridge: "0x31377888146f3253211EFEf5c676D41ECe7D58Fe",
11321
- tokenBridge: "0x99737Ec4B815d816c49A385943baf0380e75c0Ac"
11322
- }
11323
- },
11324
- mainnet: {
11325
- name: "Optimism",
11326
- chainId: 10,
11327
- wormholeChainId: 24,
11328
- rpcUrl: "https://mainnet.optimism.io",
11329
- explorerUrl: "https://optimistic.etherscan.io",
11330
- isEvm: true,
11331
- contracts: {
11332
- wormholeCoreBridge: "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722",
11333
- tokenBridge: "0x1D68124e65faFC907325e3EDbF8c4d84499DAa8b"
11334
- }
11587
+ if (clientConfig.vaultContractAddress && isContractPrincipal(clientConfig.vaultContractAddress)) {
11588
+ const parsed = parseContractPrincipal(clientConfig.vaultContractAddress);
11589
+ this.vaultContract = { address: parsed.address, name: parsed.contractName };
11590
+ } else {
11591
+ this.vaultContract = null;
11335
11592
  }
11336
- },
11337
- // ────────────────────────────────────────────────────────────────────────
11338
- // ARBITRUM
11339
- // ────────────────────────────────────────────────────────────────────────
11340
- arbitrum: {
11341
- displayName: "Arbitrum",
11342
- type: "evm",
11343
- canBeHub: true,
11344
- testnet: {
11345
- name: "Arbitrum Sepolia",
11346
- chainId: 421614,
11347
- wormholeChainId: 10003,
11348
- rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
11349
- explorerUrl: "https://sepolia.arbiscan.io",
11350
- isEvm: true,
11593
+ if (clientConfig.wormholeVerifierAddress && isContractPrincipal(clientConfig.wormholeVerifierAddress)) {
11594
+ const parsed = parseContractPrincipal(clientConfig.wormholeVerifierAddress);
11595
+ this.wormholeVerifierContract = { address: parsed.address, name: parsed.contractName };
11596
+ } else {
11597
+ this.wormholeVerifierContract = null;
11598
+ }
11599
+ if (clientConfig.vaultVaaContractAddress && isContractPrincipal(clientConfig.vaultVaaContractAddress)) {
11600
+ const parsed = parseContractPrincipal(clientConfig.vaultVaaContractAddress);
11601
+ this.vaultVaaContract = { address: parsed.address, name: parsed.contractName };
11602
+ } else {
11603
+ this.vaultVaaContract = null;
11604
+ }
11605
+ if (this.spokeContract && !this.vaultContract) {
11606
+ this.vaultContract = {
11607
+ address: this.spokeContract.address,
11608
+ name: "veridex-vault"
11609
+ };
11610
+ }
11611
+ if (this.spokeContract && !this.wormholeVerifierContract) {
11612
+ this.wormholeVerifierContract = {
11613
+ address: this.spokeContract.address,
11614
+ name: "veridex-wormhole-verifier"
11615
+ };
11616
+ }
11617
+ if (this.spokeContract && !this.vaultVaaContract) {
11618
+ this.vaultVaaContract = {
11619
+ address: this.spokeContract.address,
11620
+ name: "veridex-vault-vaa"
11621
+ };
11622
+ }
11623
+ this.config = {
11624
+ name: `Stacks ${this.networkType}`,
11625
+ chainId: this.networkType === "mainnet" ? 1 : 2147483648,
11626
+ wormholeChainId: clientConfig.wormholeChainId,
11627
+ rpcUrl: this.rpcUrl,
11628
+ explorerUrl: this.networkType === "testnet" ? "https://explorer.hiro.so/?chain=testnet" : "https://explorer.hiro.so",
11629
+ isEvm: false,
11351
11630
  contracts: {
11352
- vaultFactory: "0xd36D3D5DB59d78f1E33813490F72DABC15C9B07c",
11353
- vaultImplementation: "0xB10ACf39eBF17fc33F722cBD955b7aeCB0611bc4",
11354
- wormholeCoreBridge: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35",
11355
- tokenBridge: "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"
11631
+ hub: clientConfig.spokeContractAddress,
11632
+ wormholeCoreBridge: "",
11633
+ wormholeVerifier: this.wormholeVerifierContract ? `${this.wormholeVerifierContract.address}.${this.wormholeVerifierContract.name}` : void 0,
11634
+ vaultVaa: this.vaultVaaContract ? `${this.vaultVaaContract.address}.${this.vaultVaaContract.name}` : void 0
11356
11635
  }
11357
- },
11358
- mainnet: {
11359
- name: "Arbitrum",
11360
- chainId: 42161,
11361
- wormholeChainId: 23,
11362
- rpcUrl: "https://arb1.arbitrum.io/rpc",
11363
- explorerUrl: "https://arbiscan.io",
11364
- isEvm: true,
11365
- contracts: {
11366
- wormholeCoreBridge: "0xa5f208e072434bC67592E4C49C1B991BA79BCA46",
11367
- tokenBridge: "0x0b2402144Bb366A632D14B83F244D2e0e21bD39c"
11636
+ };
11637
+ }
11638
+ // ========================================================================
11639
+ // ChainClient Interface - Configuration
11640
+ // ========================================================================
11641
+ getConfig() {
11642
+ return this.config;
11643
+ }
11644
+ // ========================================================================
11645
+ // ChainClient Interface - Nonce & Fees
11646
+ // ========================================================================
11647
+ /**
11648
+ * Get the current nonce for a user identity from the spoke contract.
11649
+ * Calls the read-only function `get-nonce` on veridex-spoke.
11650
+ */
11651
+ async getNonce(userKeyHash) {
11652
+ if (!this.spokeContract) {
11653
+ return 0n;
11654
+ }
11655
+ try {
11656
+ const result = await this.callReadOnly(
11657
+ this.spokeContract.address,
11658
+ this.spokeContract.name,
11659
+ "get-nonce",
11660
+ [`0x${userKeyHash.replace("0x", "")}`]
11661
+ );
11662
+ if (result && result.value !== void 0) {
11663
+ return BigInt(result.value);
11368
11664
  }
11665
+ return 0n;
11666
+ } catch {
11667
+ return 0n;
11369
11668
  }
11370
- },
11669
+ }
11670
+ /**
11671
+ * Get the Wormhole message fee.
11672
+ * Phase 1: No Wormhole integration, returns 0.
11673
+ */
11674
+ async getMessageFee() {
11675
+ return 0n;
11676
+ }
11677
+ // ========================================================================
11678
+ // ChainClient Interface - Payload Building
11679
+ // ========================================================================
11680
+ async buildTransferPayload(params) {
11681
+ return encodeTransferAction(params.token, params.recipient, params.amount);
11682
+ }
11683
+ async buildExecutePayload(params) {
11684
+ return encodeExecuteAction(params.target, params.value, params.data);
11685
+ }
11686
+ async buildBridgePayload(params) {
11687
+ return encodeBridgeAction(params.token, params.amount, params.destinationChain, params.recipient);
11688
+ }
11689
+ // ========================================================================
11690
+ // ChainClient Interface - Dispatch
11691
+ // ========================================================================
11692
+ /**
11693
+ * Direct dispatch is not supported on Stacks in Phase 1.
11694
+ * Stacks actions are executed via sponsored transactions through the relayer.
11695
+ */
11696
+ async dispatch(_signature, _publicKeyX, _publicKeyY, _targetChain, _actionPayload, _nonce, _signer) {
11697
+ throw new Error(
11698
+ "Direct dispatch not supported on Stacks in Phase 1. Use dispatchGasless() to route through the relayer, which sponsors Stacks transactions. Phase 2 will add Wormhole cross-chain dispatch support."
11699
+ );
11700
+ }
11701
+ /**
11702
+ * Dispatch an action via the relayer (gasless/sponsored).
11703
+ *
11704
+ * Flow:
11705
+ * 1. User signs action with Passkey (on client)
11706
+ * 2. SDK submits to relayer with targetChain=60 (Stacks)
11707
+ * 3. Relayer builds Clarity contract-call transaction
11708
+ * 4. Relayer sponsors the transaction (pays STX gas)
11709
+ * 5. Relayer broadcasts to Stacks network
11710
+ * 6. Transaction confirmed on Stacks
11711
+ */
11712
+ async dispatchGasless(signature, publicKeyX, publicKeyY, targetChain, actionPayload, nonce, relayerUrl) {
11713
+ const keyHash = await computeKeyHashFromCoords(publicKeyX, publicKeyY);
11714
+ const compressedPubkey = compressPublicKey(publicKeyX, publicKeyY);
11715
+ const compactSig = rsToCompactSignature(signature.r, signature.s);
11716
+ const request = {
11717
+ signature: {
11718
+ r: "0x" + signature.r.toString(16).padStart(64, "0"),
11719
+ s: "0x" + signature.s.toString(16).padStart(64, "0"),
11720
+ authenticatorData: signature.authenticatorData,
11721
+ clientDataJSON: signature.clientDataJSON,
11722
+ challengeIndex: signature.challengeIndex,
11723
+ typeIndex: signature.typeIndex
11724
+ },
11725
+ publicKeyX: "0x" + publicKeyX.toString(16).padStart(64, "0"),
11726
+ publicKeyY: "0x" + publicKeyY.toString(16).padStart(64, "0"),
11727
+ compressedPubkey: "0x" + bytesToHex(compressedPubkey),
11728
+ compactSignature: "0x" + bytesToHex(compactSig),
11729
+ targetChain,
11730
+ actionPayload,
11731
+ userNonce: Number(nonce)
11732
+ };
11733
+ const response = await fetch(`${relayerUrl}/api/v1/submit`, {
11734
+ method: "POST",
11735
+ headers: { "Content-Type": "application/json" },
11736
+ body: JSON.stringify(request)
11737
+ });
11738
+ if (!response.ok) {
11739
+ const errorText = await response.text().catch(() => "Unknown error");
11740
+ throw new Error(
11741
+ `Relayer submission failed: ${response.status} ${response.statusText}. Error: ${errorText}`
11742
+ );
11743
+ }
11744
+ const result = await response.json();
11745
+ return {
11746
+ transactionHash: result.transactionHash ?? result.txHash ?? result.hubTxHash ?? "",
11747
+ sequence: BigInt(result.sequence || 0),
11748
+ userKeyHash: keyHash,
11749
+ targetChain
11750
+ };
11751
+ }
11752
+ // ========================================================================
11753
+ // ChainClient Interface - Vault Management
11754
+ // ========================================================================
11755
+ /**
11756
+ * Get vault address for a user.
11757
+ * On Stacks, vaults are map-based within the vault contract.
11758
+ * The "vault address" is the vault contract principal itself.
11759
+ */
11760
+ async getVaultAddress(userKeyHash) {
11761
+ if (!this.vaultContract) {
11762
+ return null;
11763
+ }
11764
+ const exists = await this.vaultExists(userKeyHash);
11765
+ if (!exists) {
11766
+ return null;
11767
+ }
11768
+ return `${this.vaultContract.address}.${this.vaultContract.name}`;
11769
+ }
11770
+ /**
11771
+ * Compute vault address deterministically.
11772
+ * On Stacks, all vaults live in the same contract (map-based).
11773
+ */
11774
+ computeVaultAddress(_userKeyHash) {
11775
+ if (!this.vaultContract) {
11776
+ throw new Error("Vault contract not configured");
11777
+ }
11778
+ return `${this.vaultContract.address}.${this.vaultContract.name}`;
11779
+ }
11780
+ /**
11781
+ * Check if a vault (identity) exists for a user.
11782
+ * Queries the spoke contract's `identity-exists` read-only function.
11783
+ */
11784
+ async vaultExists(userKeyHash) {
11785
+ if (!this.spokeContract) {
11786
+ return false;
11787
+ }
11788
+ try {
11789
+ const result = await this.callReadOnly(
11790
+ this.spokeContract.address,
11791
+ this.spokeContract.name,
11792
+ "identity-exists",
11793
+ [`0x${userKeyHash.replace("0x", "")}`]
11794
+ );
11795
+ return result === true || result?.value === true;
11796
+ } catch {
11797
+ return false;
11798
+ }
11799
+ }
11800
+ /**
11801
+ * Create a vault (register identity) on Stacks.
11802
+ * Must be done via Hub dispatch or relayer in Phase 1.
11803
+ */
11804
+ async createVault(userKeyHash, _signer) {
11805
+ throw new Error(
11806
+ `Vault creation on Stacks requires Passkey signature verification. Use createVaultViaRelayer() for sponsored identity registration, or call register-identity directly with a signed Stacks transaction. KeyHash=${userKeyHash}`
11807
+ );
11808
+ }
11809
+ /**
11810
+ * Create a vault with a sponsor wallet.
11811
+ * On Stacks, this registers an identity via sponsored transaction.
11812
+ */
11813
+ async createVaultSponsored(userKeyHash, _sponsorPrivateKey, _rpcUrl) {
11814
+ throw new Error(
11815
+ `Sponsored vault creation on Stacks requires the user to sign with their Passkey. Use createVaultViaRelayer() which handles the sponsored transaction flow. KeyHash=${userKeyHash}`
11816
+ );
11817
+ }
11818
+ /**
11819
+ * Create a vault via the relayer (sponsored/gasless).
11820
+ * The relayer will sponsor the register-identity transaction.
11821
+ */
11822
+ async createVaultViaRelayer(userKeyHash, relayerUrl) {
11823
+ const response = await fetch(`${relayerUrl}/api/v1/stacks/vault`, {
11824
+ method: "POST",
11825
+ headers: { "Content-Type": "application/json" },
11826
+ body: JSON.stringify({
11827
+ userKeyHash,
11828
+ chainId: this.config.wormholeChainId
11829
+ })
11830
+ });
11831
+ const result = await response.json();
11832
+ if (!response.ok || !result.success) {
11833
+ throw new Error(result.error || "Failed to create vault via relayer");
11834
+ }
11835
+ return {
11836
+ address: result.vaultAddress || this.computeVaultAddress(userKeyHash),
11837
+ transactionHash: result.transactionHash || "",
11838
+ blockNumber: 0,
11839
+ gasUsed: 0n,
11840
+ alreadyExisted: result.alreadyExists || false,
11841
+ sponsoredBy: "relayer"
11842
+ };
11843
+ }
11844
+ async estimateVaultCreationGas(_userKeyHash) {
11845
+ return 10000n;
11846
+ }
11847
+ getFactoryAddress() {
11848
+ return void 0;
11849
+ }
11850
+ getImplementationAddress() {
11851
+ return void 0;
11852
+ }
11853
+ // ========================================================================
11854
+ // Stacks-Specific: Balance Queries
11855
+ // ========================================================================
11856
+ /**
11857
+ * Get native STX balance for an address.
11858
+ */
11859
+ async getNativeBalance(address) {
11860
+ try {
11861
+ const response = await fetch(
11862
+ `${this.rpcUrl}/v2/accounts/${address}?proof=0`
11863
+ );
11864
+ if (!response.ok) {
11865
+ return 0n;
11866
+ }
11867
+ const data = await response.json();
11868
+ return BigInt(data.balance || "0");
11869
+ } catch {
11870
+ return 0n;
11871
+ }
11872
+ }
11873
+ /**
11874
+ * Get vault STX balance for an identity.
11875
+ * Queries the vault contract's `get-stx-balance` read-only function.
11876
+ */
11877
+ async getVaultStxBalance(keyHash) {
11878
+ if (!this.vaultContract) {
11879
+ return 0n;
11880
+ }
11881
+ try {
11882
+ const result = await this.callReadOnly(
11883
+ this.vaultContract.address,
11884
+ this.vaultContract.name,
11885
+ "get-stx-balance",
11886
+ [`0x${keyHash.replace("0x", "")}`]
11887
+ );
11888
+ if (result && result.value !== void 0) {
11889
+ return BigInt(result.value);
11890
+ }
11891
+ return 0n;
11892
+ } catch {
11893
+ return 0n;
11894
+ }
11895
+ }
11896
+ /**
11897
+ * Get vault sBTC balance for an identity.
11898
+ */
11899
+ async getVaultSbtcBalance(keyHash) {
11900
+ if (!this.vaultContract) {
11901
+ return 0n;
11902
+ }
11903
+ try {
11904
+ const result = await this.callReadOnly(
11905
+ this.vaultContract.address,
11906
+ this.vaultContract.name,
11907
+ "get-sbtc-balance",
11908
+ [`0x${keyHash.replace("0x", "")}`]
11909
+ );
11910
+ if (result && result.value !== void 0) {
11911
+ return BigInt(result.value);
11912
+ }
11913
+ return 0n;
11914
+ } catch {
11915
+ return 0n;
11916
+ }
11917
+ }
11918
+ // ========================================================================
11919
+ // Stacks-Specific: Identity & Session Queries
11920
+ // ========================================================================
11921
+ /**
11922
+ * Get identity info from the spoke contract.
11923
+ */
11924
+ async getIdentity(keyHash) {
11925
+ if (!this.spokeContract) {
11926
+ return null;
11927
+ }
11928
+ try {
11929
+ const result = await this.callReadOnly(
11930
+ this.spokeContract.address,
11931
+ this.spokeContract.name,
11932
+ "get-identity",
11933
+ [`0x${keyHash.replace("0x", "")}`]
11934
+ );
11935
+ if (!result || result.value === void 0) {
11936
+ return null;
11937
+ }
11938
+ const val = result.value;
11939
+ return {
11940
+ compressedPubkey: val["compressed-pubkey"]?.value || "",
11941
+ owner: val.owner?.value || "",
11942
+ nonce: BigInt(val.nonce?.value || 0),
11943
+ createdAt: BigInt(val["created-at"]?.value || 0)
11944
+ };
11945
+ } catch {
11946
+ return null;
11947
+ }
11948
+ }
11949
+ /**
11950
+ * Get session info from the spoke contract.
11951
+ */
11952
+ async getSession(keyHash, sessionHash) {
11953
+ if (!this.spokeContract) {
11954
+ return null;
11955
+ }
11956
+ try {
11957
+ const cleanKeyHash = `0x${keyHash.replace("0x", "")}`;
11958
+ const cleanSessionHash = `0x${sessionHash.replace("0x", "")}`;
11959
+ const result = await this.callReadOnly(
11960
+ this.spokeContract.address,
11961
+ this.spokeContract.name,
11962
+ "get-session",
11963
+ [cleanKeyHash, cleanSessionHash]
11964
+ );
11965
+ if (!result || result.value === void 0) {
11966
+ return null;
11967
+ }
11968
+ const val = result.value;
11969
+ return {
11970
+ sessionPubkey: val["session-pubkey"]?.value || "",
11971
+ expiry: BigInt(val.expiry?.value || 0),
11972
+ maxValue: BigInt(val["max-value"]?.value || 0),
11973
+ spent: BigInt(val.spent?.value || 0),
11974
+ revoked: val.revoked?.value === true,
11975
+ createdAt: BigInt(val["created-at"]?.value || 0)
11976
+ };
11977
+ } catch {
11978
+ return null;
11979
+ }
11980
+ }
11981
+ /**
11982
+ * Check if a session is currently active.
11983
+ */
11984
+ async checkSessionActive(keyHash, sessionHash) {
11985
+ if (!this.spokeContract) {
11986
+ return false;
11987
+ }
11988
+ try {
11989
+ const cleanKeyHash = `0x${keyHash.replace("0x", "")}`;
11990
+ const cleanSessionHash = `0x${sessionHash.replace("0x", "")}`;
11991
+ const result = await this.callReadOnly(
11992
+ this.spokeContract.address,
11993
+ this.spokeContract.name,
11994
+ "is-session-active",
11995
+ [cleanKeyHash, cleanSessionHash]
11996
+ );
11997
+ return result?.value === true;
11998
+ } catch {
11999
+ return false;
12000
+ }
12001
+ }
12002
+ /**
12003
+ * Get remaining spending budget for a session.
12004
+ */
12005
+ async getRemainingBudget(keyHash, sessionHash) {
12006
+ if (!this.spokeContract) {
12007
+ return 0n;
12008
+ }
12009
+ try {
12010
+ const cleanKeyHash = `0x${keyHash.replace("0x", "")}`;
12011
+ const cleanSessionHash = `0x${sessionHash.replace("0x", "")}`;
12012
+ const result = await this.callReadOnly(
12013
+ this.spokeContract.address,
12014
+ this.spokeContract.name,
12015
+ "get-remaining-budget",
12016
+ [cleanKeyHash, cleanSessionHash]
12017
+ );
12018
+ if (result && result.value !== void 0) {
12019
+ return BigInt(result.value);
12020
+ }
12021
+ return 0n;
12022
+ } catch {
12023
+ return 0n;
12024
+ }
12025
+ }
12026
+ // ========================================================================
12027
+ // Stacks-Specific: Protocol Status
12028
+ // ========================================================================
12029
+ /**
12030
+ * Check if the spoke contract is paused.
12031
+ */
12032
+ async isProtocolPaused() {
12033
+ if (!this.spokeContract) {
12034
+ return false;
12035
+ }
12036
+ try {
12037
+ const result = await this.callReadOnly(
12038
+ this.spokeContract.address,
12039
+ this.spokeContract.name,
12040
+ "is-paused",
12041
+ []
12042
+ );
12043
+ return result === true || result?.value === true;
12044
+ } catch {
12045
+ return false;
12046
+ }
12047
+ }
12048
+ /**
12049
+ * Get global identity count.
12050
+ */
12051
+ async getIdentityCount() {
12052
+ if (!this.spokeContract) {
12053
+ return 0n;
12054
+ }
12055
+ try {
12056
+ const result = await this.callReadOnly(
12057
+ this.spokeContract.address,
12058
+ this.spokeContract.name,
12059
+ "get-identity-count",
12060
+ []
12061
+ );
12062
+ return BigInt(result?.value || result || 0);
12063
+ } catch {
12064
+ return 0n;
12065
+ }
12066
+ }
12067
+ /**
12068
+ * Get total STX deposited across all vaults.
12069
+ */
12070
+ async getTotalStxDeposited() {
12071
+ if (!this.vaultContract) {
12072
+ return 0n;
12073
+ }
12074
+ try {
12075
+ const result = await this.callReadOnly(
12076
+ this.vaultContract.address,
12077
+ this.vaultContract.name,
12078
+ "get-total-stx-deposited",
12079
+ []
12080
+ );
12081
+ return BigInt(result?.value || result || 0);
12082
+ } catch {
12083
+ return 0n;
12084
+ }
12085
+ }
12086
+ // ========================================================================
12087
+ // Session Management (Issue #13)
12088
+ // ========================================================================
12089
+ /**
12090
+ * Register a session key on the Stacks spoke.
12091
+ * On Stacks, sessions are managed directly on the spoke contract
12092
+ * (unlike EVM spokes where sessions are on the Hub).
12093
+ */
12094
+ async registerSession(_params) {
12095
+ throw new Error(
12096
+ "Session registration on Stacks requires a Passkey signature. Build a register-session transaction with the Passkey signature, then submit via the relayer for sponsored execution."
12097
+ );
12098
+ }
12099
+ /**
12100
+ * Revoke a session key on the Stacks spoke.
12101
+ */
12102
+ async revokeSession(_params) {
12103
+ throw new Error(
12104
+ "Session revocation on Stacks requires a Passkey signature. Build a revoke-session transaction with the Passkey signature, then submit via the relayer for sponsored execution."
12105
+ );
12106
+ }
12107
+ /**
12108
+ * Check if a session is active.
12109
+ */
12110
+ async isSessionActive(userKeyHash, sessionKeyHash) {
12111
+ const active = await this.checkSessionActive(userKeyHash, sessionKeyHash);
12112
+ const session = await this.getSession(userKeyHash, sessionKeyHash);
12113
+ return {
12114
+ isActive: active,
12115
+ expiry: session ? Number(session.expiry) : 0,
12116
+ maxValue: session?.maxValue ?? 0n,
12117
+ chainScopes: [this.config.wormholeChainId]
12118
+ };
12119
+ }
12120
+ /**
12121
+ * Get all sessions for a user.
12122
+ * Note: Clarity maps don't support enumeration, so this requires
12123
+ * off-chain indexing or event log parsing.
12124
+ */
12125
+ async getUserSessions(_userKeyHash) {
12126
+ throw new Error(
12127
+ 'Enumerating all sessions is not supported on Stacks (Clarity maps are not iterable). Use checkSessionActive() with a known session hash, or query the Stacks event log for "session-registered" print events via the Hiro API.'
12128
+ );
12129
+ }
12130
+ // ========================================================================
12131
+ // Stacks-Specific: Transaction Status
12132
+ // ========================================================================
12133
+ /**
12134
+ * Get the status of a Stacks transaction.
12135
+ */
12136
+ async getTransactionStatus(txId) {
12137
+ try {
12138
+ const cleanTxId = txId.startsWith("0x") ? txId : `0x${txId}`;
12139
+ const response = await fetch(
12140
+ `${this.rpcUrl}/extended/v1/tx/${cleanTxId}`
12141
+ );
12142
+ if (!response.ok) {
12143
+ return { status: "not_found" };
12144
+ }
12145
+ const data = await response.json();
12146
+ const txStatus = data.tx_status;
12147
+ if (txStatus === "success") {
12148
+ return {
12149
+ status: "success",
12150
+ blockHeight: data.block_height
12151
+ };
12152
+ }
12153
+ if (txStatus === "pending") {
12154
+ return { status: "pending" };
12155
+ }
12156
+ if (txStatus === "abort_by_response" || txStatus === "abort_by_post_condition") {
12157
+ return {
12158
+ status: "failed",
12159
+ error: `Transaction aborted: ${txStatus}`
12160
+ };
12161
+ }
12162
+ return { status: "pending" };
12163
+ } catch {
12164
+ return { status: "not_found" };
12165
+ }
12166
+ }
12167
+ /**
12168
+ * Wait for a transaction to be confirmed.
12169
+ *
12170
+ * @param txId - Transaction ID
12171
+ * @param maxAttempts - Maximum polling attempts (default: 60)
12172
+ * @param pollIntervalMs - Polling interval in milliseconds (default: 5000)
12173
+ */
12174
+ async waitForConfirmation(txId, maxAttempts = 60, pollIntervalMs = 5e3) {
12175
+ for (let i = 0; i < maxAttempts; i++) {
12176
+ const status = await this.getTransactionStatus(txId);
12177
+ if (status.status === "success") {
12178
+ return { confirmed: true, blockHeight: status.blockHeight };
12179
+ }
12180
+ if (status.status === "failed") {
12181
+ throw new Error(`Transaction failed: ${status.error}`);
12182
+ }
12183
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
12184
+ }
12185
+ return { confirmed: false };
12186
+ }
12187
+ // ========================================================================
12188
+ // Stacks-Specific: Network Info
12189
+ // ========================================================================
12190
+ /**
12191
+ * Get Stacks network info (block height, network version, etc.).
12192
+ */
12193
+ async getNetworkInfo() {
12194
+ const response = await fetch(`${this.rpcUrl}/v2/info`);
12195
+ if (!response.ok) {
12196
+ throw new Error(`Failed to get Stacks network info: ${response.statusText}`);
12197
+ }
12198
+ const data = await response.json();
12199
+ return {
12200
+ networkId: data.network_id,
12201
+ stacksBlockHeight: data.stacks_tip_height,
12202
+ burnBlockHeight: data.burn_block_height,
12203
+ serverVersion: data.server_version
12204
+ };
12205
+ }
12206
+ /**
12207
+ * Get the current Stacks block height.
12208
+ * Used for session expiry calculations.
12209
+ */
12210
+ async getCurrentBlockHeight() {
12211
+ const info = await this.getNetworkInfo();
12212
+ return info.stacksBlockHeight;
12213
+ }
12214
+ // ========================================================================
12215
+ // Internal: Read-Only Contract Calls via Hiro API
12216
+ // ========================================================================
12217
+ /**
12218
+ * Call a read-only Clarity function via the Hiro API.
12219
+ * Uses the /v2/contracts/call-read endpoint.
12220
+ */
12221
+ async callReadOnly(contractAddress, contractName, functionName, args) {
12222
+ const url = `${this.rpcUrl}/v2/contracts/call-read/${contractAddress}/${contractName}/${functionName}`;
12223
+ const response = await fetch(url, {
12224
+ method: "POST",
12225
+ headers: { "Content-Type": "application/json" },
12226
+ body: JSON.stringify({
12227
+ sender: contractAddress,
12228
+ arguments: args
12229
+ })
12230
+ });
12231
+ if (!response.ok) {
12232
+ const errorText = await response.text().catch(() => "Unknown error");
12233
+ throw new Error(
12234
+ `Read-only call failed: ${contractAddress}.${contractName}::${functionName} - ${response.status}: ${errorText}`
12235
+ );
12236
+ }
12237
+ const data = await response.json();
12238
+ if (!data.okay) {
12239
+ throw new Error(
12240
+ `Read-only call returned error: ${contractAddress}.${contractName}::${functionName} - ${data.cause || "Unknown cause"}`
12241
+ );
12242
+ }
12243
+ return this.parseClarityValue(data.result);
12244
+ }
12245
+ /**
12246
+ * Parse a hex-encoded Clarity value from the API response.
12247
+ * This is a simplified parser for common Clarity types.
12248
+ */
12249
+ parseClarityValue(hex) {
12250
+ if (!hex || hex === "0x") {
12251
+ return null;
12252
+ }
12253
+ const bytes = hexToBytes(hex);
12254
+ if (bytes.length === 0) {
12255
+ return null;
12256
+ }
12257
+ const typeId = bytes[0];
12258
+ switch (typeId) {
12259
+ // int (0x00)
12260
+ case 0: {
12261
+ let value = 0n;
12262
+ for (let i = 1; i < 17 && i < bytes.length; i++) {
12263
+ value = value << 8n | BigInt(bytes[i]);
12264
+ }
12265
+ return { value };
12266
+ }
12267
+ // uint (0x01)
12268
+ case 1: {
12269
+ let value = 0n;
12270
+ for (let i = 1; i < 17 && i < bytes.length; i++) {
12271
+ value = value << 8n | BigInt(bytes[i]);
12272
+ }
12273
+ return { value };
12274
+ }
12275
+ // buffer (0x02)
12276
+ case 2: {
12277
+ const len = bytes[1] << 24 | bytes[2] << 16 | bytes[3] << 8 | bytes[4];
12278
+ const bufValue = bytes.slice(5, 5 + len);
12279
+ return { value: "0x" + bytesToHex(bufValue) };
12280
+ }
12281
+ // bool true (0x03)
12282
+ case 3:
12283
+ return true;
12284
+ // bool false (0x04)
12285
+ case 4:
12286
+ return false;
12287
+ // optional none (0x09)
12288
+ case 9:
12289
+ return null;
12290
+ // optional some (0x0a)
12291
+ case 10:
12292
+ return this.parseClarityValue("0x" + bytesToHex(bytes.slice(1)));
12293
+ // response ok (0x07)
12294
+ case 7:
12295
+ return this.parseClarityValue("0x" + bytesToHex(bytes.slice(1)));
12296
+ // response err (0x08)
12297
+ case 8: {
12298
+ const errVal = this.parseClarityValue("0x" + bytesToHex(bytes.slice(1)));
12299
+ throw new Error(`Clarity error: ${JSON.stringify(errVal)}`);
12300
+ }
12301
+ // tuple (0x0c)
12302
+ case 12: {
12303
+ return { value: hex };
12304
+ }
12305
+ default:
12306
+ return { value: hex };
12307
+ }
12308
+ }
12309
+ };
12310
+
12311
+ // src/presets.ts
12312
+ var CHAIN_NAMES = {
12313
+ // EVM L2s (Hub-capable)
12314
+ BASE: "base",
12315
+ OPTIMISM: "optimism",
12316
+ ARBITRUM: "arbitrum",
12317
+ SCROLL: "scroll",
12318
+ BLAST: "blast",
12319
+ MANTLE: "mantle",
12320
+ // EVM L1s
12321
+ ETHEREUM: "ethereum",
12322
+ POLYGON: "polygon",
12323
+ BSC: "bsc",
12324
+ AVALANCHE: "avalanche",
12325
+ FANTOM: "fantom",
12326
+ CELO: "celo",
12327
+ MOONBEAM: "moonbeam",
12328
+ // EVM L1s (High Performance)
12329
+ MONAD: "monad",
12330
+ // Non-EVM
12331
+ SOLANA: "solana",
12332
+ APTOS: "aptos",
12333
+ SUI: "sui",
12334
+ STARKNET: "starknet",
12335
+ STACKS: "stacks",
12336
+ NEAR: "near",
12337
+ SEI: "sei"
12338
+ };
12339
+ var CHAIN_PRESETS = {
12340
+ // ────────────────────────────────────────────────────────────────────────
12341
+ // BASE - Primary Hub Chain
12342
+ // ────────────────────────────────────────────────────────────────────────
12343
+ base: {
12344
+ displayName: "Base",
12345
+ type: "evm",
12346
+ canBeHub: true,
12347
+ testnet: {
12348
+ name: "Base Sepolia",
12349
+ chainId: 84532,
12350
+ wormholeChainId: 10004,
12351
+ rpcUrl: "https://sepolia.base.org",
12352
+ explorerUrl: "https://sepolia.basescan.org",
12353
+ isEvm: true,
12354
+ contracts: {
12355
+ hub: "0x66D87dE68327f48A099c5B9bE97020Feab9a7c82",
12356
+ vaultFactory: "0xCFaEb5652aa2Ee60b2229dC8895B4159749C7e53",
12357
+ vaultImplementation: "0x0d13367C16c6f0B24eD275CC67C7D9f42878285c",
12358
+ wormholeCoreBridge: "0x79A1027a6A159502049F10906D333EC57E95F083",
12359
+ tokenBridge: "0x86F55A04690fd7815A3D802bD587e83eA888B239"
12360
+ }
12361
+ },
12362
+ mainnet: {
12363
+ name: "Base",
12364
+ chainId: 8453,
12365
+ wormholeChainId: 30,
12366
+ rpcUrl: "https://mainnet.base.org",
12367
+ explorerUrl: "https://basescan.org",
12368
+ isEvm: true,
12369
+ contracts: {
12370
+ // TODO: Deploy mainnet contracts
12371
+ wormholeCoreBridge: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6",
12372
+ tokenBridge: "0x8d2de8d2f73F1F4cAB472AC9A881C9b123C79627"
12373
+ }
12374
+ }
12375
+ },
12376
+ // ────────────────────────────────────────────────────────────────────────
12377
+ // OPTIMISM - Secondary Hub / Spoke
12378
+ // ────────────────────────────────────────────────────────────────────────
12379
+ optimism: {
12380
+ displayName: "Optimism",
12381
+ type: "evm",
12382
+ canBeHub: true,
12383
+ testnet: {
12384
+ name: "Optimism Sepolia",
12385
+ chainId: 11155420,
12386
+ wormholeChainId: 10005,
12387
+ rpcUrl: "https://sepolia.optimism.io",
12388
+ explorerUrl: "https://sepolia-optimism.etherscan.io",
12389
+ isEvm: true,
12390
+ contracts: {
12391
+ vaultFactory: "0xA5653d54079ABeCe780F8d9597B2bc4B09fe464A",
12392
+ vaultImplementation: "0x8099b1406485d2255ff89Ce5Ea18520802AFC150",
12393
+ wormholeCoreBridge: "0x31377888146f3253211EFEf5c676D41ECe7D58Fe",
12394
+ tokenBridge: "0x99737Ec4B815d816c49A385943baf0380e75c0Ac"
12395
+ }
12396
+ },
12397
+ mainnet: {
12398
+ name: "Optimism",
12399
+ chainId: 10,
12400
+ wormholeChainId: 24,
12401
+ rpcUrl: "https://mainnet.optimism.io",
12402
+ explorerUrl: "https://optimistic.etherscan.io",
12403
+ isEvm: true,
12404
+ contracts: {
12405
+ wormholeCoreBridge: "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722",
12406
+ tokenBridge: "0x1D68124e65faFC907325e3EDbF8c4d84499DAa8b"
12407
+ }
12408
+ }
12409
+ },
12410
+ // ────────────────────────────────────────────────────────────────────────
12411
+ // ARBITRUM
12412
+ // ────────────────────────────────────────────────────────────────────────
12413
+ arbitrum: {
12414
+ displayName: "Arbitrum",
12415
+ type: "evm",
12416
+ canBeHub: true,
12417
+ testnet: {
12418
+ name: "Arbitrum Sepolia",
12419
+ chainId: 421614,
12420
+ wormholeChainId: 10003,
12421
+ rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
12422
+ explorerUrl: "https://sepolia.arbiscan.io",
12423
+ isEvm: true,
12424
+ contracts: {
12425
+ vaultFactory: "0xd36D3D5DB59d78f1E33813490F72DABC15C9B07c",
12426
+ vaultImplementation: "0xB10ACf39eBF17fc33F722cBD955b7aeCB0611bc4",
12427
+ wormholeCoreBridge: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35",
12428
+ tokenBridge: "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"
12429
+ }
12430
+ },
12431
+ mainnet: {
12432
+ name: "Arbitrum",
12433
+ chainId: 42161,
12434
+ wormholeChainId: 23,
12435
+ rpcUrl: "https://arb1.arbitrum.io/rpc",
12436
+ explorerUrl: "https://arbiscan.io",
12437
+ isEvm: true,
12438
+ contracts: {
12439
+ wormholeCoreBridge: "0xa5f208e072434bC67592E4C49C1B991BA79BCA46",
12440
+ tokenBridge: "0x0b2402144Bb366A632D14B83F244D2e0e21bD39c"
12441
+ }
12442
+ }
12443
+ },
11371
12444
  // ────────────────────────────────────────────────────────────────────────
11372
12445
  // ETHEREUM
11373
12446
  // ────────────────────────────────────────────────────────────────────────
@@ -11691,6 +12764,45 @@ var CHAIN_PRESETS = {
11691
12764
  }
11692
12765
  },
11693
12766
  // ────────────────────────────────────────────────────────────────────────
12767
+ // MONAD - High-Performance L1 with EIP-7951 P256 + Agent Gateway
12768
+ // ────────────────────────────────────────────────────────────────────────
12769
+ monad: {
12770
+ displayName: "Monad",
12771
+ type: "evm",
12772
+ canBeHub: true,
12773
+ // Has native P-256 precompile (EIP-7951)
12774
+ testnet: {
12775
+ name: "Monad Testnet",
12776
+ chainId: 10143,
12777
+ wormholeChainId: 10048,
12778
+ rpcUrl: "https://testnet-rpc.monad.xyz",
12779
+ explorerUrl: "https://testnet.monadvision.com",
12780
+ isEvm: true,
12781
+ contracts: {
12782
+ vaultFactory: "0x07F608AFf6d63b68029488b726d895c4Bb593038",
12783
+ vaultImplementation: "0xD66153fccFB6731fB6c4944FbD607ba86A76a1f6",
12784
+ wormholeCoreBridge: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd",
12785
+ // Agent Gateway contracts
12786
+ serviceDirectory: "0x0D2B4193e78107678a5aC29d795e0EcD361aE3A7"
12787
+ },
12788
+ hubChainId: 10004
12789
+ // Base Sepolia
12790
+ },
12791
+ mainnet: {
12792
+ name: "Monad",
12793
+ chainId: 143,
12794
+ wormholeChainId: 0,
12795
+ // TBD
12796
+ rpcUrl: "https://rpc.monad.xyz",
12797
+ explorerUrl: "https://monadscan.com",
12798
+ isEvm: true,
12799
+ contracts: {
12800
+ wormholeCoreBridge: "0x194B123c5E96B9b2E49763619985790Dc241CAC0",
12801
+ tokenBridge: "0x0B2719cdA2F10595369e6673ceA3Ee2EDFa13BA7"
12802
+ }
12803
+ }
12804
+ },
12805
+ // ────────────────────────────────────────────────────────────────────────
11694
12806
  // SEI
11695
12807
  // ────────────────────────────────────────────────────────────────────────
11696
12808
  sei: {
@@ -11855,6 +12967,49 @@ var CHAIN_PRESETS = {
11855
12967
  }
11856
12968
  },
11857
12969
  // ────────────────────────────────────────────────────────────────────────
12970
+ // STACKS
12971
+ // ────────────────────────────────────────────────────────────────────────
12972
+ stacks: {
12973
+ displayName: "Stacks",
12974
+ type: "stacks",
12975
+ canBeHub: false,
12976
+ testnet: {
12977
+ name: "Stacks Testnet",
12978
+ chainId: 2147483648,
12979
+ // CAIP-2: stacks:2147483648
12980
+ wormholeChainId: 60,
12981
+ // Official Wormhole chain ID for Stacks
12982
+ rpcUrl: "https://api.testnet.hiro.so",
12983
+ explorerUrl: "https://explorer.hiro.so/?chain=testnet",
12984
+ isEvm: false,
12985
+ contracts: {
12986
+ // Spoke contract: identity + session management
12987
+ hub: "ST1CKNN84MDPCJRQDHDCY34GMRKQ3TASNNDJQDRTN.veridex-spoke",
12988
+ // Vault contract: STX/sBTC custody
12989
+ vaultFactory: "ST1CKNN84MDPCJRQDHDCY34GMRKQ3TASNNDJQDRTN.veridex-vault",
12990
+ wormholeCoreBridge: "",
12991
+ // Phase 2: Wormhole integration contracts
12992
+ wormholeVerifier: "ST1CKNN84MDPCJRQDHDCY34GMRKQ3TASNNDJQDRTN.veridex-wormhole-verifier",
12993
+ vaultVaa: "ST1CKNN84MDPCJRQDHDCY34GMRKQ3TASNNDJQDRTN.veridex-vault-vaa"
12994
+ },
12995
+ hubChainId: 10004
12996
+ // Base Sepolia
12997
+ },
12998
+ mainnet: {
12999
+ name: "Stacks",
13000
+ chainId: 1,
13001
+ // CAIP-2: stacks:1
13002
+ wormholeChainId: 60,
13003
+ rpcUrl: "https://api.hiro.so",
13004
+ explorerUrl: "https://explorer.hiro.so",
13005
+ isEvm: false,
13006
+ contracts: {
13007
+ // TODO: Deploy mainnet contracts
13008
+ wormholeCoreBridge: ""
13009
+ }
13010
+ }
13011
+ },
13012
+ // ────────────────────────────────────────────────────────────────────────
11858
13013
  // NEAR
11859
13014
  // ────────────────────────────────────────────────────────────────────────
11860
13015
  near: {
@@ -11977,6 +13132,13 @@ function createChainClient(chain, network, customRpcUrl) {
11977
13132
  wormholeChainId: config.wormholeChainId,
11978
13133
  network: network === "testnet" ? "sepolia" : "mainnet"
11979
13134
  });
13135
+ case "stacks":
13136
+ return new StacksClient({
13137
+ rpcUrl,
13138
+ spokeContractAddress: config.contracts.hub || void 0,
13139
+ wormholeChainId: config.wormholeChainId,
13140
+ network
13141
+ });
11980
13142
  case "near":
11981
13143
  case "cosmos":
11982
13144
  throw new Error(`Chain type "${preset.type}" is not yet supported. Coming soon!`);
@@ -12032,8 +13194,8 @@ function createSessionSDK(chain = "base", config = {}) {
12032
13194
  }
12033
13195
 
12034
13196
  // src/core/CrossOriginAuth.ts
12035
- var VERIDEX_RP_ID = "veridex.network";
12036
13197
  var DEFAULT_AUTH_PORTAL_URL = "https://auth.veridex.network";
13198
+ var DEFAULT_RELAYER_URL = "https://amused-kameko-veridex-demo-37453117.koyeb.app/api/v1";
12037
13199
  var AUTH_MESSAGE_TYPES = {
12038
13200
  AUTH_REQUEST: "VERIDEX_AUTH_REQUEST",
12039
13201
  AUTH_RESPONSE: "VERIDEX_AUTH_RESPONSE",
@@ -12046,6 +13208,7 @@ var CrossOriginAuth = class {
12046
13208
  this.config = {
12047
13209
  rpId: config.rpId ?? VERIDEX_RP_ID,
12048
13210
  authPortalUrl: config.authPortalUrl ?? DEFAULT_AUTH_PORTAL_URL,
13211
+ relayerUrl: config.relayerUrl ?? DEFAULT_RELAYER_URL,
12049
13212
  mode: config.mode ?? "popup",
12050
13213
  popupFeatures: config.popupFeatures ?? "width=500,height=600,left=100,top=100",
12051
13214
  timeout: config.timeout ?? 12e4,
@@ -12233,6 +13396,84 @@ var CrossOriginAuth = class {
12233
13396
  getAuthPortalUrl() {
12234
13397
  return this.config.authPortalUrl;
12235
13398
  }
13399
+ // ========================================================================
13400
+ // Server-Side Session Tokens (ADR-0018)
13401
+ // ========================================================================
13402
+ /**
13403
+ * Create a server-validated session token via the relayer.
13404
+ * Call this after authenticating (via ROR or auth portal) to get a
13405
+ * server-side session that the relayer can verify on subsequent requests.
13406
+ */
13407
+ async createServerSession(session, options) {
13408
+ const keyHash = session.credential?.keyHash;
13409
+ if (!keyHash) {
13410
+ throw new Error("Session must include credential with keyHash");
13411
+ }
13412
+ const response = await fetch(`${this.config.relayerUrl}/session/create`, {
13413
+ method: "POST",
13414
+ headers: { "Content-Type": "application/json" },
13415
+ body: JSON.stringify({
13416
+ keyHash,
13417
+ appOrigin: typeof window !== "undefined" ? window.location.origin : "",
13418
+ sessionPublicKey: session.sessionPublicKey || "",
13419
+ permissions: options?.permissions ?? ["read", "transfer"],
13420
+ expiresInMs: options?.expiresInMs ?? 36e5,
13421
+ signature: session.signature
13422
+ })
13423
+ });
13424
+ if (!response.ok) {
13425
+ const data2 = await response.json().catch(() => ({ error: "Unknown error" }));
13426
+ throw new Error(data2.error || `Failed to create server session: ${response.status}`);
13427
+ }
13428
+ const data = await response.json();
13429
+ return data.session;
13430
+ }
13431
+ /**
13432
+ * Validate an existing server session token.
13433
+ * Returns the session details if valid, null if expired/revoked.
13434
+ */
13435
+ async validateServerSession(sessionId) {
13436
+ const response = await fetch(`${this.config.relayerUrl}/session/${encodeURIComponent(sessionId)}`);
13437
+ if (!response.ok) {
13438
+ return null;
13439
+ }
13440
+ const data = await response.json();
13441
+ if (!data.valid) {
13442
+ return null;
13443
+ }
13444
+ return data.session;
13445
+ }
13446
+ /**
13447
+ * Revoke a server session token.
13448
+ */
13449
+ async revokeServerSession(sessionId) {
13450
+ const response = await fetch(`${this.config.relayerUrl}/session/${encodeURIComponent(sessionId)}`, {
13451
+ method: "DELETE"
13452
+ });
13453
+ return response.ok;
13454
+ }
13455
+ /**
13456
+ * Full authentication flow: authenticate + create server session.
13457
+ * Automatically detects ROR support and falls back to auth portal.
13458
+ */
13459
+ async authenticateAndCreateSession(options) {
13460
+ let session;
13461
+ if (await this.supportsRelatedOrigins()) {
13462
+ const result = await this.authenticate();
13463
+ session = {
13464
+ address: "",
13465
+ sessionPublicKey: "",
13466
+ expiresAt: Date.now() + (options?.expiresInMs ?? 36e5),
13467
+ signature: result.signature,
13468
+ credential: result.credential
13469
+ };
13470
+ } else {
13471
+ session = await this.connectWithVeridex();
13472
+ }
13473
+ const serverSession = await this.createServerSession(session, options);
13474
+ session.serverSessionId = serverSession.id;
13475
+ return { session, serverSession };
13476
+ }
12236
13477
  };
12237
13478
  function createCrossOriginAuth(config) {
12238
13479
  return new CrossOriginAuth(config);
@@ -13286,6 +14527,72 @@ var EVMHubClientAdapter = class {
13286
14527
  }
13287
14528
  };
13288
14529
 
14530
+ // src/chains/stacks/StacksPostConditions.ts
14531
+ function buildStxWithdrawalPostConditions(contractPrincipal, amount) {
14532
+ return [
14533
+ {
14534
+ type: "stx",
14535
+ principal: contractPrincipal,
14536
+ comparison: "eq",
14537
+ amount
14538
+ }
14539
+ ];
14540
+ }
14541
+ function buildStxDepositPostConditions(senderPrincipal, amount) {
14542
+ return [
14543
+ {
14544
+ type: "stx",
14545
+ principal: senderPrincipal,
14546
+ comparison: "eq",
14547
+ amount
14548
+ }
14549
+ ];
14550
+ }
14551
+ function buildSbtcWithdrawalPostConditions(contractPrincipal, amount, sbtcContractAddress = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4", sbtcContractName = "sbtc-token") {
14552
+ return [
14553
+ {
14554
+ type: "ft",
14555
+ principal: contractPrincipal,
14556
+ comparison: "eq",
14557
+ amount,
14558
+ contractAddress: sbtcContractAddress,
14559
+ contractName: sbtcContractName,
14560
+ tokenName: "sbtc-token"
14561
+ }
14562
+ ];
14563
+ }
14564
+ function buildExecutePostConditions(actionType, contractPrincipal, amount) {
14565
+ switch (actionType) {
14566
+ case 1:
14567
+ return buildStxWithdrawalPostConditions(contractPrincipal, amount);
14568
+ case 2:
14569
+ return buildSbtcWithdrawalPostConditions(contractPrincipal, amount);
14570
+ default:
14571
+ return [];
14572
+ }
14573
+ }
14574
+ function validatePostConditions(postConditions, expectedAmount) {
14575
+ if (postConditions.length === 0) {
14576
+ return {
14577
+ valid: false,
14578
+ error: "No post-conditions attached. Asset transfers require post-conditions for safety."
14579
+ };
14580
+ }
14581
+ const hasMatchingAmount = postConditions.some((pc) => {
14582
+ if (pc.type === "stx" || pc.type === "ft") {
14583
+ return pc.amount === expectedAmount && pc.comparison === "eq";
14584
+ }
14585
+ return false;
14586
+ });
14587
+ if (!hasMatchingAmount) {
14588
+ return {
14589
+ valid: false,
14590
+ error: `No post-condition matches expected amount ${expectedAmount}. Ensure exact-match post-conditions are attached.`
14591
+ };
14592
+ }
14593
+ return { valid: true };
14594
+ }
14595
+
13289
14596
  // src/constants/errors.ts
13290
14597
  var ERROR_RANGES = {
13291
14598
  /** Core protocol errors (paused, unauthorized, limits, etc.) */
@@ -13528,6 +14835,101 @@ function getSuggestedAction(code) {
13528
14835
  return "An error occurred. Please try again or contact support.";
13529
14836
  }
13530
14837
  }
14838
+
14839
+ // src/erc8004/contracts.ts
14840
+ var ERC8004_MAINNET_IDENTITY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";
14841
+ var ERC8004_MAINNET_REPUTATION = "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63";
14842
+ var ERC8004_TESTNET_IDENTITY = "0x8004A818BFB912233c491871b3d84c89A494BD9e";
14843
+ var ERC8004_TESTNET_REPUTATION = "0x8004B663056A597Dffe9eCcC1965A193B7388713";
14844
+ function getERC8004Addresses(testnet) {
14845
+ return {
14846
+ identityRegistry: testnet ? ERC8004_TESTNET_IDENTITY : ERC8004_MAINNET_IDENTITY,
14847
+ reputationRegistry: testnet ? ERC8004_TESTNET_REPUTATION : ERC8004_MAINNET_REPUTATION
14848
+ };
14849
+ }
14850
+ function isERC8004Chain(chainName) {
14851
+ return ERC8004_CHAINS.mainnet.includes(chainName) || ERC8004_CHAINS.testnet.includes(chainName);
14852
+ }
14853
+ var ERC8004_CHAINS = {
14854
+ mainnet: [
14855
+ "ethereum",
14856
+ "base",
14857
+ "polygon",
14858
+ "arbitrum",
14859
+ "optimism",
14860
+ "linea",
14861
+ "megaeth",
14862
+ "monad"
14863
+ ],
14864
+ testnet: [
14865
+ "ethereum-sepolia",
14866
+ "base-sepolia",
14867
+ "polygon-amoy",
14868
+ "arbitrum-sepolia",
14869
+ "optimism-sepolia",
14870
+ "monad-testnet"
14871
+ ]
14872
+ };
14873
+ var IDENTITY_REGISTRY_READ_ABI = [
14874
+ "function ownerOf(uint256 tokenId) view returns (address)",
14875
+ "function balanceOf(address owner) view returns (uint256)",
14876
+ "function totalSupply() view returns (uint256)",
14877
+ "function agentURI(uint256 agentId) view returns (string)",
14878
+ "function agentWallet(uint256 agentId) view returns (address)",
14879
+ "function getMetadata(uint256 agentId, string key) view returns (string)"
14880
+ ];
14881
+ var REPUTATION_REGISTRY_READ_ABI = [
14882
+ "function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint256 count, int128 summaryValue, uint8 summaryValueDecimals)",
14883
+ "function readFeedback(uint256 agentId, address clientAddress, uint256 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
14884
+ "function getClients(uint256 agentId) view returns (address[])",
14885
+ "function getLastIndex(uint256 agentId, address clientAddress) view returns (uint256)"
14886
+ ];
14887
+ var IDENTITY_REGISTRY_ABI = [
14888
+ // Registration
14889
+ "function register(string agentURI) returns (uint256)",
14890
+ "function register(string agentURI, tuple(string key, string value)[] metadata) returns (uint256)",
14891
+ // Read — ERC-721 standard
14892
+ "function ownerOf(uint256 tokenId) view returns (address)",
14893
+ "function balanceOf(address owner) view returns (uint256)",
14894
+ "function totalSupply() view returns (uint256)",
14895
+ // Read — ERC-8004 specific
14896
+ "function agentURI(uint256 agentId) view returns (string)",
14897
+ "function agentWallet(uint256 agentId) view returns (address)",
14898
+ "function getMetadata(uint256 agentId, string key) view returns (string)",
14899
+ // Write — URI and wallet management
14900
+ "function setAgentURI(uint256 agentId, string newURI)",
14901
+ "function setAgentWallet(uint256 agentId, address wallet, uint256 deadline, bytes signature)",
14902
+ "function unsetAgentWallet(uint256 agentId)",
14903
+ "function setMetadata(uint256 agentId, string key, string value)",
14904
+ // Events
14905
+ "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)",
14906
+ "event AgentURIUpdated(uint256 indexed agentId, string newURI)",
14907
+ "event AgentWalletSet(uint256 indexed agentId, address wallet)",
14908
+ "event AgentWalletUnset(uint256 indexed agentId)",
14909
+ "event MetadataUpdated(uint256 indexed agentId, string key, string value)"
14910
+ ];
14911
+ var REPUTATION_REGISTRY_ABI = [
14912
+ // Write
14913
+ "function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2)",
14914
+ "function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpointURI, string feedbackURI, bytes32 feedbackHash)",
14915
+ "function revokeFeedback(uint256 agentId, uint256 feedbackIndex)",
14916
+ "function appendResponse(uint256 agentId, address clientAddress, uint256 feedbackIndex, string responseURI, bytes32 responseHash)",
14917
+ // Read
14918
+ "function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint256 count, int128 summaryValue, uint8 summaryValueDecimals)",
14919
+ "function readFeedback(uint256 agentId, address clientAddress, uint256 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
14920
+ "function getClients(uint256 agentId) view returns (address[])",
14921
+ "function getLastIndex(uint256 agentId, address clientAddress) view returns (uint256)",
14922
+ // Events
14923
+ "event FeedbackGiven(uint256 indexed agentId, address indexed client, int128 value, uint8 valueDecimals)",
14924
+ "event FeedbackRevoked(uint256 indexed agentId, address indexed client, uint256 feedbackIndex)",
14925
+ "event ResponseAppended(uint256 indexed agentId, address indexed client, uint256 feedbackIndex)"
14926
+ ];
14927
+ var VALIDATION_REGISTRY_ABI = [
14928
+ "function validationRequest(address validatorAddress, uint256 agentId, string requestURI, bytes32 requestHash) returns (bytes32)",
14929
+ "function getValidationStatus(bytes32 requestHash) view returns (address validatorAddress, uint256 agentId, uint8 response, bytes32 responseHash, string tag, uint256 lastUpdate)",
14930
+ "function getSummary(uint256 agentId, address[] validatorAddresses, string tag) view returns (uint256 count, uint256 averageResponse)",
14931
+ "function getAgentValidations(uint256 agentId) view returns (bytes32[])"
14932
+ ];
13531
14933
  // Annotate the CommonJS export names for ESM import in node:
13532
14934
  0 && (module.exports = {
13533
14935
  ACTION_BRIDGE,
@@ -13537,6 +14939,7 @@ function getSuggestedAction(code) {
13537
14939
  ACTION_TYPES,
13538
14940
  ARBITRUM_SEPOLIA_TOKENS,
13539
14941
  AUTH_MESSAGE_TYPES,
14942
+ AptosClient,
13540
14943
  BASE_SEPOLIA_TOKENS,
13541
14944
  BalanceManager,
13542
14945
  CHAIN_DISPLAY_INFO,
@@ -13549,35 +14952,53 @@ function getSuggestedAction(code) {
13549
14952
  CrossOriginAuth,
13550
14953
  DEFAULT_AUTH_PORTAL_URL,
13551
14954
  DEFAULT_REFRESH_BUFFER,
14955
+ DEFAULT_RELAYER_URL,
13552
14956
  DEFAULT_SESSION_DURATION,
14957
+ ERC8004_CHAINS,
14958
+ ERC8004_MAINNET_IDENTITY,
14959
+ ERC8004_MAINNET_REPUTATION,
14960
+ ERC8004_TESTNET_IDENTITY,
14961
+ ERC8004_TESTNET_REPUTATION,
13553
14962
  ERROR_MESSAGES,
13554
14963
  ERROR_RANGES,
13555
14964
  ETHEREUM_SEPOLIA_TOKENS,
14965
+ EVMClient,
13556
14966
  EVMHubClientAdapter,
13557
14967
  EVM_ZERO_ADDRESS,
13558
14968
  GUARDIAN_CONFIG,
13559
14969
  GasSponsor,
13560
14970
  HUB_ABI,
14971
+ IDENTITY_REGISTRY_ABI,
14972
+ IDENTITY_REGISTRY_READ_ABI,
13561
14973
  IndexedDBSessionStorage,
13562
14974
  LIMIT_PRESETS,
13563
14975
  LocalStorageSessionStorage,
13564
14976
  MAINNET_CHAINS,
13565
14977
  MAX_SESSION_DURATION,
13566
14978
  MIN_SESSION_DURATION,
14979
+ MONAD_TESTNET_TOKENS,
13567
14980
  NATIVE_TOKEN_ADDRESS,
13568
14981
  OPTIMISM_SEPOLIA_TOKENS,
13569
14982
  PROTOCOL_VERSION,
13570
14983
  PasskeyManager,
13571
14984
  QueryHubStateError,
13572
14985
  QueryPortfolioError,
14986
+ REPUTATION_REGISTRY_ABI,
14987
+ REPUTATION_REGISTRY_READ_ABI,
13573
14988
  RelayerClient,
14989
+ STACKS_ACTION_TYPES,
13574
14990
  SessionError,
13575
14991
  SessionManager,
14992
+ SolanaClient,
13576
14993
  SpendingLimitsManager,
14994
+ StacksClient,
14995
+ StarknetClient,
14996
+ SuiClient,
13577
14997
  TESTNET_CHAINS,
13578
14998
  TOKEN_REGISTRY,
13579
14999
  TransactionParser,
13580
15000
  TransactionTracker,
15001
+ VALIDATION_REGISTRY_ABI,
13581
15002
  VAULT_ABI,
13582
15003
  VAULT_FACTORY_ABI,
13583
15004
  VERIDEX_ERRORS,
@@ -13595,6 +15016,10 @@ function getSuggestedAction(code) {
13595
15016
  base64URLEncode,
13596
15017
  buildChallenge,
13597
15018
  buildGaslessChallenge,
15019
+ buildSbtcWithdrawalPostConditions,
15020
+ buildStacksExecutePostConditions,
15021
+ buildStxDepositPostConditions,
15022
+ buildStxWithdrawalPostConditions,
13598
15023
  calculatePercentage,
13599
15024
  computeKeyHash,
13600
15025
  computeSessionKeyHash,
@@ -13654,11 +15079,16 @@ function getSuggestedAction(code) {
13654
15079
  getChainPreset,
13655
15080
  getConfigTypeName,
13656
15081
  getDefaultHub,
15082
+ getERC8004Addresses,
13657
15083
  getErrorCategory,
13658
15084
  getErrorMessage,
13659
15085
  getExplorerUrl,
13660
15086
  getHubChains,
13661
15087
  getSequenceFromTxReceipt,
15088
+ getStacksContractPrincipal,
15089
+ getStacksExplorerAddressUrl,
15090
+ getStacksExplorerTxUrl,
15091
+ getStacksNetworkFromAddress,
13662
15092
  getSuggestedAction,
13663
15093
  getSupportedChainIds,
13664
15094
  getSupportedChains,
@@ -13674,20 +15104,26 @@ function getSuggestedAction(code) {
13674
15104
  isAbiError,
13675
15105
  isChainSupported,
13676
15106
  isCoreError,
15107
+ isERC8004Chain,
13677
15108
  isEvmChain,
13678
15109
  isNativeToken,
13679
15110
  isQueryError,
13680
15111
  isQueryExecutionError,
13681
15112
  isQueryParsingError,
13682
15113
  isRetryableError,
15114
+ isStacksContractPrincipal,
13683
15115
  isValidBytes32,
13684
15116
  isValidEvmAddress,
15117
+ isValidStacksContractName,
15118
+ isValidStacksPrincipal,
15119
+ isValidStacksStandardPrincipal,
13685
15120
  isValidWormholeChainId,
13686
15121
  logTransactionSummary,
13687
15122
  normalizeEmitterAddress,
13688
15123
  padTo32Bytes,
13689
15124
  parseAmount,
13690
15125
  parseDERSignature,
15126
+ parseStacksContractPrincipal,
13691
15127
  parseVAA,
13692
15128
  parseVAABytes,
13693
15129
  parseVeridexError,
@@ -13699,10 +15135,22 @@ function getSuggestedAction(code) {
13699
15135
  sendAuthResponse,
13700
15136
  signWithSessionKey,
13701
15137
  solanaAddressToBytes32,
15138
+ stacksBuildExecuteHash,
15139
+ stacksBuildRegistrationHash,
15140
+ stacksBuildRevocationHash,
15141
+ stacksBuildSessionRegistrationHash,
15142
+ stacksBuildWithdrawalHash,
15143
+ stacksCompressPublicKey,
15144
+ stacksComputeKeyHash,
15145
+ stacksComputeKeyHashFromCoords,
15146
+ stacksDerToCompactSignature,
15147
+ stacksRsToCompactSignature,
15148
+ supportsRelatedOrigins,
13702
15149
  supportsRelayer,
13703
15150
  trimTo20Bytes,
13704
15151
  validateEmitter,
13705
15152
  validateSessionConfig,
15153
+ validateStacksPostConditions,
13706
15154
  verifySessionSignature,
13707
15155
  waitForGuardianSignatures
13708
15156
  });