@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.mjs CHANGED
@@ -546,17 +546,36 @@ function sleep(ms) {
546
546
  }
547
547
 
548
548
  // src/core/PasskeyManager.ts
549
- function detectRpId() {
549
+ var VERIDEX_RP_ID = "veridex.network";
550
+ function detectRpId(forceLocal) {
550
551
  if (typeof window === "undefined") return "localhost";
551
552
  const hostname = window.location.hostname;
552
553
  if (hostname === "localhost" || hostname === "127.0.0.1" || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
553
554
  return hostname;
554
555
  }
555
- const parts = hostname.split(".");
556
- if (parts.length <= 2) {
557
- return hostname;
556
+ if (forceLocal) {
557
+ const parts = hostname.split(".");
558
+ if (parts.length <= 2) {
559
+ return hostname;
560
+ }
561
+ return parts.slice(-2).join(".");
562
+ }
563
+ return VERIDEX_RP_ID;
564
+ }
565
+ async function supportsRelatedOrigins() {
566
+ if (typeof window === "undefined" || !window.PublicKeyCredential) {
567
+ return false;
568
+ }
569
+ if ("getClientCapabilities" in PublicKeyCredential) {
570
+ try {
571
+ const getCapabilities = PublicKeyCredential.getClientCapabilities;
572
+ const capabilities = await getCapabilities();
573
+ return capabilities?.relatedOrigins === true;
574
+ } catch {
575
+ return false;
576
+ }
558
577
  }
559
- return parts.slice(-2).join(".");
578
+ return false;
560
579
  }
561
580
  var PasskeyManager = class _PasskeyManager {
562
581
  config;
@@ -1667,11 +1686,46 @@ var ETHEREUM_SEPOLIA_TOKENS = {
1667
1686
  }
1668
1687
  ]
1669
1688
  };
1689
+ var MONAD_TESTNET_TOKENS = {
1690
+ wormholeChainId: 10048,
1691
+ chainName: "Monad Testnet",
1692
+ nativeToken: {
1693
+ symbol: "MON",
1694
+ name: "Monad",
1695
+ decimals: 18,
1696
+ address: NATIVE_TOKEN_ADDRESS,
1697
+ isNative: true
1698
+ },
1699
+ tokens: [
1700
+ {
1701
+ symbol: "USDC",
1702
+ name: "USD Coin",
1703
+ decimals: 6,
1704
+ address: "0x754704Bc059F8C67012fEd69BC8A327a5aafb603",
1705
+ isNative: false
1706
+ },
1707
+ {
1708
+ symbol: "AUSD",
1709
+ name: "Agora Dollar",
1710
+ decimals: 18,
1711
+ address: "0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a",
1712
+ isNative: false
1713
+ },
1714
+ {
1715
+ symbol: "WMON",
1716
+ name: "Wrapped MON",
1717
+ decimals: 18,
1718
+ address: "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A",
1719
+ isNative: false
1720
+ }
1721
+ ]
1722
+ };
1670
1723
  var TOKEN_REGISTRY = {
1671
1724
  10004: BASE_SEPOLIA_TOKENS,
1672
1725
  10005: OPTIMISM_SEPOLIA_TOKENS,
1673
1726
  10003: ARBITRUM_SEPOLIA_TOKENS,
1674
- 10002: ETHEREUM_SEPOLIA_TOKENS
1727
+ 10002: ETHEREUM_SEPOLIA_TOKENS,
1728
+ 10048: MONAD_TESTNET_TOKENS
1675
1729
  };
1676
1730
  function getTokenList(wormholeChainId) {
1677
1731
  return TOKEN_REGISTRY[wormholeChainId] ?? null;
@@ -1702,9 +1756,10 @@ function getChainName(wormholeChainId) {
1702
1756
 
1703
1757
  // src/core/BalanceManager.ts
1704
1758
  var DEFAULT_RPC_URLS = {
1759
+ 10002: "https://ethereum-sepolia-rpc.publicnode.com",
1760
+ 10003: "https://sepolia-rollup.arbitrum.io/rpc",
1705
1761
  10004: "https://sepolia.base.org",
1706
- 10005: "https://sepolia.optimism.io",
1707
- 10003: "https://sepolia-rollup.arbitrum.io/rpc"
1762
+ 10005: "https://sepolia.optimism.io"
1708
1763
  };
1709
1764
  var TESTNET_TOKEN_PRICES = {
1710
1765
  ETH: 2500,
@@ -1939,9 +1994,10 @@ var DEFAULT_POLLING_INTERVAL = 2e3;
1939
1994
  var DEFAULT_REQUIRED_CONFIRMATIONS = 1;
1940
1995
  var DEFAULT_TIMEOUT = 3e5;
1941
1996
  var DEFAULT_RPC_URLS2 = {
1997
+ 10002: "https://ethereum-sepolia-rpc.publicnode.com",
1998
+ 10003: "https://sepolia-rollup.arbitrum.io/rpc",
1942
1999
  10004: "https://sepolia.base.org",
1943
- 10005: "https://sepolia.optimism.io",
1944
- 10003: "https://sepolia-rollup.arbitrum.io/rpc"
2000
+ 10005: "https://sepolia.optimism.io"
1945
2001
  };
1946
2002
  var TransactionTracker = class {
1947
2003
  config;
@@ -11054,136 +11110,1107 @@ var EVMClient = class {
11054
11110
  }
11055
11111
  };
11056
11112
 
11057
- // src/presets.ts
11058
- var CHAIN_NAMES = {
11059
- // EVM L2s (Hub-capable)
11060
- BASE: "base",
11061
- OPTIMISM: "optimism",
11062
- ARBITRUM: "arbitrum",
11063
- SCROLL: "scroll",
11064
- BLAST: "blast",
11065
- MANTLE: "mantle",
11066
- // EVM L1s
11067
- ETHEREUM: "ethereum",
11068
- POLYGON: "polygon",
11069
- BSC: "bsc",
11070
- AVALANCHE: "avalanche",
11071
- FANTOM: "fantom",
11072
- CELO: "celo",
11073
- MOONBEAM: "moonbeam",
11074
- // Non-EVM
11075
- SOLANA: "solana",
11076
- APTOS: "aptos",
11077
- SUI: "sui",
11078
- STARKNET: "starknet",
11079
- NEAR: "near",
11080
- SEI: "sei"
11113
+ // src/chains/stacks/StacksSigner.ts
11114
+ var P256_ORDER = BigInt(
11115
+ "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"
11116
+ );
11117
+ var P256_HALF_ORDER = P256_ORDER / 2n;
11118
+ function compressPublicKey(x, y) {
11119
+ const prefix = y % 2n === 0n ? 2 : 3;
11120
+ const xBytes = bigintToBytes(x, 32);
11121
+ const compressed = new Uint8Array(33);
11122
+ compressed[0] = prefix;
11123
+ compressed.set(xBytes, 1);
11124
+ return compressed;
11125
+ }
11126
+ function rsToCompactSignature(r, s) {
11127
+ const normalizedS = s > P256_HALF_ORDER ? P256_ORDER - s : s;
11128
+ const compact = new Uint8Array(64);
11129
+ compact.set(bigintToBytes(r, 32), 0);
11130
+ compact.set(bigintToBytes(normalizedS, 32), 32);
11131
+ return compact;
11132
+ }
11133
+ function parseDERSignature2(der) {
11134
+ if (der[0] !== 48) {
11135
+ throw new Error("Invalid DER signature: expected SEQUENCE tag 0x30");
11136
+ }
11137
+ let offset = 2;
11138
+ if (der[offset] !== 2) {
11139
+ throw new Error("Invalid DER signature: expected INTEGER tag 0x02 for r");
11140
+ }
11141
+ offset++;
11142
+ const rLen = der[offset];
11143
+ offset++;
11144
+ const rBytes = der.slice(offset, offset + rLen);
11145
+ offset += rLen;
11146
+ if (der[offset] !== 2) {
11147
+ throw new Error("Invalid DER signature: expected INTEGER tag 0x02 for s");
11148
+ }
11149
+ offset++;
11150
+ const sLen = der[offset];
11151
+ offset++;
11152
+ const sBytes = der.slice(offset, offset + sLen);
11153
+ return {
11154
+ r: bytesToBigint(rBytes),
11155
+ s: bytesToBigint(sBytes)
11156
+ };
11157
+ }
11158
+ function derToCompactSignature(der) {
11159
+ const { r, s } = parseDERSignature2(der);
11160
+ return rsToCompactSignature(r, s);
11161
+ }
11162
+ async function computeKeyHash2(compressedPubkey) {
11163
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", compressedPubkey.buffer);
11164
+ const hashArray = new Uint8Array(hashBuffer);
11165
+ return "0x" + bytesToHex(hashArray);
11166
+ }
11167
+ async function computeKeyHashFromCoords(x, y) {
11168
+ const compressed = compressPublicKey(x, y);
11169
+ return computeKeyHash2(compressed);
11170
+ }
11171
+ async function buildRegistrationHash(nonce) {
11172
+ const message = `veridex:register:${nonce}`;
11173
+ const encoded = new TextEncoder().encode(message);
11174
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11175
+ return new Uint8Array(hashBuffer);
11176
+ }
11177
+ async function buildSessionRegistrationHash(sessionKeyHash, duration, maxValue, nonce) {
11178
+ const cleanHash = sessionKeyHash.replace("0x", "");
11179
+ const message = `veridex:session:${cleanHash}:${duration}:${maxValue}:${nonce}`;
11180
+ const encoded = new TextEncoder().encode(message);
11181
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11182
+ return new Uint8Array(hashBuffer);
11183
+ }
11184
+ async function buildRevocationHash(sessionHash, nonce) {
11185
+ const cleanHash = sessionHash.replace("0x", "");
11186
+ const message = `veridex:revoke:${cleanHash}:${nonce}`;
11187
+ const encoded = new TextEncoder().encode(message);
11188
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11189
+ return new Uint8Array(hashBuffer);
11190
+ }
11191
+ async function buildExecuteHash(actionType, amount, recipient, nonce) {
11192
+ const message = `veridex:execute:${actionType}:${amount}:${recipient}:${nonce}`;
11193
+ const encoded = new TextEncoder().encode(message);
11194
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11195
+ return new Uint8Array(hashBuffer);
11196
+ }
11197
+ async function buildWithdrawalHash(amount, recipient, nonce) {
11198
+ const message = `veridex:withdraw:${amount}:${recipient}:${nonce}`;
11199
+ const encoded = new TextEncoder().encode(message);
11200
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded.buffer);
11201
+ return new Uint8Array(hashBuffer);
11202
+ }
11203
+ function bigintToBytes(value, length) {
11204
+ const hex = value.toString(16).padStart(length * 2, "0");
11205
+ const bytes = new Uint8Array(length);
11206
+ for (let i = 0; i < length; i++) {
11207
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
11208
+ }
11209
+ return bytes;
11210
+ }
11211
+ function bytesToBigint(bytes) {
11212
+ let start = 0;
11213
+ while (start < bytes.length - 1 && bytes[start] === 0) {
11214
+ start++;
11215
+ }
11216
+ let result = 0n;
11217
+ for (let i = start; i < bytes.length; i++) {
11218
+ result = result << 8n | BigInt(bytes[i]);
11219
+ }
11220
+ return result;
11221
+ }
11222
+ function bytesToHex(bytes) {
11223
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
11224
+ }
11225
+ function hexToBytes(hex) {
11226
+ const clean = hex.replace("0x", "");
11227
+ const bytes = new Uint8Array(clean.length / 2);
11228
+ for (let i = 0; i < bytes.length; i++) {
11229
+ bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
11230
+ }
11231
+ return bytes;
11232
+ }
11233
+
11234
+ // src/chains/stacks/StacksAddressUtils.ts
11235
+ var STACKS_MAINNET_PREFIX = "SP";
11236
+ var STACKS_TESTNET_PREFIX = "ST";
11237
+ var C32_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
11238
+ function isValidStacksPrincipal(address) {
11239
+ if (!address || typeof address !== "string") {
11240
+ return false;
11241
+ }
11242
+ const parts = address.split(".");
11243
+ if (parts.length > 2) {
11244
+ return false;
11245
+ }
11246
+ const standardPart = parts[0];
11247
+ const contractName = parts[1];
11248
+ if (!isValidStandardPrincipal(standardPart)) {
11249
+ return false;
11250
+ }
11251
+ if (contractName !== void 0) {
11252
+ if (!isValidContractName(contractName)) {
11253
+ return false;
11254
+ }
11255
+ }
11256
+ return true;
11257
+ }
11258
+ function isValidStandardPrincipal(address) {
11259
+ if (!address || address.length < 5) {
11260
+ return false;
11261
+ }
11262
+ const prefix = address.slice(0, 2);
11263
+ if (prefix !== STACKS_MAINNET_PREFIX && prefix !== STACKS_TESTNET_PREFIX) {
11264
+ return false;
11265
+ }
11266
+ if (address.length < 38 || address.length > 42) {
11267
+ return false;
11268
+ }
11269
+ const body = address.slice(1).toUpperCase();
11270
+ for (const char of body) {
11271
+ if (!C32_ALPHABET.includes(char)) {
11272
+ return false;
11273
+ }
11274
+ }
11275
+ return true;
11276
+ }
11277
+ function isValidContractName(name) {
11278
+ if (!name || name.length === 0 || name.length > 128) {
11279
+ return false;
11280
+ }
11281
+ if (!/^[a-zA-Z]/.test(name)) {
11282
+ return false;
11283
+ }
11284
+ if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) {
11285
+ return false;
11286
+ }
11287
+ return true;
11288
+ }
11289
+ function getNetworkFromAddress(address) {
11290
+ const prefix = address.slice(0, 2);
11291
+ if (prefix === STACKS_MAINNET_PREFIX) {
11292
+ return "mainnet";
11293
+ }
11294
+ return "testnet";
11295
+ }
11296
+ function getContractPrincipal(deployerAddress, contractName) {
11297
+ if (!isValidStandardPrincipal(deployerAddress)) {
11298
+ throw new Error(`Invalid deployer address: ${deployerAddress}`);
11299
+ }
11300
+ if (!isValidContractName(contractName)) {
11301
+ throw new Error(`Invalid contract name: ${contractName}`);
11302
+ }
11303
+ return `${deployerAddress}.${contractName}`;
11304
+ }
11305
+ function parseContractPrincipal(contractPrincipal) {
11306
+ const dotIndex = contractPrincipal.indexOf(".");
11307
+ if (dotIndex === -1) {
11308
+ throw new Error(
11309
+ `Not a contract principal: ${contractPrincipal}. Expected format: address.contract-name`
11310
+ );
11311
+ }
11312
+ return {
11313
+ address: contractPrincipal.slice(0, dotIndex),
11314
+ contractName: contractPrincipal.slice(dotIndex + 1)
11315
+ };
11316
+ }
11317
+ function isContractPrincipal(address) {
11318
+ return address.includes(".");
11319
+ }
11320
+ function getStacksExplorerTxUrl(txId, network = "testnet") {
11321
+ const cleanTxId = txId.startsWith("0x") ? txId : `0x${txId}`;
11322
+ const chainParam = network === "testnet" ? "&chain=testnet" : "";
11323
+ return `https://explorer.hiro.so/txid/${cleanTxId}?${chainParam}`;
11324
+ }
11325
+ function getStacksExplorerAddressUrl(address, network = "testnet") {
11326
+ const chainParam = network === "testnet" ? "?chain=testnet" : "";
11327
+ return `https://explorer.hiro.so/address/${address}${chainParam}`;
11328
+ }
11329
+
11330
+ // src/chains/stacks/StacksClient.ts
11331
+ var STACKS_ACTION_TYPES = {
11332
+ TRANSFER_STX: 1,
11333
+ TRANSFER_SBTC: 2,
11334
+ CONTRACT_CALL: 3
11081
11335
  };
11082
- var CHAIN_PRESETS = {
11083
- // ────────────────────────────────────────────────────────────────────────
11084
- // BASE - Primary Hub Chain
11085
- // ────────────────────────────────────────────────────────────────────────
11086
- base: {
11087
- displayName: "Base",
11088
- type: "evm",
11089
- canBeHub: true,
11090
- testnet: {
11091
- name: "Base Sepolia",
11092
- chainId: 84532,
11093
- wormholeChainId: 10004,
11094
- rpcUrl: "https://sepolia.base.org",
11095
- explorerUrl: "https://sepolia.basescan.org",
11096
- isEvm: true,
11097
- contracts: {
11098
- hub: "0x66D87dE68327f48A099c5B9bE97020Feab9a7c82",
11099
- vaultFactory: "0xCFaEb5652aa2Ee60b2229dC8895B4159749C7e53",
11100
- vaultImplementation: "0x0d13367C16c6f0B24eD275CC67C7D9f42878285c",
11101
- wormholeCoreBridge: "0x79A1027a6A159502049F10906D333EC57E95F083",
11102
- tokenBridge: "0x86F55A04690fd7815A3D802bD587e83eA888B239"
11103
- }
11104
- },
11105
- mainnet: {
11106
- name: "Base",
11107
- chainId: 8453,
11108
- wormholeChainId: 30,
11109
- rpcUrl: "https://mainnet.base.org",
11110
- explorerUrl: "https://basescan.org",
11111
- isEvm: true,
11112
- contracts: {
11113
- // TODO: Deploy mainnet contracts
11114
- wormholeCoreBridge: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6",
11115
- tokenBridge: "0x8d2de8d2f73F1F4cAB472AC9A881C9b123C79627"
11116
- }
11336
+ var HIRO_API = {
11337
+ testnet: "https://api.testnet.hiro.so",
11338
+ mainnet: "https://api.hiro.so"
11339
+ };
11340
+ var StacksClient = class {
11341
+ config;
11342
+ rpcUrl;
11343
+ spokeContract;
11344
+ vaultContract;
11345
+ wormholeVerifierContract;
11346
+ vaultVaaContract;
11347
+ networkType;
11348
+ constructor(clientConfig) {
11349
+ this.networkType = clientConfig.network || "testnet";
11350
+ this.rpcUrl = clientConfig.rpcUrl || HIRO_API[this.networkType];
11351
+ if (clientConfig.spokeContractAddress && isContractPrincipal(clientConfig.spokeContractAddress)) {
11352
+ const parsed = parseContractPrincipal(clientConfig.spokeContractAddress);
11353
+ this.spokeContract = { address: parsed.address, name: parsed.contractName };
11354
+ } else {
11355
+ this.spokeContract = null;
11117
11356
  }
11118
- },
11119
- // ────────────────────────────────────────────────────────────────────────
11120
- // OPTIMISM - Secondary Hub / Spoke
11121
- // ────────────────────────────────────────────────────────────────────────
11122
- optimism: {
11123
- displayName: "Optimism",
11124
- type: "evm",
11125
- canBeHub: true,
11126
- testnet: {
11127
- name: "Optimism Sepolia",
11128
- chainId: 11155420,
11129
- wormholeChainId: 10005,
11130
- rpcUrl: "https://sepolia.optimism.io",
11131
- explorerUrl: "https://sepolia-optimism.etherscan.io",
11132
- isEvm: true,
11133
- contracts: {
11134
- vaultFactory: "0xA5653d54079ABeCe780F8d9597B2bc4B09fe464A",
11135
- vaultImplementation: "0x8099b1406485d2255ff89Ce5Ea18520802AFC150",
11136
- wormholeCoreBridge: "0x31377888146f3253211EFEf5c676D41ECe7D58Fe",
11137
- tokenBridge: "0x99737Ec4B815d816c49A385943baf0380e75c0Ac"
11138
- }
11139
- },
11140
- mainnet: {
11141
- name: "Optimism",
11142
- chainId: 10,
11143
- wormholeChainId: 24,
11144
- rpcUrl: "https://mainnet.optimism.io",
11145
- explorerUrl: "https://optimistic.etherscan.io",
11146
- isEvm: true,
11147
- contracts: {
11148
- wormholeCoreBridge: "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722",
11149
- tokenBridge: "0x1D68124e65faFC907325e3EDbF8c4d84499DAa8b"
11150
- }
11357
+ if (clientConfig.vaultContractAddress && isContractPrincipal(clientConfig.vaultContractAddress)) {
11358
+ const parsed = parseContractPrincipal(clientConfig.vaultContractAddress);
11359
+ this.vaultContract = { address: parsed.address, name: parsed.contractName };
11360
+ } else {
11361
+ this.vaultContract = null;
11151
11362
  }
11152
- },
11153
- // ────────────────────────────────────────────────────────────────────────
11154
- // ARBITRUM
11155
- // ────────────────────────────────────────────────────────────────────────
11156
- arbitrum: {
11157
- displayName: "Arbitrum",
11158
- type: "evm",
11159
- canBeHub: true,
11160
- testnet: {
11161
- name: "Arbitrum Sepolia",
11162
- chainId: 421614,
11163
- wormholeChainId: 10003,
11164
- rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
11165
- explorerUrl: "https://sepolia.arbiscan.io",
11166
- isEvm: true,
11363
+ if (clientConfig.wormholeVerifierAddress && isContractPrincipal(clientConfig.wormholeVerifierAddress)) {
11364
+ const parsed = parseContractPrincipal(clientConfig.wormholeVerifierAddress);
11365
+ this.wormholeVerifierContract = { address: parsed.address, name: parsed.contractName };
11366
+ } else {
11367
+ this.wormholeVerifierContract = null;
11368
+ }
11369
+ if (clientConfig.vaultVaaContractAddress && isContractPrincipal(clientConfig.vaultVaaContractAddress)) {
11370
+ const parsed = parseContractPrincipal(clientConfig.vaultVaaContractAddress);
11371
+ this.vaultVaaContract = { address: parsed.address, name: parsed.contractName };
11372
+ } else {
11373
+ this.vaultVaaContract = null;
11374
+ }
11375
+ if (this.spokeContract && !this.vaultContract) {
11376
+ this.vaultContract = {
11377
+ address: this.spokeContract.address,
11378
+ name: "veridex-vault"
11379
+ };
11380
+ }
11381
+ if (this.spokeContract && !this.wormholeVerifierContract) {
11382
+ this.wormholeVerifierContract = {
11383
+ address: this.spokeContract.address,
11384
+ name: "veridex-wormhole-verifier"
11385
+ };
11386
+ }
11387
+ if (this.spokeContract && !this.vaultVaaContract) {
11388
+ this.vaultVaaContract = {
11389
+ address: this.spokeContract.address,
11390
+ name: "veridex-vault-vaa"
11391
+ };
11392
+ }
11393
+ this.config = {
11394
+ name: `Stacks ${this.networkType}`,
11395
+ chainId: this.networkType === "mainnet" ? 1 : 2147483648,
11396
+ wormholeChainId: clientConfig.wormholeChainId,
11397
+ rpcUrl: this.rpcUrl,
11398
+ explorerUrl: this.networkType === "testnet" ? "https://explorer.hiro.so/?chain=testnet" : "https://explorer.hiro.so",
11399
+ isEvm: false,
11167
11400
  contracts: {
11168
- vaultFactory: "0xd36D3D5DB59d78f1E33813490F72DABC15C9B07c",
11169
- vaultImplementation: "0xB10ACf39eBF17fc33F722cBD955b7aeCB0611bc4",
11170
- wormholeCoreBridge: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35",
11171
- tokenBridge: "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"
11401
+ hub: clientConfig.spokeContractAddress,
11402
+ wormholeCoreBridge: "",
11403
+ wormholeVerifier: this.wormholeVerifierContract ? `${this.wormholeVerifierContract.address}.${this.wormholeVerifierContract.name}` : void 0,
11404
+ vaultVaa: this.vaultVaaContract ? `${this.vaultVaaContract.address}.${this.vaultVaaContract.name}` : void 0
11172
11405
  }
11173
- },
11174
- mainnet: {
11175
- name: "Arbitrum",
11176
- chainId: 42161,
11177
- wormholeChainId: 23,
11178
- rpcUrl: "https://arb1.arbitrum.io/rpc",
11179
- explorerUrl: "https://arbiscan.io",
11180
- isEvm: true,
11181
- contracts: {
11182
- wormholeCoreBridge: "0xa5f208e072434bC67592E4C49C1B991BA79BCA46",
11183
- tokenBridge: "0x0b2402144Bb366A632D14B83F244D2e0e21bD39c"
11406
+ };
11407
+ }
11408
+ // ========================================================================
11409
+ // ChainClient Interface - Configuration
11410
+ // ========================================================================
11411
+ getConfig() {
11412
+ return this.config;
11413
+ }
11414
+ // ========================================================================
11415
+ // ChainClient Interface - Nonce & Fees
11416
+ // ========================================================================
11417
+ /**
11418
+ * Get the current nonce for a user identity from the spoke contract.
11419
+ * Calls the read-only function `get-nonce` on veridex-spoke.
11420
+ */
11421
+ async getNonce(userKeyHash) {
11422
+ if (!this.spokeContract) {
11423
+ return 0n;
11424
+ }
11425
+ try {
11426
+ const result = await this.callReadOnly(
11427
+ this.spokeContract.address,
11428
+ this.spokeContract.name,
11429
+ "get-nonce",
11430
+ [`0x${userKeyHash.replace("0x", "")}`]
11431
+ );
11432
+ if (result && result.value !== void 0) {
11433
+ return BigInt(result.value);
11184
11434
  }
11435
+ return 0n;
11436
+ } catch {
11437
+ return 0n;
11185
11438
  }
11186
- },
11439
+ }
11440
+ /**
11441
+ * Get the Wormhole message fee.
11442
+ * Phase 1: No Wormhole integration, returns 0.
11443
+ */
11444
+ async getMessageFee() {
11445
+ return 0n;
11446
+ }
11447
+ // ========================================================================
11448
+ // ChainClient Interface - Payload Building
11449
+ // ========================================================================
11450
+ async buildTransferPayload(params) {
11451
+ return encodeTransferAction(params.token, params.recipient, params.amount);
11452
+ }
11453
+ async buildExecutePayload(params) {
11454
+ return encodeExecuteAction(params.target, params.value, params.data);
11455
+ }
11456
+ async buildBridgePayload(params) {
11457
+ return encodeBridgeAction(params.token, params.amount, params.destinationChain, params.recipient);
11458
+ }
11459
+ // ========================================================================
11460
+ // ChainClient Interface - Dispatch
11461
+ // ========================================================================
11462
+ /**
11463
+ * Direct dispatch is not supported on Stacks in Phase 1.
11464
+ * Stacks actions are executed via sponsored transactions through the relayer.
11465
+ */
11466
+ async dispatch(_signature, _publicKeyX, _publicKeyY, _targetChain, _actionPayload, _nonce, _signer) {
11467
+ throw new Error(
11468
+ "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."
11469
+ );
11470
+ }
11471
+ /**
11472
+ * Dispatch an action via the relayer (gasless/sponsored).
11473
+ *
11474
+ * Flow:
11475
+ * 1. User signs action with Passkey (on client)
11476
+ * 2. SDK submits to relayer with targetChain=60 (Stacks)
11477
+ * 3. Relayer builds Clarity contract-call transaction
11478
+ * 4. Relayer sponsors the transaction (pays STX gas)
11479
+ * 5. Relayer broadcasts to Stacks network
11480
+ * 6. Transaction confirmed on Stacks
11481
+ */
11482
+ async dispatchGasless(signature, publicKeyX, publicKeyY, targetChain, actionPayload, nonce, relayerUrl) {
11483
+ const keyHash = await computeKeyHashFromCoords(publicKeyX, publicKeyY);
11484
+ const compressedPubkey = compressPublicKey(publicKeyX, publicKeyY);
11485
+ const compactSig = rsToCompactSignature(signature.r, signature.s);
11486
+ const request = {
11487
+ signature: {
11488
+ r: "0x" + signature.r.toString(16).padStart(64, "0"),
11489
+ s: "0x" + signature.s.toString(16).padStart(64, "0"),
11490
+ authenticatorData: signature.authenticatorData,
11491
+ clientDataJSON: signature.clientDataJSON,
11492
+ challengeIndex: signature.challengeIndex,
11493
+ typeIndex: signature.typeIndex
11494
+ },
11495
+ publicKeyX: "0x" + publicKeyX.toString(16).padStart(64, "0"),
11496
+ publicKeyY: "0x" + publicKeyY.toString(16).padStart(64, "0"),
11497
+ compressedPubkey: "0x" + bytesToHex(compressedPubkey),
11498
+ compactSignature: "0x" + bytesToHex(compactSig),
11499
+ targetChain,
11500
+ actionPayload,
11501
+ userNonce: Number(nonce)
11502
+ };
11503
+ const response = await fetch(`${relayerUrl}/api/v1/submit`, {
11504
+ method: "POST",
11505
+ headers: { "Content-Type": "application/json" },
11506
+ body: JSON.stringify(request)
11507
+ });
11508
+ if (!response.ok) {
11509
+ const errorText = await response.text().catch(() => "Unknown error");
11510
+ throw new Error(
11511
+ `Relayer submission failed: ${response.status} ${response.statusText}. Error: ${errorText}`
11512
+ );
11513
+ }
11514
+ const result = await response.json();
11515
+ return {
11516
+ transactionHash: result.transactionHash ?? result.txHash ?? result.hubTxHash ?? "",
11517
+ sequence: BigInt(result.sequence || 0),
11518
+ userKeyHash: keyHash,
11519
+ targetChain
11520
+ };
11521
+ }
11522
+ // ========================================================================
11523
+ // ChainClient Interface - Vault Management
11524
+ // ========================================================================
11525
+ /**
11526
+ * Get vault address for a user.
11527
+ * On Stacks, vaults are map-based within the vault contract.
11528
+ * The "vault address" is the vault contract principal itself.
11529
+ */
11530
+ async getVaultAddress(userKeyHash) {
11531
+ if (!this.vaultContract) {
11532
+ return null;
11533
+ }
11534
+ const exists = await this.vaultExists(userKeyHash);
11535
+ if (!exists) {
11536
+ return null;
11537
+ }
11538
+ return `${this.vaultContract.address}.${this.vaultContract.name}`;
11539
+ }
11540
+ /**
11541
+ * Compute vault address deterministically.
11542
+ * On Stacks, all vaults live in the same contract (map-based).
11543
+ */
11544
+ computeVaultAddress(_userKeyHash) {
11545
+ if (!this.vaultContract) {
11546
+ throw new Error("Vault contract not configured");
11547
+ }
11548
+ return `${this.vaultContract.address}.${this.vaultContract.name}`;
11549
+ }
11550
+ /**
11551
+ * Check if a vault (identity) exists for a user.
11552
+ * Queries the spoke contract's `identity-exists` read-only function.
11553
+ */
11554
+ async vaultExists(userKeyHash) {
11555
+ if (!this.spokeContract) {
11556
+ return false;
11557
+ }
11558
+ try {
11559
+ const result = await this.callReadOnly(
11560
+ this.spokeContract.address,
11561
+ this.spokeContract.name,
11562
+ "identity-exists",
11563
+ [`0x${userKeyHash.replace("0x", "")}`]
11564
+ );
11565
+ return result === true || result?.value === true;
11566
+ } catch {
11567
+ return false;
11568
+ }
11569
+ }
11570
+ /**
11571
+ * Create a vault (register identity) on Stacks.
11572
+ * Must be done via Hub dispatch or relayer in Phase 1.
11573
+ */
11574
+ async createVault(userKeyHash, _signer) {
11575
+ throw new Error(
11576
+ `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}`
11577
+ );
11578
+ }
11579
+ /**
11580
+ * Create a vault with a sponsor wallet.
11581
+ * On Stacks, this registers an identity via sponsored transaction.
11582
+ */
11583
+ async createVaultSponsored(userKeyHash, _sponsorPrivateKey, _rpcUrl) {
11584
+ throw new Error(
11585
+ `Sponsored vault creation on Stacks requires the user to sign with their Passkey. Use createVaultViaRelayer() which handles the sponsored transaction flow. KeyHash=${userKeyHash}`
11586
+ );
11587
+ }
11588
+ /**
11589
+ * Create a vault via the relayer (sponsored/gasless).
11590
+ * The relayer will sponsor the register-identity transaction.
11591
+ */
11592
+ async createVaultViaRelayer(userKeyHash, relayerUrl) {
11593
+ const response = await fetch(`${relayerUrl}/api/v1/stacks/vault`, {
11594
+ method: "POST",
11595
+ headers: { "Content-Type": "application/json" },
11596
+ body: JSON.stringify({
11597
+ userKeyHash,
11598
+ chainId: this.config.wormholeChainId
11599
+ })
11600
+ });
11601
+ const result = await response.json();
11602
+ if (!response.ok || !result.success) {
11603
+ throw new Error(result.error || "Failed to create vault via relayer");
11604
+ }
11605
+ return {
11606
+ address: result.vaultAddress || this.computeVaultAddress(userKeyHash),
11607
+ transactionHash: result.transactionHash || "",
11608
+ blockNumber: 0,
11609
+ gasUsed: 0n,
11610
+ alreadyExisted: result.alreadyExists || false,
11611
+ sponsoredBy: "relayer"
11612
+ };
11613
+ }
11614
+ async estimateVaultCreationGas(_userKeyHash) {
11615
+ return 10000n;
11616
+ }
11617
+ getFactoryAddress() {
11618
+ return void 0;
11619
+ }
11620
+ getImplementationAddress() {
11621
+ return void 0;
11622
+ }
11623
+ // ========================================================================
11624
+ // Stacks-Specific: Balance Queries
11625
+ // ========================================================================
11626
+ /**
11627
+ * Get native STX balance for an address.
11628
+ */
11629
+ async getNativeBalance(address) {
11630
+ try {
11631
+ const response = await fetch(
11632
+ `${this.rpcUrl}/v2/accounts/${address}?proof=0`
11633
+ );
11634
+ if (!response.ok) {
11635
+ return 0n;
11636
+ }
11637
+ const data = await response.json();
11638
+ return BigInt(data.balance || "0");
11639
+ } catch {
11640
+ return 0n;
11641
+ }
11642
+ }
11643
+ /**
11644
+ * Get vault STX balance for an identity.
11645
+ * Queries the vault contract's `get-stx-balance` read-only function.
11646
+ */
11647
+ async getVaultStxBalance(keyHash) {
11648
+ if (!this.vaultContract) {
11649
+ return 0n;
11650
+ }
11651
+ try {
11652
+ const result = await this.callReadOnly(
11653
+ this.vaultContract.address,
11654
+ this.vaultContract.name,
11655
+ "get-stx-balance",
11656
+ [`0x${keyHash.replace("0x", "")}`]
11657
+ );
11658
+ if (result && result.value !== void 0) {
11659
+ return BigInt(result.value);
11660
+ }
11661
+ return 0n;
11662
+ } catch {
11663
+ return 0n;
11664
+ }
11665
+ }
11666
+ /**
11667
+ * Get vault sBTC balance for an identity.
11668
+ */
11669
+ async getVaultSbtcBalance(keyHash) {
11670
+ if (!this.vaultContract) {
11671
+ return 0n;
11672
+ }
11673
+ try {
11674
+ const result = await this.callReadOnly(
11675
+ this.vaultContract.address,
11676
+ this.vaultContract.name,
11677
+ "get-sbtc-balance",
11678
+ [`0x${keyHash.replace("0x", "")}`]
11679
+ );
11680
+ if (result && result.value !== void 0) {
11681
+ return BigInt(result.value);
11682
+ }
11683
+ return 0n;
11684
+ } catch {
11685
+ return 0n;
11686
+ }
11687
+ }
11688
+ // ========================================================================
11689
+ // Stacks-Specific: Identity & Session Queries
11690
+ // ========================================================================
11691
+ /**
11692
+ * Get identity info from the spoke contract.
11693
+ */
11694
+ async getIdentity(keyHash) {
11695
+ if (!this.spokeContract) {
11696
+ return null;
11697
+ }
11698
+ try {
11699
+ const result = await this.callReadOnly(
11700
+ this.spokeContract.address,
11701
+ this.spokeContract.name,
11702
+ "get-identity",
11703
+ [`0x${keyHash.replace("0x", "")}`]
11704
+ );
11705
+ if (!result || result.value === void 0) {
11706
+ return null;
11707
+ }
11708
+ const val = result.value;
11709
+ return {
11710
+ compressedPubkey: val["compressed-pubkey"]?.value || "",
11711
+ owner: val.owner?.value || "",
11712
+ nonce: BigInt(val.nonce?.value || 0),
11713
+ createdAt: BigInt(val["created-at"]?.value || 0)
11714
+ };
11715
+ } catch {
11716
+ return null;
11717
+ }
11718
+ }
11719
+ /**
11720
+ * Get session info from the spoke contract.
11721
+ */
11722
+ async getSession(keyHash, sessionHash) {
11723
+ if (!this.spokeContract) {
11724
+ return null;
11725
+ }
11726
+ try {
11727
+ const cleanKeyHash = `0x${keyHash.replace("0x", "")}`;
11728
+ const cleanSessionHash = `0x${sessionHash.replace("0x", "")}`;
11729
+ const result = await this.callReadOnly(
11730
+ this.spokeContract.address,
11731
+ this.spokeContract.name,
11732
+ "get-session",
11733
+ [cleanKeyHash, cleanSessionHash]
11734
+ );
11735
+ if (!result || result.value === void 0) {
11736
+ return null;
11737
+ }
11738
+ const val = result.value;
11739
+ return {
11740
+ sessionPubkey: val["session-pubkey"]?.value || "",
11741
+ expiry: BigInt(val.expiry?.value || 0),
11742
+ maxValue: BigInt(val["max-value"]?.value || 0),
11743
+ spent: BigInt(val.spent?.value || 0),
11744
+ revoked: val.revoked?.value === true,
11745
+ createdAt: BigInt(val["created-at"]?.value || 0)
11746
+ };
11747
+ } catch {
11748
+ return null;
11749
+ }
11750
+ }
11751
+ /**
11752
+ * Check if a session is currently active.
11753
+ */
11754
+ async checkSessionActive(keyHash, sessionHash) {
11755
+ if (!this.spokeContract) {
11756
+ return false;
11757
+ }
11758
+ try {
11759
+ const cleanKeyHash = `0x${keyHash.replace("0x", "")}`;
11760
+ const cleanSessionHash = `0x${sessionHash.replace("0x", "")}`;
11761
+ const result = await this.callReadOnly(
11762
+ this.spokeContract.address,
11763
+ this.spokeContract.name,
11764
+ "is-session-active",
11765
+ [cleanKeyHash, cleanSessionHash]
11766
+ );
11767
+ return result?.value === true;
11768
+ } catch {
11769
+ return false;
11770
+ }
11771
+ }
11772
+ /**
11773
+ * Get remaining spending budget for a session.
11774
+ */
11775
+ async getRemainingBudget(keyHash, sessionHash) {
11776
+ if (!this.spokeContract) {
11777
+ return 0n;
11778
+ }
11779
+ try {
11780
+ const cleanKeyHash = `0x${keyHash.replace("0x", "")}`;
11781
+ const cleanSessionHash = `0x${sessionHash.replace("0x", "")}`;
11782
+ const result = await this.callReadOnly(
11783
+ this.spokeContract.address,
11784
+ this.spokeContract.name,
11785
+ "get-remaining-budget",
11786
+ [cleanKeyHash, cleanSessionHash]
11787
+ );
11788
+ if (result && result.value !== void 0) {
11789
+ return BigInt(result.value);
11790
+ }
11791
+ return 0n;
11792
+ } catch {
11793
+ return 0n;
11794
+ }
11795
+ }
11796
+ // ========================================================================
11797
+ // Stacks-Specific: Protocol Status
11798
+ // ========================================================================
11799
+ /**
11800
+ * Check if the spoke contract is paused.
11801
+ */
11802
+ async isProtocolPaused() {
11803
+ if (!this.spokeContract) {
11804
+ return false;
11805
+ }
11806
+ try {
11807
+ const result = await this.callReadOnly(
11808
+ this.spokeContract.address,
11809
+ this.spokeContract.name,
11810
+ "is-paused",
11811
+ []
11812
+ );
11813
+ return result === true || result?.value === true;
11814
+ } catch {
11815
+ return false;
11816
+ }
11817
+ }
11818
+ /**
11819
+ * Get global identity count.
11820
+ */
11821
+ async getIdentityCount() {
11822
+ if (!this.spokeContract) {
11823
+ return 0n;
11824
+ }
11825
+ try {
11826
+ const result = await this.callReadOnly(
11827
+ this.spokeContract.address,
11828
+ this.spokeContract.name,
11829
+ "get-identity-count",
11830
+ []
11831
+ );
11832
+ return BigInt(result?.value || result || 0);
11833
+ } catch {
11834
+ return 0n;
11835
+ }
11836
+ }
11837
+ /**
11838
+ * Get total STX deposited across all vaults.
11839
+ */
11840
+ async getTotalStxDeposited() {
11841
+ if (!this.vaultContract) {
11842
+ return 0n;
11843
+ }
11844
+ try {
11845
+ const result = await this.callReadOnly(
11846
+ this.vaultContract.address,
11847
+ this.vaultContract.name,
11848
+ "get-total-stx-deposited",
11849
+ []
11850
+ );
11851
+ return BigInt(result?.value || result || 0);
11852
+ } catch {
11853
+ return 0n;
11854
+ }
11855
+ }
11856
+ // ========================================================================
11857
+ // Session Management (Issue #13)
11858
+ // ========================================================================
11859
+ /**
11860
+ * Register a session key on the Stacks spoke.
11861
+ * On Stacks, sessions are managed directly on the spoke contract
11862
+ * (unlike EVM spokes where sessions are on the Hub).
11863
+ */
11864
+ async registerSession(_params) {
11865
+ throw new Error(
11866
+ "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."
11867
+ );
11868
+ }
11869
+ /**
11870
+ * Revoke a session key on the Stacks spoke.
11871
+ */
11872
+ async revokeSession(_params) {
11873
+ throw new Error(
11874
+ "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."
11875
+ );
11876
+ }
11877
+ /**
11878
+ * Check if a session is active.
11879
+ */
11880
+ async isSessionActive(userKeyHash, sessionKeyHash) {
11881
+ const active = await this.checkSessionActive(userKeyHash, sessionKeyHash);
11882
+ const session = await this.getSession(userKeyHash, sessionKeyHash);
11883
+ return {
11884
+ isActive: active,
11885
+ expiry: session ? Number(session.expiry) : 0,
11886
+ maxValue: session?.maxValue ?? 0n,
11887
+ chainScopes: [this.config.wormholeChainId]
11888
+ };
11889
+ }
11890
+ /**
11891
+ * Get all sessions for a user.
11892
+ * Note: Clarity maps don't support enumeration, so this requires
11893
+ * off-chain indexing or event log parsing.
11894
+ */
11895
+ async getUserSessions(_userKeyHash) {
11896
+ throw new Error(
11897
+ '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.'
11898
+ );
11899
+ }
11900
+ // ========================================================================
11901
+ // Stacks-Specific: Transaction Status
11902
+ // ========================================================================
11903
+ /**
11904
+ * Get the status of a Stacks transaction.
11905
+ */
11906
+ async getTransactionStatus(txId) {
11907
+ try {
11908
+ const cleanTxId = txId.startsWith("0x") ? txId : `0x${txId}`;
11909
+ const response = await fetch(
11910
+ `${this.rpcUrl}/extended/v1/tx/${cleanTxId}`
11911
+ );
11912
+ if (!response.ok) {
11913
+ return { status: "not_found" };
11914
+ }
11915
+ const data = await response.json();
11916
+ const txStatus = data.tx_status;
11917
+ if (txStatus === "success") {
11918
+ return {
11919
+ status: "success",
11920
+ blockHeight: data.block_height
11921
+ };
11922
+ }
11923
+ if (txStatus === "pending") {
11924
+ return { status: "pending" };
11925
+ }
11926
+ if (txStatus === "abort_by_response" || txStatus === "abort_by_post_condition") {
11927
+ return {
11928
+ status: "failed",
11929
+ error: `Transaction aborted: ${txStatus}`
11930
+ };
11931
+ }
11932
+ return { status: "pending" };
11933
+ } catch {
11934
+ return { status: "not_found" };
11935
+ }
11936
+ }
11937
+ /**
11938
+ * Wait for a transaction to be confirmed.
11939
+ *
11940
+ * @param txId - Transaction ID
11941
+ * @param maxAttempts - Maximum polling attempts (default: 60)
11942
+ * @param pollIntervalMs - Polling interval in milliseconds (default: 5000)
11943
+ */
11944
+ async waitForConfirmation(txId, maxAttempts = 60, pollIntervalMs = 5e3) {
11945
+ for (let i = 0; i < maxAttempts; i++) {
11946
+ const status = await this.getTransactionStatus(txId);
11947
+ if (status.status === "success") {
11948
+ return { confirmed: true, blockHeight: status.blockHeight };
11949
+ }
11950
+ if (status.status === "failed") {
11951
+ throw new Error(`Transaction failed: ${status.error}`);
11952
+ }
11953
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
11954
+ }
11955
+ return { confirmed: false };
11956
+ }
11957
+ // ========================================================================
11958
+ // Stacks-Specific: Network Info
11959
+ // ========================================================================
11960
+ /**
11961
+ * Get Stacks network info (block height, network version, etc.).
11962
+ */
11963
+ async getNetworkInfo() {
11964
+ const response = await fetch(`${this.rpcUrl}/v2/info`);
11965
+ if (!response.ok) {
11966
+ throw new Error(`Failed to get Stacks network info: ${response.statusText}`);
11967
+ }
11968
+ const data = await response.json();
11969
+ return {
11970
+ networkId: data.network_id,
11971
+ stacksBlockHeight: data.stacks_tip_height,
11972
+ burnBlockHeight: data.burn_block_height,
11973
+ serverVersion: data.server_version
11974
+ };
11975
+ }
11976
+ /**
11977
+ * Get the current Stacks block height.
11978
+ * Used for session expiry calculations.
11979
+ */
11980
+ async getCurrentBlockHeight() {
11981
+ const info = await this.getNetworkInfo();
11982
+ return info.stacksBlockHeight;
11983
+ }
11984
+ // ========================================================================
11985
+ // Internal: Read-Only Contract Calls via Hiro API
11986
+ // ========================================================================
11987
+ /**
11988
+ * Call a read-only Clarity function via the Hiro API.
11989
+ * Uses the /v2/contracts/call-read endpoint.
11990
+ */
11991
+ async callReadOnly(contractAddress, contractName, functionName, args) {
11992
+ const url = `${this.rpcUrl}/v2/contracts/call-read/${contractAddress}/${contractName}/${functionName}`;
11993
+ const response = await fetch(url, {
11994
+ method: "POST",
11995
+ headers: { "Content-Type": "application/json" },
11996
+ body: JSON.stringify({
11997
+ sender: contractAddress,
11998
+ arguments: args
11999
+ })
12000
+ });
12001
+ if (!response.ok) {
12002
+ const errorText = await response.text().catch(() => "Unknown error");
12003
+ throw new Error(
12004
+ `Read-only call failed: ${contractAddress}.${contractName}::${functionName} - ${response.status}: ${errorText}`
12005
+ );
12006
+ }
12007
+ const data = await response.json();
12008
+ if (!data.okay) {
12009
+ throw new Error(
12010
+ `Read-only call returned error: ${contractAddress}.${contractName}::${functionName} - ${data.cause || "Unknown cause"}`
12011
+ );
12012
+ }
12013
+ return this.parseClarityValue(data.result);
12014
+ }
12015
+ /**
12016
+ * Parse a hex-encoded Clarity value from the API response.
12017
+ * This is a simplified parser for common Clarity types.
12018
+ */
12019
+ parseClarityValue(hex) {
12020
+ if (!hex || hex === "0x") {
12021
+ return null;
12022
+ }
12023
+ const bytes = hexToBytes(hex);
12024
+ if (bytes.length === 0) {
12025
+ return null;
12026
+ }
12027
+ const typeId = bytes[0];
12028
+ switch (typeId) {
12029
+ // int (0x00)
12030
+ case 0: {
12031
+ let value = 0n;
12032
+ for (let i = 1; i < 17 && i < bytes.length; i++) {
12033
+ value = value << 8n | BigInt(bytes[i]);
12034
+ }
12035
+ return { value };
12036
+ }
12037
+ // uint (0x01)
12038
+ case 1: {
12039
+ let value = 0n;
12040
+ for (let i = 1; i < 17 && i < bytes.length; i++) {
12041
+ value = value << 8n | BigInt(bytes[i]);
12042
+ }
12043
+ return { value };
12044
+ }
12045
+ // buffer (0x02)
12046
+ case 2: {
12047
+ const len = bytes[1] << 24 | bytes[2] << 16 | bytes[3] << 8 | bytes[4];
12048
+ const bufValue = bytes.slice(5, 5 + len);
12049
+ return { value: "0x" + bytesToHex(bufValue) };
12050
+ }
12051
+ // bool true (0x03)
12052
+ case 3:
12053
+ return true;
12054
+ // bool false (0x04)
12055
+ case 4:
12056
+ return false;
12057
+ // optional none (0x09)
12058
+ case 9:
12059
+ return null;
12060
+ // optional some (0x0a)
12061
+ case 10:
12062
+ return this.parseClarityValue("0x" + bytesToHex(bytes.slice(1)));
12063
+ // response ok (0x07)
12064
+ case 7:
12065
+ return this.parseClarityValue("0x" + bytesToHex(bytes.slice(1)));
12066
+ // response err (0x08)
12067
+ case 8: {
12068
+ const errVal = this.parseClarityValue("0x" + bytesToHex(bytes.slice(1)));
12069
+ throw new Error(`Clarity error: ${JSON.stringify(errVal)}`);
12070
+ }
12071
+ // tuple (0x0c)
12072
+ case 12: {
12073
+ return { value: hex };
12074
+ }
12075
+ default:
12076
+ return { value: hex };
12077
+ }
12078
+ }
12079
+ };
12080
+
12081
+ // src/presets.ts
12082
+ var CHAIN_NAMES = {
12083
+ // EVM L2s (Hub-capable)
12084
+ BASE: "base",
12085
+ OPTIMISM: "optimism",
12086
+ ARBITRUM: "arbitrum",
12087
+ SCROLL: "scroll",
12088
+ BLAST: "blast",
12089
+ MANTLE: "mantle",
12090
+ // EVM L1s
12091
+ ETHEREUM: "ethereum",
12092
+ POLYGON: "polygon",
12093
+ BSC: "bsc",
12094
+ AVALANCHE: "avalanche",
12095
+ FANTOM: "fantom",
12096
+ CELO: "celo",
12097
+ MOONBEAM: "moonbeam",
12098
+ // EVM L1s (High Performance)
12099
+ MONAD: "monad",
12100
+ // Non-EVM
12101
+ SOLANA: "solana",
12102
+ APTOS: "aptos",
12103
+ SUI: "sui",
12104
+ STARKNET: "starknet",
12105
+ STACKS: "stacks",
12106
+ NEAR: "near",
12107
+ SEI: "sei"
12108
+ };
12109
+ var CHAIN_PRESETS = {
12110
+ // ────────────────────────────────────────────────────────────────────────
12111
+ // BASE - Primary Hub Chain
12112
+ // ────────────────────────────────────────────────────────────────────────
12113
+ base: {
12114
+ displayName: "Base",
12115
+ type: "evm",
12116
+ canBeHub: true,
12117
+ testnet: {
12118
+ name: "Base Sepolia",
12119
+ chainId: 84532,
12120
+ wormholeChainId: 10004,
12121
+ rpcUrl: "https://sepolia.base.org",
12122
+ explorerUrl: "https://sepolia.basescan.org",
12123
+ isEvm: true,
12124
+ contracts: {
12125
+ hub: "0x66D87dE68327f48A099c5B9bE97020Feab9a7c82",
12126
+ vaultFactory: "0xCFaEb5652aa2Ee60b2229dC8895B4159749C7e53",
12127
+ vaultImplementation: "0x0d13367C16c6f0B24eD275CC67C7D9f42878285c",
12128
+ wormholeCoreBridge: "0x79A1027a6A159502049F10906D333EC57E95F083",
12129
+ tokenBridge: "0x86F55A04690fd7815A3D802bD587e83eA888B239"
12130
+ }
12131
+ },
12132
+ mainnet: {
12133
+ name: "Base",
12134
+ chainId: 8453,
12135
+ wormholeChainId: 30,
12136
+ rpcUrl: "https://mainnet.base.org",
12137
+ explorerUrl: "https://basescan.org",
12138
+ isEvm: true,
12139
+ contracts: {
12140
+ // TODO: Deploy mainnet contracts
12141
+ wormholeCoreBridge: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6",
12142
+ tokenBridge: "0x8d2de8d2f73F1F4cAB472AC9A881C9b123C79627"
12143
+ }
12144
+ }
12145
+ },
12146
+ // ────────────────────────────────────────────────────────────────────────
12147
+ // OPTIMISM - Secondary Hub / Spoke
12148
+ // ────────────────────────────────────────────────────────────────────────
12149
+ optimism: {
12150
+ displayName: "Optimism",
12151
+ type: "evm",
12152
+ canBeHub: true,
12153
+ testnet: {
12154
+ name: "Optimism Sepolia",
12155
+ chainId: 11155420,
12156
+ wormholeChainId: 10005,
12157
+ rpcUrl: "https://sepolia.optimism.io",
12158
+ explorerUrl: "https://sepolia-optimism.etherscan.io",
12159
+ isEvm: true,
12160
+ contracts: {
12161
+ vaultFactory: "0xA5653d54079ABeCe780F8d9597B2bc4B09fe464A",
12162
+ vaultImplementation: "0x8099b1406485d2255ff89Ce5Ea18520802AFC150",
12163
+ wormholeCoreBridge: "0x31377888146f3253211EFEf5c676D41ECe7D58Fe",
12164
+ tokenBridge: "0x99737Ec4B815d816c49A385943baf0380e75c0Ac"
12165
+ }
12166
+ },
12167
+ mainnet: {
12168
+ name: "Optimism",
12169
+ chainId: 10,
12170
+ wormholeChainId: 24,
12171
+ rpcUrl: "https://mainnet.optimism.io",
12172
+ explorerUrl: "https://optimistic.etherscan.io",
12173
+ isEvm: true,
12174
+ contracts: {
12175
+ wormholeCoreBridge: "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722",
12176
+ tokenBridge: "0x1D68124e65faFC907325e3EDbF8c4d84499DAa8b"
12177
+ }
12178
+ }
12179
+ },
12180
+ // ────────────────────────────────────────────────────────────────────────
12181
+ // ARBITRUM
12182
+ // ────────────────────────────────────────────────────────────────────────
12183
+ arbitrum: {
12184
+ displayName: "Arbitrum",
12185
+ type: "evm",
12186
+ canBeHub: true,
12187
+ testnet: {
12188
+ name: "Arbitrum Sepolia",
12189
+ chainId: 421614,
12190
+ wormholeChainId: 10003,
12191
+ rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
12192
+ explorerUrl: "https://sepolia.arbiscan.io",
12193
+ isEvm: true,
12194
+ contracts: {
12195
+ vaultFactory: "0xd36D3D5DB59d78f1E33813490F72DABC15C9B07c",
12196
+ vaultImplementation: "0xB10ACf39eBF17fc33F722cBD955b7aeCB0611bc4",
12197
+ wormholeCoreBridge: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35",
12198
+ tokenBridge: "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"
12199
+ }
12200
+ },
12201
+ mainnet: {
12202
+ name: "Arbitrum",
12203
+ chainId: 42161,
12204
+ wormholeChainId: 23,
12205
+ rpcUrl: "https://arb1.arbitrum.io/rpc",
12206
+ explorerUrl: "https://arbiscan.io",
12207
+ isEvm: true,
12208
+ contracts: {
12209
+ wormholeCoreBridge: "0xa5f208e072434bC67592E4C49C1B991BA79BCA46",
12210
+ tokenBridge: "0x0b2402144Bb366A632D14B83F244D2e0e21bD39c"
12211
+ }
12212
+ }
12213
+ },
11187
12214
  // ────────────────────────────────────────────────────────────────────────
11188
12215
  // ETHEREUM
11189
12216
  // ────────────────────────────────────────────────────────────────────────
@@ -11507,6 +12534,45 @@ var CHAIN_PRESETS = {
11507
12534
  }
11508
12535
  },
11509
12536
  // ────────────────────────────────────────────────────────────────────────
12537
+ // MONAD - High-Performance L1 with EIP-7951 P256 + Agent Gateway
12538
+ // ────────────────────────────────────────────────────────────────────────
12539
+ monad: {
12540
+ displayName: "Monad",
12541
+ type: "evm",
12542
+ canBeHub: true,
12543
+ // Has native P-256 precompile (EIP-7951)
12544
+ testnet: {
12545
+ name: "Monad Testnet",
12546
+ chainId: 10143,
12547
+ wormholeChainId: 10048,
12548
+ rpcUrl: "https://testnet-rpc.monad.xyz",
12549
+ explorerUrl: "https://testnet.monadvision.com",
12550
+ isEvm: true,
12551
+ contracts: {
12552
+ vaultFactory: "0x07F608AFf6d63b68029488b726d895c4Bb593038",
12553
+ vaultImplementation: "0xD66153fccFB6731fB6c4944FbD607ba86A76a1f6",
12554
+ wormholeCoreBridge: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd",
12555
+ // Agent Gateway contracts
12556
+ serviceDirectory: "0x0D2B4193e78107678a5aC29d795e0EcD361aE3A7"
12557
+ },
12558
+ hubChainId: 10004
12559
+ // Base Sepolia
12560
+ },
12561
+ mainnet: {
12562
+ name: "Monad",
12563
+ chainId: 143,
12564
+ wormholeChainId: 0,
12565
+ // TBD
12566
+ rpcUrl: "https://rpc.monad.xyz",
12567
+ explorerUrl: "https://monadscan.com",
12568
+ isEvm: true,
12569
+ contracts: {
12570
+ wormholeCoreBridge: "0x194B123c5E96B9b2E49763619985790Dc241CAC0",
12571
+ tokenBridge: "0x0B2719cdA2F10595369e6673ceA3Ee2EDFa13BA7"
12572
+ }
12573
+ }
12574
+ },
12575
+ // ────────────────────────────────────────────────────────────────────────
11510
12576
  // SEI
11511
12577
  // ────────────────────────────────────────────────────────────────────────
11512
12578
  sei: {
@@ -11671,6 +12737,49 @@ var CHAIN_PRESETS = {
11671
12737
  }
11672
12738
  },
11673
12739
  // ────────────────────────────────────────────────────────────────────────
12740
+ // STACKS
12741
+ // ────────────────────────────────────────────────────────────────────────
12742
+ stacks: {
12743
+ displayName: "Stacks",
12744
+ type: "stacks",
12745
+ canBeHub: false,
12746
+ testnet: {
12747
+ name: "Stacks Testnet",
12748
+ chainId: 2147483648,
12749
+ // CAIP-2: stacks:2147483648
12750
+ wormholeChainId: 60,
12751
+ // Official Wormhole chain ID for Stacks
12752
+ rpcUrl: "https://api.testnet.hiro.so",
12753
+ explorerUrl: "https://explorer.hiro.so/?chain=testnet",
12754
+ isEvm: false,
12755
+ contracts: {
12756
+ // Spoke contract: identity + session management
12757
+ hub: "ST1CKNN84MDPCJRQDHDCY34GMRKQ3TASNNDJQDRTN.veridex-spoke",
12758
+ // Vault contract: STX/sBTC custody
12759
+ vaultFactory: "ST1CKNN84MDPCJRQDHDCY34GMRKQ3TASNNDJQDRTN.veridex-vault",
12760
+ wormholeCoreBridge: "",
12761
+ // Phase 2: Wormhole integration contracts
12762
+ wormholeVerifier: "ST1CKNN84MDPCJRQDHDCY34GMRKQ3TASNNDJQDRTN.veridex-wormhole-verifier",
12763
+ vaultVaa: "ST1CKNN84MDPCJRQDHDCY34GMRKQ3TASNNDJQDRTN.veridex-vault-vaa"
12764
+ },
12765
+ hubChainId: 10004
12766
+ // Base Sepolia
12767
+ },
12768
+ mainnet: {
12769
+ name: "Stacks",
12770
+ chainId: 1,
12771
+ // CAIP-2: stacks:1
12772
+ wormholeChainId: 60,
12773
+ rpcUrl: "https://api.hiro.so",
12774
+ explorerUrl: "https://explorer.hiro.so",
12775
+ isEvm: false,
12776
+ contracts: {
12777
+ // TODO: Deploy mainnet contracts
12778
+ wormholeCoreBridge: ""
12779
+ }
12780
+ }
12781
+ },
12782
+ // ────────────────────────────────────────────────────────────────────────
11674
12783
  // NEAR
11675
12784
  // ────────────────────────────────────────────────────────────────────────
11676
12785
  near: {
@@ -11793,6 +12902,13 @@ function createChainClient(chain, network, customRpcUrl) {
11793
12902
  wormholeChainId: config.wormholeChainId,
11794
12903
  network: network === "testnet" ? "sepolia" : "mainnet"
11795
12904
  });
12905
+ case "stacks":
12906
+ return new StacksClient({
12907
+ rpcUrl,
12908
+ spokeContractAddress: config.contracts.hub || void 0,
12909
+ wormholeChainId: config.wormholeChainId,
12910
+ network
12911
+ });
11796
12912
  case "near":
11797
12913
  case "cosmos":
11798
12914
  throw new Error(`Chain type "${preset.type}" is not yet supported. Coming soon!`);
@@ -11848,8 +12964,8 @@ function createSessionSDK(chain = "base", config = {}) {
11848
12964
  }
11849
12965
 
11850
12966
  // src/core/CrossOriginAuth.ts
11851
- var VERIDEX_RP_ID = "veridex.network";
11852
12967
  var DEFAULT_AUTH_PORTAL_URL = "https://auth.veridex.network";
12968
+ var DEFAULT_RELAYER_URL = "https://amused-kameko-veridex-demo-37453117.koyeb.app/api/v1";
11853
12969
  var AUTH_MESSAGE_TYPES = {
11854
12970
  AUTH_REQUEST: "VERIDEX_AUTH_REQUEST",
11855
12971
  AUTH_RESPONSE: "VERIDEX_AUTH_RESPONSE",
@@ -11862,6 +12978,7 @@ var CrossOriginAuth = class {
11862
12978
  this.config = {
11863
12979
  rpId: config.rpId ?? VERIDEX_RP_ID,
11864
12980
  authPortalUrl: config.authPortalUrl ?? DEFAULT_AUTH_PORTAL_URL,
12981
+ relayerUrl: config.relayerUrl ?? DEFAULT_RELAYER_URL,
11865
12982
  mode: config.mode ?? "popup",
11866
12983
  popupFeatures: config.popupFeatures ?? "width=500,height=600,left=100,top=100",
11867
12984
  timeout: config.timeout ?? 12e4,
@@ -12049,6 +13166,84 @@ var CrossOriginAuth = class {
12049
13166
  getAuthPortalUrl() {
12050
13167
  return this.config.authPortalUrl;
12051
13168
  }
13169
+ // ========================================================================
13170
+ // Server-Side Session Tokens (ADR-0018)
13171
+ // ========================================================================
13172
+ /**
13173
+ * Create a server-validated session token via the relayer.
13174
+ * Call this after authenticating (via ROR or auth portal) to get a
13175
+ * server-side session that the relayer can verify on subsequent requests.
13176
+ */
13177
+ async createServerSession(session, options) {
13178
+ const keyHash = session.credential?.keyHash;
13179
+ if (!keyHash) {
13180
+ throw new Error("Session must include credential with keyHash");
13181
+ }
13182
+ const response = await fetch(`${this.config.relayerUrl}/session/create`, {
13183
+ method: "POST",
13184
+ headers: { "Content-Type": "application/json" },
13185
+ body: JSON.stringify({
13186
+ keyHash,
13187
+ appOrigin: typeof window !== "undefined" ? window.location.origin : "",
13188
+ sessionPublicKey: session.sessionPublicKey || "",
13189
+ permissions: options?.permissions ?? ["read", "transfer"],
13190
+ expiresInMs: options?.expiresInMs ?? 36e5,
13191
+ signature: session.signature
13192
+ })
13193
+ });
13194
+ if (!response.ok) {
13195
+ const data2 = await response.json().catch(() => ({ error: "Unknown error" }));
13196
+ throw new Error(data2.error || `Failed to create server session: ${response.status}`);
13197
+ }
13198
+ const data = await response.json();
13199
+ return data.session;
13200
+ }
13201
+ /**
13202
+ * Validate an existing server session token.
13203
+ * Returns the session details if valid, null if expired/revoked.
13204
+ */
13205
+ async validateServerSession(sessionId) {
13206
+ const response = await fetch(`${this.config.relayerUrl}/session/${encodeURIComponent(sessionId)}`);
13207
+ if (!response.ok) {
13208
+ return null;
13209
+ }
13210
+ const data = await response.json();
13211
+ if (!data.valid) {
13212
+ return null;
13213
+ }
13214
+ return data.session;
13215
+ }
13216
+ /**
13217
+ * Revoke a server session token.
13218
+ */
13219
+ async revokeServerSession(sessionId) {
13220
+ const response = await fetch(`${this.config.relayerUrl}/session/${encodeURIComponent(sessionId)}`, {
13221
+ method: "DELETE"
13222
+ });
13223
+ return response.ok;
13224
+ }
13225
+ /**
13226
+ * Full authentication flow: authenticate + create server session.
13227
+ * Automatically detects ROR support and falls back to auth portal.
13228
+ */
13229
+ async authenticateAndCreateSession(options) {
13230
+ let session;
13231
+ if (await this.supportsRelatedOrigins()) {
13232
+ const result = await this.authenticate();
13233
+ session = {
13234
+ address: "",
13235
+ sessionPublicKey: "",
13236
+ expiresAt: Date.now() + (options?.expiresInMs ?? 36e5),
13237
+ signature: result.signature,
13238
+ credential: result.credential
13239
+ };
13240
+ } else {
13241
+ session = await this.connectWithVeridex();
13242
+ }
13243
+ const serverSession = await this.createServerSession(session, options);
13244
+ session.serverSessionId = serverSession.id;
13245
+ return { session, serverSession };
13246
+ }
12052
13247
  };
12053
13248
  function createCrossOriginAuth(config) {
12054
13249
  return new CrossOriginAuth(config);
@@ -13102,6 +14297,72 @@ var EVMHubClientAdapter = class {
13102
14297
  }
13103
14298
  };
13104
14299
 
14300
+ // src/chains/stacks/StacksPostConditions.ts
14301
+ function buildStxWithdrawalPostConditions(contractPrincipal, amount) {
14302
+ return [
14303
+ {
14304
+ type: "stx",
14305
+ principal: contractPrincipal,
14306
+ comparison: "eq",
14307
+ amount
14308
+ }
14309
+ ];
14310
+ }
14311
+ function buildStxDepositPostConditions(senderPrincipal, amount) {
14312
+ return [
14313
+ {
14314
+ type: "stx",
14315
+ principal: senderPrincipal,
14316
+ comparison: "eq",
14317
+ amount
14318
+ }
14319
+ ];
14320
+ }
14321
+ function buildSbtcWithdrawalPostConditions(contractPrincipal, amount, sbtcContractAddress = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4", sbtcContractName = "sbtc-token") {
14322
+ return [
14323
+ {
14324
+ type: "ft",
14325
+ principal: contractPrincipal,
14326
+ comparison: "eq",
14327
+ amount,
14328
+ contractAddress: sbtcContractAddress,
14329
+ contractName: sbtcContractName,
14330
+ tokenName: "sbtc-token"
14331
+ }
14332
+ ];
14333
+ }
14334
+ function buildExecutePostConditions(actionType, contractPrincipal, amount) {
14335
+ switch (actionType) {
14336
+ case 1:
14337
+ return buildStxWithdrawalPostConditions(contractPrincipal, amount);
14338
+ case 2:
14339
+ return buildSbtcWithdrawalPostConditions(contractPrincipal, amount);
14340
+ default:
14341
+ return [];
14342
+ }
14343
+ }
14344
+ function validatePostConditions(postConditions, expectedAmount) {
14345
+ if (postConditions.length === 0) {
14346
+ return {
14347
+ valid: false,
14348
+ error: "No post-conditions attached. Asset transfers require post-conditions for safety."
14349
+ };
14350
+ }
14351
+ const hasMatchingAmount = postConditions.some((pc) => {
14352
+ if (pc.type === "stx" || pc.type === "ft") {
14353
+ return pc.amount === expectedAmount && pc.comparison === "eq";
14354
+ }
14355
+ return false;
14356
+ });
14357
+ if (!hasMatchingAmount) {
14358
+ return {
14359
+ valid: false,
14360
+ error: `No post-condition matches expected amount ${expectedAmount}. Ensure exact-match post-conditions are attached.`
14361
+ };
14362
+ }
14363
+ return { valid: true };
14364
+ }
14365
+
13105
14366
  // src/constants/errors.ts
13106
14367
  var ERROR_RANGES = {
13107
14368
  /** Core protocol errors (paused, unauthorized, limits, etc.) */
@@ -13344,6 +14605,101 @@ function getSuggestedAction(code) {
13344
14605
  return "An error occurred. Please try again or contact support.";
13345
14606
  }
13346
14607
  }
14608
+
14609
+ // src/erc8004/contracts.ts
14610
+ var ERC8004_MAINNET_IDENTITY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";
14611
+ var ERC8004_MAINNET_REPUTATION = "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63";
14612
+ var ERC8004_TESTNET_IDENTITY = "0x8004A818BFB912233c491871b3d84c89A494BD9e";
14613
+ var ERC8004_TESTNET_REPUTATION = "0x8004B663056A597Dffe9eCcC1965A193B7388713";
14614
+ function getERC8004Addresses(testnet) {
14615
+ return {
14616
+ identityRegistry: testnet ? ERC8004_TESTNET_IDENTITY : ERC8004_MAINNET_IDENTITY,
14617
+ reputationRegistry: testnet ? ERC8004_TESTNET_REPUTATION : ERC8004_MAINNET_REPUTATION
14618
+ };
14619
+ }
14620
+ function isERC8004Chain(chainName) {
14621
+ return ERC8004_CHAINS.mainnet.includes(chainName) || ERC8004_CHAINS.testnet.includes(chainName);
14622
+ }
14623
+ var ERC8004_CHAINS = {
14624
+ mainnet: [
14625
+ "ethereum",
14626
+ "base",
14627
+ "polygon",
14628
+ "arbitrum",
14629
+ "optimism",
14630
+ "linea",
14631
+ "megaeth",
14632
+ "monad"
14633
+ ],
14634
+ testnet: [
14635
+ "ethereum-sepolia",
14636
+ "base-sepolia",
14637
+ "polygon-amoy",
14638
+ "arbitrum-sepolia",
14639
+ "optimism-sepolia",
14640
+ "monad-testnet"
14641
+ ]
14642
+ };
14643
+ var IDENTITY_REGISTRY_READ_ABI = [
14644
+ "function ownerOf(uint256 tokenId) view returns (address)",
14645
+ "function balanceOf(address owner) view returns (uint256)",
14646
+ "function totalSupply() view returns (uint256)",
14647
+ "function agentURI(uint256 agentId) view returns (string)",
14648
+ "function agentWallet(uint256 agentId) view returns (address)",
14649
+ "function getMetadata(uint256 agentId, string key) view returns (string)"
14650
+ ];
14651
+ var REPUTATION_REGISTRY_READ_ABI = [
14652
+ "function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint256 count, int128 summaryValue, uint8 summaryValueDecimals)",
14653
+ "function readFeedback(uint256 agentId, address clientAddress, uint256 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
14654
+ "function getClients(uint256 agentId) view returns (address[])",
14655
+ "function getLastIndex(uint256 agentId, address clientAddress) view returns (uint256)"
14656
+ ];
14657
+ var IDENTITY_REGISTRY_ABI = [
14658
+ // Registration
14659
+ "function register(string agentURI) returns (uint256)",
14660
+ "function register(string agentURI, tuple(string key, string value)[] metadata) returns (uint256)",
14661
+ // Read — ERC-721 standard
14662
+ "function ownerOf(uint256 tokenId) view returns (address)",
14663
+ "function balanceOf(address owner) view returns (uint256)",
14664
+ "function totalSupply() view returns (uint256)",
14665
+ // Read — ERC-8004 specific
14666
+ "function agentURI(uint256 agentId) view returns (string)",
14667
+ "function agentWallet(uint256 agentId) view returns (address)",
14668
+ "function getMetadata(uint256 agentId, string key) view returns (string)",
14669
+ // Write — URI and wallet management
14670
+ "function setAgentURI(uint256 agentId, string newURI)",
14671
+ "function setAgentWallet(uint256 agentId, address wallet, uint256 deadline, bytes signature)",
14672
+ "function unsetAgentWallet(uint256 agentId)",
14673
+ "function setMetadata(uint256 agentId, string key, string value)",
14674
+ // Events
14675
+ "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)",
14676
+ "event AgentURIUpdated(uint256 indexed agentId, string newURI)",
14677
+ "event AgentWalletSet(uint256 indexed agentId, address wallet)",
14678
+ "event AgentWalletUnset(uint256 indexed agentId)",
14679
+ "event MetadataUpdated(uint256 indexed agentId, string key, string value)"
14680
+ ];
14681
+ var REPUTATION_REGISTRY_ABI = [
14682
+ // Write
14683
+ "function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2)",
14684
+ "function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpointURI, string feedbackURI, bytes32 feedbackHash)",
14685
+ "function revokeFeedback(uint256 agentId, uint256 feedbackIndex)",
14686
+ "function appendResponse(uint256 agentId, address clientAddress, uint256 feedbackIndex, string responseURI, bytes32 responseHash)",
14687
+ // Read
14688
+ "function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint256 count, int128 summaryValue, uint8 summaryValueDecimals)",
14689
+ "function readFeedback(uint256 agentId, address clientAddress, uint256 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
14690
+ "function getClients(uint256 agentId) view returns (address[])",
14691
+ "function getLastIndex(uint256 agentId, address clientAddress) view returns (uint256)",
14692
+ // Events
14693
+ "event FeedbackGiven(uint256 indexed agentId, address indexed client, int128 value, uint8 valueDecimals)",
14694
+ "event FeedbackRevoked(uint256 indexed agentId, address indexed client, uint256 feedbackIndex)",
14695
+ "event ResponseAppended(uint256 indexed agentId, address indexed client, uint256 feedbackIndex)"
14696
+ ];
14697
+ var VALIDATION_REGISTRY_ABI = [
14698
+ "function validationRequest(address validatorAddress, uint256 agentId, string requestURI, bytes32 requestHash) returns (bytes32)",
14699
+ "function getValidationStatus(bytes32 requestHash) view returns (address validatorAddress, uint256 agentId, uint8 response, bytes32 responseHash, string tag, uint256 lastUpdate)",
14700
+ "function getSummary(uint256 agentId, address[] validatorAddresses, string tag) view returns (uint256 count, uint256 averageResponse)",
14701
+ "function getAgentValidations(uint256 agentId) view returns (bytes32[])"
14702
+ ];
13347
14703
  export {
13348
14704
  ACTION_BRIDGE,
13349
14705
  ACTION_CONFIG,
@@ -13352,6 +14708,7 @@ export {
13352
14708
  ACTION_TYPES,
13353
14709
  ARBITRUM_SEPOLIA_TOKENS,
13354
14710
  AUTH_MESSAGE_TYPES,
14711
+ AptosClient,
13355
14712
  BASE_SEPOLIA_TOKENS,
13356
14713
  BalanceManager,
13357
14714
  CHAIN_DISPLAY_INFO,
@@ -13364,35 +14721,53 @@ export {
13364
14721
  CrossOriginAuth,
13365
14722
  DEFAULT_AUTH_PORTAL_URL,
13366
14723
  DEFAULT_REFRESH_BUFFER,
14724
+ DEFAULT_RELAYER_URL,
13367
14725
  DEFAULT_SESSION_DURATION,
14726
+ ERC8004_CHAINS,
14727
+ ERC8004_MAINNET_IDENTITY,
14728
+ ERC8004_MAINNET_REPUTATION,
14729
+ ERC8004_TESTNET_IDENTITY,
14730
+ ERC8004_TESTNET_REPUTATION,
13368
14731
  ERROR_MESSAGES,
13369
14732
  ERROR_RANGES,
13370
14733
  ETHEREUM_SEPOLIA_TOKENS,
14734
+ EVMClient,
13371
14735
  EVMHubClientAdapter,
13372
14736
  EVM_ZERO_ADDRESS,
13373
14737
  GUARDIAN_CONFIG,
13374
14738
  GasSponsor,
13375
14739
  HUB_ABI,
14740
+ IDENTITY_REGISTRY_ABI,
14741
+ IDENTITY_REGISTRY_READ_ABI,
13376
14742
  IndexedDBSessionStorage,
13377
14743
  LIMIT_PRESETS,
13378
14744
  LocalStorageSessionStorage,
13379
14745
  MAINNET_CHAINS,
13380
14746
  MAX_SESSION_DURATION,
13381
14747
  MIN_SESSION_DURATION,
14748
+ MONAD_TESTNET_TOKENS,
13382
14749
  NATIVE_TOKEN_ADDRESS,
13383
14750
  OPTIMISM_SEPOLIA_TOKENS,
13384
14751
  PROTOCOL_VERSION,
13385
14752
  PasskeyManager,
13386
14753
  QueryHubStateError,
13387
14754
  QueryPortfolioError,
14755
+ REPUTATION_REGISTRY_ABI,
14756
+ REPUTATION_REGISTRY_READ_ABI,
13388
14757
  RelayerClient,
14758
+ STACKS_ACTION_TYPES,
13389
14759
  SessionError,
13390
14760
  SessionManager,
14761
+ SolanaClient,
13391
14762
  SpendingLimitsManager,
14763
+ StacksClient,
14764
+ StarknetClient,
14765
+ SuiClient,
13392
14766
  TESTNET_CHAINS,
13393
14767
  TOKEN_REGISTRY,
13394
14768
  TransactionParser,
13395
14769
  TransactionTracker,
14770
+ VALIDATION_REGISTRY_ABI,
13396
14771
  VAULT_ABI,
13397
14772
  VAULT_FACTORY_ABI,
13398
14773
  VERIDEX_ERRORS,
@@ -13410,6 +14785,10 @@ export {
13410
14785
  base64URLEncode,
13411
14786
  buildChallenge,
13412
14787
  buildGaslessChallenge,
14788
+ buildSbtcWithdrawalPostConditions,
14789
+ buildExecutePostConditions as buildStacksExecutePostConditions,
14790
+ buildStxDepositPostConditions,
14791
+ buildStxWithdrawalPostConditions,
13413
14792
  calculatePercentage,
13414
14793
  computeKeyHash,
13415
14794
  computeSessionKeyHash,
@@ -13470,11 +14849,16 @@ export {
13470
14849
  getChainPreset,
13471
14850
  getConfigTypeName,
13472
14851
  getDefaultHub,
14852
+ getERC8004Addresses,
13473
14853
  getErrorCategory,
13474
14854
  getErrorMessage,
13475
14855
  getExplorerUrl,
13476
14856
  getHubChains,
13477
14857
  getSequenceFromTxReceipt,
14858
+ getContractPrincipal as getStacksContractPrincipal,
14859
+ getStacksExplorerAddressUrl,
14860
+ getStacksExplorerTxUrl,
14861
+ getNetworkFromAddress as getStacksNetworkFromAddress,
13478
14862
  getSuggestedAction,
13479
14863
  getSupportedChainIds,
13480
14864
  getSupportedChains,
@@ -13490,20 +14874,26 @@ export {
13490
14874
  isAbiError,
13491
14875
  isChainSupported,
13492
14876
  isCoreError,
14877
+ isERC8004Chain,
13493
14878
  isEvmChain,
13494
14879
  isNativeToken,
13495
14880
  isQueryError,
13496
14881
  isQueryExecutionError,
13497
14882
  isQueryParsingError,
13498
14883
  isRetryableError,
14884
+ isContractPrincipal as isStacksContractPrincipal,
13499
14885
  isValidBytes32,
13500
14886
  isValidEvmAddress,
14887
+ isValidContractName as isValidStacksContractName,
14888
+ isValidStacksPrincipal,
14889
+ isValidStandardPrincipal as isValidStacksStandardPrincipal,
13501
14890
  isValidWormholeChainId,
13502
14891
  logTransactionSummary,
13503
14892
  normalizeEmitterAddress,
13504
14893
  padTo32Bytes,
13505
14894
  parseAmount,
13506
14895
  parseDERSignature,
14896
+ parseContractPrincipal as parseStacksContractPrincipal,
13507
14897
  parseVAA,
13508
14898
  parseVAABytes,
13509
14899
  parseVeridexError,
@@ -13515,10 +14905,22 @@ export {
13515
14905
  sendAuthResponse,
13516
14906
  signWithSessionKey,
13517
14907
  solanaAddressToBytes32,
14908
+ buildExecuteHash as stacksBuildExecuteHash,
14909
+ buildRegistrationHash as stacksBuildRegistrationHash,
14910
+ buildRevocationHash as stacksBuildRevocationHash,
14911
+ buildSessionRegistrationHash as stacksBuildSessionRegistrationHash,
14912
+ buildWithdrawalHash as stacksBuildWithdrawalHash,
14913
+ compressPublicKey as stacksCompressPublicKey,
14914
+ computeKeyHash2 as stacksComputeKeyHash,
14915
+ computeKeyHashFromCoords as stacksComputeKeyHashFromCoords,
14916
+ derToCompactSignature as stacksDerToCompactSignature,
14917
+ rsToCompactSignature as stacksRsToCompactSignature,
14918
+ supportsRelatedOrigins,
13518
14919
  supportsRelayer,
13519
14920
  trimTo20Bytes,
13520
14921
  validateEmitter,
13521
14922
  validateSessionConfig,
14923
+ validatePostConditions as validateStacksPostConditions,
13522
14924
  verifySessionSignature,
13523
14925
  waitForGuardianSignatures
13524
14926
  };