@sip-protocol/sdk 0.6.0 → 0.6.1

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 (61) hide show
  1. package/README.md +58 -0
  2. package/dist/browser.d.mts +4 -4
  3. package/dist/browser.d.ts +4 -4
  4. package/dist/browser.js +2745 -457
  5. package/dist/browser.mjs +31 -1
  6. package/dist/chunk-7QZPORY5.mjs +15604 -0
  7. package/dist/chunk-C2NPCUAJ.mjs +17010 -0
  8. package/dist/chunk-FCVLFUIC.mjs +16699 -0
  9. package/dist/chunk-G5UHXECN.mjs +16340 -0
  10. package/dist/chunk-GEDEIZHJ.mjs +16798 -0
  11. package/dist/chunk-MTNYSNR7.mjs +16269 -0
  12. package/dist/chunk-O5PIB2EA.mjs +16698 -0
  13. package/dist/chunk-PCFM7FQO.mjs +17010 -0
  14. package/dist/chunk-QK464ARC.mjs +16946 -0
  15. package/dist/chunk-VNBMNGC3.mjs +16698 -0
  16. package/dist/chunk-W5TUELDQ.mjs +16947 -0
  17. package/dist/index-CD_zShu-.d.ts +10870 -0
  18. package/dist/index-CQBYdLYy.d.mts +10976 -0
  19. package/dist/index-Cg9TYEPv.d.mts +11321 -0
  20. package/dist/index-CqZJOO8C.d.mts +11323 -0
  21. package/dist/index-CywN9Bnp.d.ts +11321 -0
  22. package/dist/index-DHy5ZjCD.d.ts +10976 -0
  23. package/dist/index-DfsVsmxu.d.ts +11323 -0
  24. package/dist/index-ObjwyVDX.d.mts +10870 -0
  25. package/dist/index-m0xbSfmT.d.mts +11318 -0
  26. package/dist/index-rWLEgvhN.d.ts +11318 -0
  27. package/dist/index.d.mts +3 -3
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +2730 -436
  30. package/dist/index.mjs +31 -1
  31. package/dist/noir-DKfEzWy9.d.mts +482 -0
  32. package/dist/noir-DKfEzWy9.d.ts +482 -0
  33. package/dist/proofs/noir.d.mts +1 -1
  34. package/dist/proofs/noir.d.ts +1 -1
  35. package/dist/proofs/noir.js +12 -3
  36. package/dist/proofs/noir.mjs +12 -3
  37. package/package.json +16 -14
  38. package/src/adapters/near-intents.ts +13 -3
  39. package/src/auction/index.ts +20 -0
  40. package/src/auction/sealed-bid.ts +1037 -0
  41. package/src/compliance/derivation.ts +13 -3
  42. package/src/compliance/reports.ts +5 -4
  43. package/src/cosmos/ibc-stealth.ts +2 -2
  44. package/src/cosmos/stealth.ts +2 -2
  45. package/src/governance/index.ts +19 -0
  46. package/src/governance/private-vote.ts +1116 -0
  47. package/src/index.ts +50 -2
  48. package/src/intent.ts +145 -8
  49. package/src/nft/index.ts +27 -0
  50. package/src/nft/private-nft.ts +811 -0
  51. package/src/proofs/browser-utils.ts +1 -7
  52. package/src/proofs/noir.ts +34 -7
  53. package/src/settlement/backends/direct-chain.ts +14 -3
  54. package/src/types/browser.d.ts +67 -0
  55. package/src/validation.ts +4 -2
  56. package/src/wallet/bitcoin/adapter.ts +159 -15
  57. package/src/wallet/bitcoin/types.ts +340 -15
  58. package/src/wallet/cosmos/mock.ts +16 -12
  59. package/src/wallet/hardware/ledger.ts +82 -12
  60. package/src/wallet/hardware/types.ts +2 -0
  61. package/LICENSE +0 -21
package/dist/browser.js CHANGED
@@ -3232,6 +3232,8 @@ __export(browser_exports, {
3232
3232
  PaymentBuilder: () => PaymentBuilder,
3233
3233
  PaymentStatus: () => import_types56.PaymentStatus,
3234
3234
  PrivacyLevel: () => import_types55.PrivacyLevel,
3235
+ PrivateNFT: () => PrivateNFT,
3236
+ PrivateVoting: () => PrivateVoting,
3235
3237
  ProofError: () => ProofError,
3236
3238
  ProofGenerationError: () => ProofGenerationError,
3237
3239
  ProofNotImplementedError: () => ProofNotImplementedError,
@@ -3244,10 +3246,12 @@ __export(browser_exports, {
3244
3246
  STABLECOIN_ADDRESSES: () => STABLECOIN_ADDRESSES,
3245
3247
  STABLECOIN_DECIMALS: () => STABLECOIN_DECIMALS,
3246
3248
  STABLECOIN_INFO: () => STABLECOIN_INFO,
3249
+ SealedBidAuction: () => SealedBidAuction,
3247
3250
  SettlementRegistry: () => SettlementRegistry,
3248
3251
  SettlementRegistryError: () => SettlementRegistryError,
3249
3252
  SmartRouter: () => SmartRouter,
3250
3253
  SolanaWalletAdapter: () => SolanaWalletAdapter,
3254
+ SuiStealthService: () => SuiStealthService,
3251
3255
  SwapStatus: () => SwapStatus,
3252
3256
  ThresholdViewingKey: () => ThresholdViewingKey,
3253
3257
  Treasury: () => Treasury,
@@ -3273,6 +3277,7 @@ __export(browser_exports, {
3273
3277
  checkEd25519StealthAddress: () => checkEd25519StealthAddress,
3274
3278
  checkMobileWASMCompatibility: () => checkMobileWASMCompatibility,
3275
3279
  checkStealthAddress: () => checkStealthAddress,
3280
+ checkSuiStealthAddress: () => checkSuiStealthAddress,
3276
3281
  commit: () => commit,
3277
3282
  commitZero: () => commitZero,
3278
3283
  computeAttestationHash: () => computeAttestationHash,
@@ -3292,8 +3297,11 @@ __export(browser_exports, {
3292
3297
  createNEARIntentsAdapter: () => createNEARIntentsAdapter,
3293
3298
  createNEARIntentsBackend: () => createNEARIntentsBackend,
3294
3299
  createOracleRegistry: () => createOracleRegistry,
3300
+ createPrivateOwnership: () => createPrivateOwnership,
3301
+ createPrivateVoting: () => createPrivateVoting,
3295
3302
  createProductionSIP: () => createProductionSIP,
3296
3303
  createSIP: () => createSIP,
3304
+ createSealedBidAuction: () => createSealedBidAuction,
3297
3305
  createShieldedIntent: () => createShieldedIntent,
3298
3306
  createShieldedPayment: () => createShieldedPayment,
3299
3307
  createSmartRouter: () => createSmartRouter,
@@ -3314,6 +3322,7 @@ __export(browser_exports, {
3314
3322
  deriveEd25519StealthPrivateKey: () => deriveEd25519StealthPrivateKey,
3315
3323
  deriveOracleId: () => deriveOracleId,
3316
3324
  deriveStealthPrivateKey: () => deriveStealthPrivateKey,
3325
+ deriveSuiStealthPrivateKey: () => deriveSuiStealthPrivateKey,
3317
3326
  deriveViewingKey: () => deriveViewingKey,
3318
3327
  deserializeAttestationMessage: () => deserializeAttestationMessage,
3319
3328
  deserializeIntent: () => deserializeIntent,
@@ -3325,6 +3334,7 @@ __export(browser_exports, {
3325
3334
  ed25519PublicKeyToAptosAddress: () => ed25519PublicKeyToAptosAddress,
3326
3335
  ed25519PublicKeyToNearAddress: () => ed25519PublicKeyToNearAddress,
3327
3336
  ed25519PublicKeyToSolanaAddress: () => ed25519PublicKeyToSolanaAddress,
3337
+ ed25519PublicKeyToSuiAddress: () => ed25519PublicKeyToSuiAddress,
3328
3338
  encodeStealthMetaAddress: () => encodeStealthMetaAddress,
3329
3339
  encryptForViewing: () => encryptForViewing,
3330
3340
  featureNotSupportedError: () => featureNotSupportedError,
@@ -3342,6 +3352,7 @@ __export(browser_exports, {
3342
3352
  generateRandomBytes: () => generateRandomBytes,
3343
3353
  generateStealthAddress: () => generateStealthAddress,
3344
3354
  generateStealthMetaAddress: () => generateStealthMetaAddress,
3355
+ generateSuiStealthAddress: () => generateSuiStealthAddress,
3345
3356
  generateViewingKey: () => generateViewingKey,
3346
3357
  getActiveOracles: () => getActiveOracles,
3347
3358
  getAvailableTransports: () => getAvailableTransports,
@@ -3401,10 +3412,13 @@ __export(browser_exports, {
3401
3412
  isValidSlippage: () => isValidSlippage,
3402
3413
  isValidSolanaAddress: () => isValidSolanaAddress,
3403
3414
  isValidStealthMetaAddress: () => isValidStealthMetaAddress,
3415
+ isValidSuiAddress: () => isValidSuiAddress,
3404
3416
  isValidTaprootAddress: () => isValidTaprootAddress,
3405
3417
  nearAddressToEd25519PublicKey: () => nearAddressToEd25519PublicKey,
3406
3418
  normalizeAddress: () => normalizeAddress,
3419
+ normalizeSuiAddress: () => normalizeSuiAddress,
3407
3420
  notConnectedError: () => notConnectedError,
3421
+ proveOwnership: () => proveOwnership,
3408
3422
  publicKeyToEthAddress: () => publicKeyToEthAddress,
3409
3423
  registerWallet: () => registerWallet,
3410
3424
  removeOracle: () => removeOracle,
@@ -3448,6 +3462,7 @@ __export(browser_exports, {
3448
3462
  verifyCommitment: () => verifyCommitment,
3449
3463
  verifyOpening: () => verifyOpening,
3450
3464
  verifyOracleSignature: () => verifyOracleSignature,
3465
+ verifyOwnership: () => verifyOwnership,
3451
3466
  walletRegistry: () => walletRegistry,
3452
3467
  withSecureBuffer: () => withSecureBuffer,
3453
3468
  withSecureBufferSync: () => withSecureBufferSync,
@@ -3720,7 +3735,7 @@ function isNonNegativeAmount(value) {
3720
3735
  function isValidSlippage(value) {
3721
3736
  return typeof value === "number" && !isNaN(value) && value >= 0 && value < 1;
3722
3737
  }
3723
- var STEALTH_META_ADDRESS_REGEX = /^sip:[a-z]+:0x[0-9a-fA-F]{66}:0x[0-9a-fA-F]{66}$/;
3738
+ var STEALTH_META_ADDRESS_REGEX = /^sip:[a-z]+:0x[0-9a-fA-F]{64,66}:0x[0-9a-fA-F]{64,66}$/;
3724
3739
  function isValidStealthMetaAddress(addr) {
3725
3740
  if (typeof addr !== "string") return false;
3726
3741
  return STEALTH_META_ADDRESS_REGEX.test(addr);
@@ -5119,6 +5134,10 @@ var IntentBuilder = class {
5119
5134
  params = {};
5120
5135
  senderAddress;
5121
5136
  proofProvider;
5137
+ ownershipSignature;
5138
+ senderSecret;
5139
+ authorizationSignature;
5140
+ allowPlaceholders;
5122
5141
  /**
5123
5142
  * Set the input for the intent
5124
5143
  *
@@ -5270,6 +5289,49 @@ var IntentBuilder = class {
5270
5289
  this.proofProvider = provider;
5271
5290
  return this;
5272
5291
  }
5292
+ /**
5293
+ * Set the signatures and secret for proof generation
5294
+ *
5295
+ * Required for production proof generation. Provides the cryptographic
5296
+ * materials needed to generate valid ZK proofs.
5297
+ *
5298
+ * @param signatures - Object containing ownership signature, sender secret, and authorization signature
5299
+ * @returns this for chaining
5300
+ *
5301
+ * @example
5302
+ * ```typescript
5303
+ * const intent = await builder
5304
+ * .input('near', 'NEAR', 100n)
5305
+ * .output('zcash', 'ZEC', 95n)
5306
+ * .privacy(PrivacyLevel.SHIELDED)
5307
+ * .withProvider(noirProvider)
5308
+ * .withSignatures({
5309
+ * ownershipSignature: await wallet.signMessage(address),
5310
+ * senderSecret: wallet.privateKey,
5311
+ * authorizationSignature: await wallet.signMessage(intentHash),
5312
+ * })
5313
+ * .build()
5314
+ * ```
5315
+ */
5316
+ withSignatures(signatures) {
5317
+ this.ownershipSignature = signatures.ownershipSignature;
5318
+ this.senderSecret = signatures.senderSecret;
5319
+ this.authorizationSignature = signatures.authorizationSignature;
5320
+ return this;
5321
+ }
5322
+ /**
5323
+ * Allow placeholder signatures for development/testing
5324
+ *
5325
+ * **WARNING**: Never use this in production! Proofs with placeholders
5326
+ * are not cryptographically valid.
5327
+ *
5328
+ * @param allow - Whether to allow placeholders (default: true)
5329
+ * @returns this for chaining
5330
+ */
5331
+ withPlaceholders(allow = true) {
5332
+ this.allowPlaceholders = allow;
5333
+ return this;
5334
+ }
5273
5335
  /**
5274
5336
  * Build the shielded intent
5275
5337
  *
@@ -5281,14 +5343,25 @@ var IntentBuilder = class {
5281
5343
  async build() {
5282
5344
  return createShieldedIntent(this.params, {
5283
5345
  senderAddress: this.senderAddress,
5284
- proofProvider: this.proofProvider
5346
+ proofProvider: this.proofProvider,
5347
+ ownershipSignature: this.ownershipSignature,
5348
+ senderSecret: this.senderSecret,
5349
+ authorizationSignature: this.authorizationSignature,
5350
+ allowPlaceholders: this.allowPlaceholders
5285
5351
  });
5286
5352
  }
5287
5353
  };
5288
5354
  async function createShieldedIntent(params, options) {
5289
5355
  validateCreateIntentParams(params);
5290
5356
  const { input, output, privacy, recipientMetaAddress, viewingKey, ttl = 300 } = params;
5291
- const { senderAddress, proofProvider } = options ?? {};
5357
+ const {
5358
+ senderAddress,
5359
+ proofProvider,
5360
+ ownershipSignature,
5361
+ senderSecret,
5362
+ authorizationSignature,
5363
+ allowPlaceholders = false
5364
+ } = options ?? {};
5292
5365
  let viewingKeyHash;
5293
5366
  if (viewingKey) {
5294
5367
  const keyHex = viewingKey.startsWith("0x") ? viewingKey.slice(2) : viewingKey;
@@ -5321,28 +5394,48 @@ async function createShieldedIntent(params, options) {
5321
5394
  let validityProof;
5322
5395
  const requiresProofs = privacy !== import_types.PrivacyLevel.TRANSPARENT;
5323
5396
  if (requiresProofs && proofProvider && proofProvider.isReady) {
5397
+ const hasSignatures = ownershipSignature && senderSecret && authorizationSignature;
5398
+ const usingPlaceholders = !hasSignatures;
5399
+ if (usingPlaceholders && !allowPlaceholders) {
5400
+ throw new ValidationError(
5401
+ "Proof generation requires signatures. Provide ownershipSignature, senderSecret, and authorizationSignature in options, or set allowPlaceholders: true for development/testing.",
5402
+ "options",
5403
+ {
5404
+ missing: [
5405
+ !ownershipSignature && "ownershipSignature",
5406
+ !senderSecret && "senderSecret",
5407
+ !authorizationSignature && "authorizationSignature"
5408
+ ].filter(Boolean)
5409
+ }
5410
+ );
5411
+ }
5412
+ if (usingPlaceholders) {
5413
+ console.warn(
5414
+ "[createShieldedIntent] WARNING: Using placeholder signatures for proof generation. These proofs are NOT cryptographically valid. Do NOT use in production!"
5415
+ );
5416
+ }
5324
5417
  const hexToUint8 = (hex) => {
5325
5418
  const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
5326
5419
  return (0, import_utils6.hexToBytes)(cleanHex);
5327
5420
  };
5421
+ const effectiveOwnershipSig = ownershipSignature ?? new Uint8Array(64);
5422
+ const effectiveSenderSecret = senderSecret ?? new Uint8Array(32);
5423
+ const effectiveAuthSig = authorizationSignature ?? new Uint8Array(64);
5328
5424
  const fundingResult = await proofProvider.generateFundingProof({
5329
5425
  balance: input.amount,
5330
5426
  minimumRequired: output.minAmount,
5331
5427
  blindingFactor: hexToUint8(inputCommitment.blindingFactor),
5332
5428
  assetId: input.asset.symbol,
5333
5429
  userAddress: senderAddress ?? "0x0",
5334
- ownershipSignature: new Uint8Array(64)
5335
- // Placeholder - would come from wallet
5430
+ ownershipSignature: effectiveOwnershipSig
5336
5431
  });
5337
5432
  fundingProof = fundingResult.proof;
5338
5433
  const validityResult = await proofProvider.generateValidityProof({
5339
5434
  intentHash: hash(intentId),
5340
5435
  senderAddress: senderAddress ?? "0x0",
5341
5436
  senderBlinding: hexToUint8(senderCommitment.blindingFactor),
5342
- senderSecret: new Uint8Array(32),
5343
- // Placeholder - would come from wallet
5344
- authorizationSignature: new Uint8Array(64),
5345
- // Placeholder - would come from wallet
5437
+ senderSecret: effectiveSenderSecret,
5438
+ authorizationSignature: effectiveAuthSig,
5346
5439
  nonce: new Uint8Array(32),
5347
5440
  // Could use randomBytes here
5348
5441
  timestamp: now,
@@ -5701,131 +5794,403 @@ var OneClickClient = class {
5701
5794
 
5702
5795
  // src/adapters/near-intents.ts
5703
5796
  var import_types3 = require("@sip-protocol/types");
5704
- var DEFAULT_ASSET_MAPPINGS = {
5705
- // NEAR assets
5706
- "near:NEAR": "nep141:wrap.near",
5707
- "near:wNEAR": "nep141:wrap.near",
5708
- "near:USDC": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
5709
- // Ethereum assets (via OMFT bridge)
5710
- "ethereum:ETH": "nep141:eth.omft.near",
5711
- "ethereum:USDC": "nep141:eth-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.omft.near",
5712
- "ethereum:USDT": "nep141:eth-0xdac17f958d2ee523a2206206994597c13d831ec7.omft.near",
5713
- // Solana assets (via OMFT bridge)
5714
- "solana:SOL": "nep141:sol.omft.near",
5715
- "solana:USDC": "nep141:sol-5ce3bf3a31af18be40ba30f721101b4341690186.omft.near",
5716
- "solana:USDT": "nep141:sol-c800a4bd850783ccb82c2b2c7e84175443606352.omft.near",
5717
- // Zcash assets
5718
- "zcash:ZEC": "nep141:zec.omft.near",
5719
- // Arbitrum assets
5720
- "arbitrum:ETH": "nep141:arb.omft.near",
5721
- "arbitrum:ARB": "nep141:arb-0x912ce59144191c1204e64559fe8253a0e49e6548.omft.near",
5722
- "arbitrum:USDC": "nep141:arb-0xaf88d065e77c8cc2239327c5edb3a432268e5831.omft.near",
5723
- // Base assets
5724
- "base:ETH": "nep141:base.omft.near",
5725
- "base:USDC": "nep141:base-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.omft.near",
5726
- // Optimism assets (via HOT bridge - uses nep245)
5727
- "optimism:ETH": "nep245:v2_1.omni.hot.tg:10_11111111111111111111",
5728
- "optimism:OP": "nep245:v2_1.omni.hot.tg:10_vLAiSt9KfUGKpw5cD3vsSyNYBo7",
5729
- "optimism:USDC": "nep245:v2_1.omni.hot.tg:10_A2ewyUyDp6qsue1jqZsGypkCxRJ",
5730
- // Polygon assets (via HOT bridge - uses nep245)
5731
- "polygon:POL": "nep245:v2_1.omni.hot.tg:137_11111111111111111111",
5732
- "polygon:MATIC": "nep245:v2_1.omni.hot.tg:137_11111111111111111111",
5733
- // POL is the rebranded MATIC
5734
- "polygon:USDC": "nep245:v2_1.omni.hot.tg:137_qiStmoQJDQPTebaPjgx5VBxZv6L",
5735
- // BNB Chain assets (via HOT bridge - uses nep245)
5736
- "bsc:BNB": "nep245:v2_1.omni.hot.tg:56_11111111111111111111",
5737
- "bsc:USDC": "nep245:v2_1.omni.hot.tg:56_2w93GqMcEmQFDru84j3HZZWt557r",
5738
- // Avalanche assets (via HOT bridge - uses nep245)
5739
- "avalanche:AVAX": "nep245:v2_1.omni.hot.tg:43114_11111111111111111111",
5740
- "avalanche:USDC": "nep245:v2_1.omni.hot.tg:43114_3atVJH3r5c4GqiSYmg9fECvjc47o",
5741
- // Bitcoin
5742
- "bitcoin:BTC": "nep141:btc.omft.near",
5743
- // Aptos
5744
- "aptos:APT": "nep141:aptos.omft.near"
5745
- };
5746
- var CHAIN_BLOCKCHAIN_MAP = {
5747
- near: "near",
5748
- ethereum: "evm",
5749
- solana: "solana",
5750
- zcash: "zcash",
5751
- polygon: "evm",
5752
- arbitrum: "evm",
5753
- optimism: "evm",
5754
- base: "evm",
5755
- bitcoin: "bitcoin",
5756
- aptos: "aptos",
5757
- sui: "sui",
5758
- cosmos: "cosmos",
5759
- osmosis: "cosmos",
5760
- injective: "cosmos",
5761
- celestia: "cosmos",
5762
- sei: "cosmos",
5763
- dydx: "cosmos"
5764
- };
5765
- var NEARIntentsAdapter = class {
5766
- client;
5767
- defaultSlippage;
5768
- defaultDeadlineOffset;
5769
- assetMappings;
5770
- constructor(config = {}) {
5771
- this.client = config.client ?? new OneClickClient({
5772
- baseUrl: config.baseUrl,
5773
- jwtToken: config.jwtToken
5774
- });
5775
- this.defaultSlippage = config.defaultSlippage ?? 100;
5776
- this.defaultDeadlineOffset = config.defaultDeadlineOffset ?? 3600;
5777
- this.assetMappings = {
5778
- ...DEFAULT_ASSET_MAPPINGS,
5779
- ...config.assetMappings
5780
- };
5797
+
5798
+ // src/move/aptos.ts
5799
+ var import_sha32 = require("@noble/hashes/sha3");
5800
+ var import_utils7 = require("@noble/hashes/utils");
5801
+ var APTOS_SINGLE_ED25519_SCHEME = 0;
5802
+ function ed25519PublicKeyToAptosAddress(publicKey) {
5803
+ if (!isValidHex(publicKey)) {
5804
+ throw new ValidationError(
5805
+ "publicKey must be a valid hex string with 0x prefix",
5806
+ "publicKey"
5807
+ );
5808
+ }
5809
+ if (!isValidEd25519PublicKey(publicKey)) {
5810
+ throw new ValidationError(
5811
+ "publicKey must be 32 bytes (64 hex characters)",
5812
+ "publicKey"
5813
+ );
5814
+ }
5815
+ const publicKeyBytes = (0, import_utils7.hexToBytes)(publicKey.slice(2));
5816
+ const authKeyInput = new Uint8Array(publicKeyBytes.length + 1);
5817
+ authKeyInput.set(publicKeyBytes, 0);
5818
+ authKeyInput[publicKeyBytes.length] = APTOS_SINGLE_ED25519_SCHEME;
5819
+ const addressHash = (0, import_sha32.sha3_256)(authKeyInput);
5820
+ return `0x${(0, import_utils7.bytesToHex)(addressHash)}`;
5821
+ }
5822
+ function isValidAptosAddress(address) {
5823
+ if (typeof address !== "string" || address.length === 0) {
5824
+ return false;
5825
+ }
5826
+ if (!address.startsWith("0x")) {
5827
+ return false;
5828
+ }
5829
+ const hexPart = address.slice(2);
5830
+ if (hexPart.length !== 64) {
5831
+ return false;
5832
+ }
5833
+ return /^[0-9a-fA-F]{64}$/.test(hexPart);
5834
+ }
5835
+ function aptosAddressToAuthKey(address) {
5836
+ if (!isValidAptosAddress(address)) {
5837
+ throw new ValidationError(
5838
+ "Invalid Aptos address format (must be 0x-prefixed 64 hex characters)",
5839
+ "address"
5840
+ );
5841
+ }
5842
+ return address.toLowerCase();
5843
+ }
5844
+ function generateAptosStealthAddress(recipientMetaAddress) {
5845
+ if (recipientMetaAddress.chain !== "aptos") {
5846
+ throw new ValidationError(
5847
+ `Expected chain 'aptos', got '${recipientMetaAddress.chain}'`,
5848
+ "recipientMetaAddress.chain"
5849
+ );
5781
5850
  }
5851
+ const { stealthAddress, sharedSecret } = generateEd25519StealthAddress(recipientMetaAddress);
5852
+ const aptosAddress = ed25519PublicKeyToAptosAddress(stealthAddress.address);
5853
+ return {
5854
+ stealthAddress: aptosAddress,
5855
+ stealthPublicKey: stealthAddress.address,
5856
+ ephemeralPublicKey: stealthAddress.ephemeralPublicKey,
5857
+ viewTag: stealthAddress.viewTag,
5858
+ sharedSecret
5859
+ };
5860
+ }
5861
+ function deriveAptosStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
5862
+ const recovery = deriveEd25519StealthPrivateKey(
5863
+ stealthAddress,
5864
+ spendingPrivateKey,
5865
+ viewingPrivateKey
5866
+ );
5867
+ const aptosAddress = ed25519PublicKeyToAptosAddress(recovery.stealthAddress);
5868
+ return {
5869
+ ...recovery,
5870
+ aptosAddress
5871
+ };
5872
+ }
5873
+ function checkAptosStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
5874
+ return checkEd25519StealthAddress(
5875
+ stealthAddress,
5876
+ spendingPrivateKey,
5877
+ viewingPrivateKey
5878
+ );
5879
+ }
5880
+ var AptosStealthService = class {
5782
5881
  /**
5783
- * Get the underlying OneClick client
5882
+ * Generate a stealth address for an Aptos recipient
5883
+ *
5884
+ * @param recipientMetaAddress - Recipient's stealth meta-address
5885
+ * @returns Complete stealth address result
5784
5886
  */
5785
- getClient() {
5786
- return this.client;
5887
+ generateStealthAddress(recipientMetaAddress) {
5888
+ return generateAptosStealthAddress(recipientMetaAddress);
5787
5889
  }
5788
5890
  /**
5789
- * Prepare a swap request
5891
+ * Convert an ed25519 public key to Aptos address format
5790
5892
  *
5791
- * For shielded/compliant modes, generates a stealth address for the recipient.
5893
+ * @param publicKey - 32-byte ed25519 public key
5894
+ * @returns Aptos address string
5895
+ */
5896
+ stealthKeyToAptosAddress(publicKey) {
5897
+ return ed25519PublicKeyToAptosAddress(publicKey);
5898
+ }
5899
+ /**
5900
+ * Derive the private key for a stealth address
5792
5901
  *
5793
- * @param request - Swap request parameters
5794
- * @param recipientMetaAddress - Recipient's stealth meta-address (for privacy modes)
5795
- * @param senderAddress - Sender's address for refunds
5796
- * @returns Prepared swap with quote request
5902
+ * @param stealthAddress - Stealth address data
5903
+ * @param spendingPrivateKey - Recipient's spending private key
5904
+ * @param viewingPrivateKey - Recipient's viewing private key
5905
+ * @returns Recovery data with derived private key
5797
5906
  */
5798
- async prepareSwap(request, recipientMetaAddress, senderAddress) {
5799
- this.validateRequest(request);
5800
- const inputChain = request.inputAsset.chain;
5801
- if (senderAddress) {
5802
- if (!isAddressValidForChain(senderAddress, inputChain)) {
5803
- const inputChainType = getChainAddressType(inputChain);
5804
- const senderFormat = senderAddress.startsWith("0x") ? "EVM" : /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(senderAddress) ? "Solana" : /^[0-9a-f]{64}$/.test(senderAddress) || /^[a-z0-9._-]+$/.test(senderAddress) ? "NEAR" : "unknown";
5805
- throw new ValidationError(
5806
- `Wallet address format doesn't match input chain. You're swapping FROM ${inputChain} (${inputChainType} format) but your connected wallet uses ${senderFormat} format. Please connect a wallet that matches the source chain (${inputChain}).`,
5807
- "senderAddress",
5808
- {
5809
- inputChain,
5810
- expectedFormat: inputChainType,
5811
- receivedFormat: senderFormat,
5812
- hint: `For ${inputChain} swaps, connect a ${inputChainType === "evm" ? "MetaMask or EVM" : inputChainType} wallet.`
5813
- }
5814
- );
5815
- }
5816
- }
5817
- let recipientAddress;
5818
- let refundAddress = senderAddress;
5819
- let stealthData;
5820
- let sharedSecret;
5821
- let curve;
5822
- let nativeRecipientAddress;
5823
- if (request.privacyLevel !== import_types3.PrivacyLevel.TRANSPARENT) {
5824
- if (!recipientMetaAddress) {
5825
- throw new ValidationError(
5826
- "recipientMetaAddress is required for shielded/compliant privacy modes",
5827
- "recipientMetaAddress"
5828
- );
5907
+ deriveStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
5908
+ return deriveAptosStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey);
5909
+ }
5910
+ /**
5911
+ * Check if a stealth address belongs to this recipient
5912
+ *
5913
+ * @param stealthAddress - Stealth address to check
5914
+ * @param spendingPrivateKey - Recipient's spending private key
5915
+ * @param viewingPrivateKey - Recipient's viewing private key
5916
+ * @returns true if the address belongs to this recipient
5917
+ */
5918
+ checkStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
5919
+ return checkAptosStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey);
5920
+ }
5921
+ /**
5922
+ * Validate an Aptos address format
5923
+ *
5924
+ * @param address - Address to validate
5925
+ * @returns true if valid format
5926
+ */
5927
+ isValidAddress(address) {
5928
+ return isValidAptosAddress(address);
5929
+ }
5930
+ };
5931
+
5932
+ // src/move/sui.ts
5933
+ var import_blake2b = require("@noble/hashes/blake2b");
5934
+ var import_utils8 = require("@noble/hashes/utils");
5935
+ var SUI_ED25519_SCHEME = 0;
5936
+ function ed25519PublicKeyToSuiAddress(publicKey) {
5937
+ if (!isValidHex(publicKey)) {
5938
+ throw new ValidationError(
5939
+ "publicKey must be a valid hex string with 0x prefix",
5940
+ "publicKey"
5941
+ );
5942
+ }
5943
+ if (!isValidEd25519PublicKey(publicKey)) {
5944
+ throw new ValidationError(
5945
+ "publicKey must be 32 bytes (64 hex characters)",
5946
+ "publicKey"
5947
+ );
5948
+ }
5949
+ const publicKeyBytes = (0, import_utils8.hexToBytes)(publicKey.slice(2));
5950
+ const addressInput = new Uint8Array(publicKeyBytes.length + 1);
5951
+ addressInput[0] = SUI_ED25519_SCHEME;
5952
+ addressInput.set(publicKeyBytes, 1);
5953
+ const hasher = new import_blake2b.BLAKE2b({ dkLen: 32 });
5954
+ hasher.update(addressInput);
5955
+ const addressHash = hasher.digest();
5956
+ return `0x${(0, import_utils8.bytesToHex)(addressHash)}`;
5957
+ }
5958
+ function isValidSuiAddress(address) {
5959
+ if (typeof address !== "string" || address.length === 0) {
5960
+ return false;
5961
+ }
5962
+ if (!address.startsWith("0x")) {
5963
+ return false;
5964
+ }
5965
+ const hexPart = address.slice(2);
5966
+ if (hexPart.length !== 64) {
5967
+ return false;
5968
+ }
5969
+ return /^[0-9a-fA-F]{64}$/.test(hexPart);
5970
+ }
5971
+ function normalizeSuiAddress(address) {
5972
+ if (!isValidSuiAddress(address)) {
5973
+ throw new ValidationError(
5974
+ "Invalid Sui address format (must be 0x-prefixed 64 hex characters)",
5975
+ "address"
5976
+ );
5977
+ }
5978
+ return address.toLowerCase();
5979
+ }
5980
+ function generateSuiStealthAddress(recipientMetaAddress) {
5981
+ if (recipientMetaAddress.chain !== "sui") {
5982
+ throw new ValidationError(
5983
+ `Expected chain 'sui', got '${recipientMetaAddress.chain}'`,
5984
+ "recipientMetaAddress.chain"
5985
+ );
5986
+ }
5987
+ const { stealthAddress, sharedSecret } = generateEd25519StealthAddress(recipientMetaAddress);
5988
+ const suiAddress = ed25519PublicKeyToSuiAddress(stealthAddress.address);
5989
+ return {
5990
+ stealthAddress: suiAddress,
5991
+ stealthPublicKey: stealthAddress.address,
5992
+ ephemeralPublicKey: stealthAddress.ephemeralPublicKey,
5993
+ viewTag: stealthAddress.viewTag,
5994
+ sharedSecret
5995
+ };
5996
+ }
5997
+ function deriveSuiStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
5998
+ const recovery = deriveEd25519StealthPrivateKey(
5999
+ stealthAddress,
6000
+ spendingPrivateKey,
6001
+ viewingPrivateKey
6002
+ );
6003
+ const suiAddress = ed25519PublicKeyToSuiAddress(recovery.stealthAddress);
6004
+ return {
6005
+ ...recovery,
6006
+ suiAddress
6007
+ };
6008
+ }
6009
+ function checkSuiStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
6010
+ return checkEd25519StealthAddress(
6011
+ stealthAddress,
6012
+ spendingPrivateKey,
6013
+ viewingPrivateKey
6014
+ );
6015
+ }
6016
+ var SuiStealthService = class {
6017
+ /**
6018
+ * Generate a stealth address for a Sui recipient
6019
+ *
6020
+ * @param recipientMetaAddress - Recipient's stealth meta-address
6021
+ * @returns Complete stealth address result
6022
+ */
6023
+ generateStealthAddress(recipientMetaAddress) {
6024
+ return generateSuiStealthAddress(recipientMetaAddress);
6025
+ }
6026
+ /**
6027
+ * Convert an ed25519 public key to Sui address format
6028
+ *
6029
+ * @param publicKey - 32-byte ed25519 public key
6030
+ * @returns Sui address string
6031
+ */
6032
+ stealthKeyToSuiAddress(publicKey) {
6033
+ return ed25519PublicKeyToSuiAddress(publicKey);
6034
+ }
6035
+ /**
6036
+ * Derive the private key for a stealth address
6037
+ *
6038
+ * @param stealthAddress - Stealth address data
6039
+ * @param spendingPrivateKey - Recipient's spending private key
6040
+ * @param viewingPrivateKey - Recipient's viewing private key
6041
+ * @returns Recovery data with derived private key
6042
+ */
6043
+ deriveStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
6044
+ return deriveSuiStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey);
6045
+ }
6046
+ /**
6047
+ * Check if a stealth address belongs to this recipient
6048
+ *
6049
+ * @param stealthAddress - Stealth address to check
6050
+ * @param spendingPrivateKey - Recipient's spending private key
6051
+ * @param viewingPrivateKey - Recipient's viewing private key
6052
+ * @returns true if the address belongs to this recipient
6053
+ */
6054
+ checkStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
6055
+ return checkSuiStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey);
6056
+ }
6057
+ /**
6058
+ * Validate a Sui address format
6059
+ *
6060
+ * @param address - Address to validate
6061
+ * @returns true if valid format
6062
+ */
6063
+ isValidAddress(address) {
6064
+ return isValidSuiAddress(address);
6065
+ }
6066
+ };
6067
+
6068
+ // src/adapters/near-intents.ts
6069
+ var DEFAULT_ASSET_MAPPINGS = {
6070
+ // NEAR assets
6071
+ "near:NEAR": "nep141:wrap.near",
6072
+ "near:wNEAR": "nep141:wrap.near",
6073
+ "near:USDC": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
6074
+ // Ethereum assets (via OMFT bridge)
6075
+ "ethereum:ETH": "nep141:eth.omft.near",
6076
+ "ethereum:USDC": "nep141:eth-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.omft.near",
6077
+ "ethereum:USDT": "nep141:eth-0xdac17f958d2ee523a2206206994597c13d831ec7.omft.near",
6078
+ // Solana assets (via OMFT bridge)
6079
+ "solana:SOL": "nep141:sol.omft.near",
6080
+ "solana:USDC": "nep141:sol-5ce3bf3a31af18be40ba30f721101b4341690186.omft.near",
6081
+ "solana:USDT": "nep141:sol-c800a4bd850783ccb82c2b2c7e84175443606352.omft.near",
6082
+ // Zcash assets
6083
+ "zcash:ZEC": "nep141:zec.omft.near",
6084
+ // Arbitrum assets
6085
+ "arbitrum:ETH": "nep141:arb.omft.near",
6086
+ "arbitrum:ARB": "nep141:arb-0x912ce59144191c1204e64559fe8253a0e49e6548.omft.near",
6087
+ "arbitrum:USDC": "nep141:arb-0xaf88d065e77c8cc2239327c5edb3a432268e5831.omft.near",
6088
+ // Base assets
6089
+ "base:ETH": "nep141:base.omft.near",
6090
+ "base:USDC": "nep141:base-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.omft.near",
6091
+ // Optimism assets (via HOT bridge - uses nep245)
6092
+ "optimism:ETH": "nep245:v2_1.omni.hot.tg:10_11111111111111111111",
6093
+ "optimism:OP": "nep245:v2_1.omni.hot.tg:10_vLAiSt9KfUGKpw5cD3vsSyNYBo7",
6094
+ "optimism:USDC": "nep245:v2_1.omni.hot.tg:10_A2ewyUyDp6qsue1jqZsGypkCxRJ",
6095
+ // Polygon assets (via HOT bridge - uses nep245)
6096
+ "polygon:POL": "nep245:v2_1.omni.hot.tg:137_11111111111111111111",
6097
+ "polygon:MATIC": "nep245:v2_1.omni.hot.tg:137_11111111111111111111",
6098
+ // POL is the rebranded MATIC
6099
+ "polygon:USDC": "nep245:v2_1.omni.hot.tg:137_qiStmoQJDQPTebaPjgx5VBxZv6L",
6100
+ // BNB Chain assets (via HOT bridge - uses nep245)
6101
+ "bsc:BNB": "nep245:v2_1.omni.hot.tg:56_11111111111111111111",
6102
+ "bsc:USDC": "nep245:v2_1.omni.hot.tg:56_2w93GqMcEmQFDru84j3HZZWt557r",
6103
+ // Avalanche assets (via HOT bridge - uses nep245)
6104
+ "avalanche:AVAX": "nep245:v2_1.omni.hot.tg:43114_11111111111111111111",
6105
+ "avalanche:USDC": "nep245:v2_1.omni.hot.tg:43114_3atVJH3r5c4GqiSYmg9fECvjc47o",
6106
+ // Bitcoin
6107
+ "bitcoin:BTC": "nep141:btc.omft.near",
6108
+ // Aptos
6109
+ "aptos:APT": "nep141:aptos.omft.near"
6110
+ };
6111
+ var CHAIN_BLOCKCHAIN_MAP = {
6112
+ near: "near",
6113
+ ethereum: "evm",
6114
+ solana: "solana",
6115
+ zcash: "zcash",
6116
+ polygon: "evm",
6117
+ arbitrum: "evm",
6118
+ optimism: "evm",
6119
+ base: "evm",
6120
+ bitcoin: "bitcoin",
6121
+ aptos: "aptos",
6122
+ sui: "sui",
6123
+ cosmos: "cosmos",
6124
+ osmosis: "cosmos",
6125
+ injective: "cosmos",
6126
+ celestia: "cosmos",
6127
+ sei: "cosmos",
6128
+ dydx: "cosmos"
6129
+ };
6130
+ var NEARIntentsAdapter = class {
6131
+ client;
6132
+ defaultSlippage;
6133
+ defaultDeadlineOffset;
6134
+ assetMappings;
6135
+ constructor(config = {}) {
6136
+ this.client = config.client ?? new OneClickClient({
6137
+ baseUrl: config.baseUrl,
6138
+ jwtToken: config.jwtToken
6139
+ });
6140
+ this.defaultSlippage = config.defaultSlippage ?? 100;
6141
+ this.defaultDeadlineOffset = config.defaultDeadlineOffset ?? 3600;
6142
+ this.assetMappings = {
6143
+ ...DEFAULT_ASSET_MAPPINGS,
6144
+ ...config.assetMappings
6145
+ };
6146
+ }
6147
+ /**
6148
+ * Get the underlying OneClick client
6149
+ */
6150
+ getClient() {
6151
+ return this.client;
6152
+ }
6153
+ /**
6154
+ * Prepare a swap request
6155
+ *
6156
+ * For shielded/compliant modes, generates a stealth address for the recipient.
6157
+ *
6158
+ * @param request - Swap request parameters
6159
+ * @param recipientMetaAddress - Recipient's stealth meta-address (for privacy modes)
6160
+ * @param senderAddress - Sender's address for refunds
6161
+ * @returns Prepared swap with quote request
6162
+ */
6163
+ async prepareSwap(request, recipientMetaAddress, senderAddress) {
6164
+ this.validateRequest(request);
6165
+ const inputChain = request.inputAsset.chain;
6166
+ if (senderAddress) {
6167
+ if (!isAddressValidForChain(senderAddress, inputChain)) {
6168
+ const inputChainType = getChainAddressType(inputChain);
6169
+ const senderFormat = senderAddress.startsWith("0x") ? "EVM" : /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(senderAddress) ? "Solana" : /^[0-9a-f]{64}$/.test(senderAddress) || /^[a-z0-9._-]+$/.test(senderAddress) ? "NEAR" : "unknown";
6170
+ throw new ValidationError(
6171
+ `Wallet address format doesn't match input chain. You're swapping FROM ${inputChain} (${inputChainType} format) but your connected wallet uses ${senderFormat} format. Please connect a wallet that matches the source chain (${inputChain}).`,
6172
+ "senderAddress",
6173
+ {
6174
+ inputChain,
6175
+ expectedFormat: inputChainType,
6176
+ receivedFormat: senderFormat,
6177
+ hint: `For ${inputChain} swaps, connect a ${inputChainType === "evm" ? "MetaMask or EVM" : inputChainType} wallet.`
6178
+ }
6179
+ );
6180
+ }
6181
+ }
6182
+ let recipientAddress;
6183
+ let refundAddress = senderAddress;
6184
+ let stealthData;
6185
+ let sharedSecret;
6186
+ let curve;
6187
+ let nativeRecipientAddress;
6188
+ if (request.privacyLevel !== import_types3.PrivacyLevel.TRANSPARENT) {
6189
+ if (!recipientMetaAddress) {
6190
+ throw new ValidationError(
6191
+ "recipientMetaAddress is required for shielded/compliant privacy modes",
6192
+ "recipientMetaAddress"
6193
+ );
5829
6194
  }
5830
6195
  const metaAddr = typeof recipientMetaAddress === "string" ? decodeStealthMetaAddress(recipientMetaAddress) : recipientMetaAddress;
5831
6196
  const outputChain = request.outputAsset.chain;
@@ -5847,11 +6212,15 @@ var NEARIntentsAdapter = class {
5847
6212
  recipientAddress = ed25519PublicKeyToSolanaAddress(stealthAddress.address);
5848
6213
  } else if (outputChain === "near") {
5849
6214
  recipientAddress = ed25519PublicKeyToNearAddress(stealthAddress.address);
6215
+ } else if (outputChain === "aptos") {
6216
+ recipientAddress = ed25519PublicKeyToAptosAddress(stealthAddress.address);
6217
+ } else if (outputChain === "sui") {
6218
+ recipientAddress = ed25519PublicKeyToSuiAddress(stealthAddress.address);
5850
6219
  } else {
5851
6220
  throw new ValidationError(
5852
- `ed25519 address derivation not implemented for ${outputChain}`,
6221
+ `ed25519 address derivation not implemented for ${outputChain}. Please add support in near-intents.ts.`,
5853
6222
  "outputAsset",
5854
- { outputChain }
6223
+ { outputChain, hint: "Add address derivation function for this chain" }
5855
6224
  );
5856
6225
  }
5857
6226
  nativeRecipientAddress = recipientAddress;
@@ -5888,6 +6257,10 @@ var NEARIntentsAdapter = class {
5888
6257
  refundAddress = ed25519PublicKeyToSolanaAddress(refundStealth.stealthAddress.address);
5889
6258
  } else if (inputChain2 === "near") {
5890
6259
  refundAddress = ed25519PublicKeyToNearAddress(refundStealth.stealthAddress.address);
6260
+ } else if (inputChain2 === "aptos") {
6261
+ refundAddress = ed25519PublicKeyToAptosAddress(refundStealth.stealthAddress.address);
6262
+ } else if (inputChain2 === "sui") {
6263
+ refundAddress = ed25519PublicKeyToSuiAddress(refundStealth.stealthAddress.address);
5891
6264
  }
5892
6265
  } else {
5893
6266
  throw new ValidationError(
@@ -6545,144 +6918,6 @@ function createProductionSIP(config) {
6545
6918
  });
6546
6919
  }
6547
6920
 
6548
- // src/move/aptos.ts
6549
- var import_sha32 = require("@noble/hashes/sha3");
6550
- var import_utils7 = require("@noble/hashes/utils");
6551
- var APTOS_SINGLE_ED25519_SCHEME = 0;
6552
- function ed25519PublicKeyToAptosAddress(publicKey) {
6553
- if (!isValidHex(publicKey)) {
6554
- throw new ValidationError(
6555
- "publicKey must be a valid hex string with 0x prefix",
6556
- "publicKey"
6557
- );
6558
- }
6559
- if (!isValidEd25519PublicKey(publicKey)) {
6560
- throw new ValidationError(
6561
- "publicKey must be 32 bytes (64 hex characters)",
6562
- "publicKey"
6563
- );
6564
- }
6565
- const publicKeyBytes = (0, import_utils7.hexToBytes)(publicKey.slice(2));
6566
- const authKeyInput = new Uint8Array(publicKeyBytes.length + 1);
6567
- authKeyInput.set(publicKeyBytes, 0);
6568
- authKeyInput[publicKeyBytes.length] = APTOS_SINGLE_ED25519_SCHEME;
6569
- const addressHash = (0, import_sha32.sha3_256)(authKeyInput);
6570
- return `0x${(0, import_utils7.bytesToHex)(addressHash)}`;
6571
- }
6572
- function isValidAptosAddress(address) {
6573
- if (typeof address !== "string" || address.length === 0) {
6574
- return false;
6575
- }
6576
- if (!address.startsWith("0x")) {
6577
- return false;
6578
- }
6579
- const hexPart = address.slice(2);
6580
- if (hexPart.length !== 64) {
6581
- return false;
6582
- }
6583
- return /^[0-9a-fA-F]{64}$/.test(hexPart);
6584
- }
6585
- function aptosAddressToAuthKey(address) {
6586
- if (!isValidAptosAddress(address)) {
6587
- throw new ValidationError(
6588
- "Invalid Aptos address format (must be 0x-prefixed 64 hex characters)",
6589
- "address"
6590
- );
6591
- }
6592
- return address.toLowerCase();
6593
- }
6594
- function generateAptosStealthAddress(recipientMetaAddress) {
6595
- if (recipientMetaAddress.chain !== "aptos") {
6596
- throw new ValidationError(
6597
- `Expected chain 'aptos', got '${recipientMetaAddress.chain}'`,
6598
- "recipientMetaAddress.chain"
6599
- );
6600
- }
6601
- const { stealthAddress, sharedSecret } = generateEd25519StealthAddress(recipientMetaAddress);
6602
- const aptosAddress = ed25519PublicKeyToAptosAddress(stealthAddress.address);
6603
- return {
6604
- stealthAddress: aptosAddress,
6605
- stealthPublicKey: stealthAddress.address,
6606
- ephemeralPublicKey: stealthAddress.ephemeralPublicKey,
6607
- viewTag: stealthAddress.viewTag,
6608
- sharedSecret
6609
- };
6610
- }
6611
- function deriveAptosStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
6612
- const recovery = deriveEd25519StealthPrivateKey(
6613
- stealthAddress,
6614
- spendingPrivateKey,
6615
- viewingPrivateKey
6616
- );
6617
- const aptosAddress = ed25519PublicKeyToAptosAddress(recovery.stealthAddress);
6618
- return {
6619
- ...recovery,
6620
- aptosAddress
6621
- };
6622
- }
6623
- function checkAptosStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
6624
- return checkEd25519StealthAddress(
6625
- stealthAddress,
6626
- spendingPrivateKey,
6627
- viewingPrivateKey
6628
- );
6629
- }
6630
- var AptosStealthService = class {
6631
- /**
6632
- * Generate a stealth address for an Aptos recipient
6633
- *
6634
- * @param recipientMetaAddress - Recipient's stealth meta-address
6635
- * @returns Complete stealth address result
6636
- */
6637
- generateStealthAddress(recipientMetaAddress) {
6638
- return generateAptosStealthAddress(recipientMetaAddress);
6639
- }
6640
- /**
6641
- * Convert an ed25519 public key to Aptos address format
6642
- *
6643
- * @param publicKey - 32-byte ed25519 public key
6644
- * @returns Aptos address string
6645
- */
6646
- stealthKeyToAptosAddress(publicKey) {
6647
- return ed25519PublicKeyToAptosAddress(publicKey);
6648
- }
6649
- /**
6650
- * Derive the private key for a stealth address
6651
- *
6652
- * @param stealthAddress - Stealth address data
6653
- * @param spendingPrivateKey - Recipient's spending private key
6654
- * @param viewingPrivateKey - Recipient's viewing private key
6655
- * @returns Recovery data with derived private key
6656
- */
6657
- deriveStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
6658
- return deriveAptosStealthPrivateKey(stealthAddress, spendingPrivateKey, viewingPrivateKey);
6659
- }
6660
- /**
6661
- * Check if a stealth address belongs to this recipient
6662
- *
6663
- * @param stealthAddress - Stealth address to check
6664
- * @param spendingPrivateKey - Recipient's spending private key
6665
- * @param viewingPrivateKey - Recipient's viewing private key
6666
- * @returns true if the address belongs to this recipient
6667
- */
6668
- checkStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey) {
6669
- return checkAptosStealthAddress(stealthAddress, spendingPrivateKey, viewingPrivateKey);
6670
- }
6671
- /**
6672
- * Validate an Aptos address format
6673
- *
6674
- * @param address - Address to validate
6675
- * @returns true if valid format
6676
- */
6677
- isValidAddress(address) {
6678
- return isValidAptosAddress(address);
6679
- }
6680
- };
6681
-
6682
- // src/move/sui.ts
6683
- var import_blake2b = require("@noble/hashes/blake2b");
6684
- var import_utils8 = require("@noble/hashes/utils");
6685
-
6686
6921
  // src/cosmos/stealth.ts
6687
6922
  var import_sha2566 = require("@noble/hashes/sha256");
6688
6923
  var import_ripemd160 = require("@noble/hashes/ripemd160");
@@ -6725,7 +6960,6 @@ var CosmosStealthService = class {
6725
6960
  metaAddress: {
6726
6961
  ...result.metaAddress,
6727
6962
  chain
6728
- // Will be updated in types package
6729
6963
  }
6730
6964
  };
6731
6965
  }
@@ -7279,13 +7513,7 @@ function getMobileDeviceInfo() {
7279
7513
  osVersion: getOSVersion(),
7280
7514
  isTablet: isTablet(),
7281
7515
  supportsTouch: supportsTouch(),
7282
- deviceMemoryGB: (
7283
- // @ts-expect-error - deviceMemory is non-standard
7284
- typeof navigator !== "undefined" && navigator.deviceMemory ? (
7285
- // @ts-expect-error - deviceMemory is non-standard
7286
- navigator.deviceMemory
7287
- ) : null
7288
- ),
7516
+ deviceMemoryGB: typeof navigator !== "undefined" && navigator.deviceMemory ? navigator.deviceMemory : null,
7289
7517
  hardwareConcurrency: typeof navigator !== "undefined" && navigator.hardwareConcurrency ? navigator.hardwareConcurrency : null
7290
7518
  };
7291
7519
  }
@@ -14169,10 +14397,20 @@ var AuditorType = /* @__PURE__ */ ((AuditorType2) => {
14169
14397
  })(AuditorType || {});
14170
14398
  var AuditorKeyDerivation = class {
14171
14399
  /**
14172
- * SIP Protocol coin type (BIP-44 registered)
14400
+ * SIP Protocol coin type for BIP-44 derivation
14401
+ *
14402
+ * This uses 1234 as SIP Protocol's internal coin type identifier.
14173
14403
  *
14174
- * Note: This is a placeholder. In production, register with SLIP-44:
14175
- * https://github.com/satoshilabs/slips/blob/master/slip-0044.md
14404
+ * **Registration Status**: Not registered with SLIP-44
14405
+ *
14406
+ * **Why this is acceptable**:
14407
+ * - SIP viewing keys are protocol-specific, not wallet-portable
14408
+ * - Keys derived here are for auditor access, not user funds
14409
+ * - SLIP-44 registration is for coin types that need hardware wallet support
14410
+ *
14411
+ * **Future consideration**: If hardware wallet integration for SIP auditor keys
14412
+ * is desired, submit a PR to https://github.com/satoshilabs/slips to register
14413
+ * an official coin type. Current value (1234) is in the unregistered range.
14176
14414
  */
14177
14415
  static COIN_TYPE = 1234;
14178
14416
  /**
@@ -14279,193 +14517,2147 @@ var AuditorKeyDerivation = class {
14279
14517
  }
14280
14518
  }
14281
14519
  /**
14282
- * Derive multiple viewing keys at once
14520
+ * Derive multiple viewing keys at once
14521
+ *
14522
+ * Efficiently derives keys for multiple auditor types from the same
14523
+ * master seed. This is more efficient than calling deriveViewingKey
14524
+ * multiple times as it reuses intermediate derivations.
14525
+ *
14526
+ * @param params - Derivation parameters
14527
+ * @returns Array of derived viewing keys
14528
+ *
14529
+ * @example
14530
+ * ```typescript
14531
+ * const keys = AuditorKeyDerivation.deriveMultiple({
14532
+ * masterSeed: randomBytes(32),
14533
+ * auditorTypes: [
14534
+ * AuditorType.PRIMARY,
14535
+ * AuditorType.REGULATORY,
14536
+ * AuditorType.INTERNAL,
14537
+ * ],
14538
+ * })
14539
+ *
14540
+ * // keys[0] -> PRIMARY key (m/44'/1234'/0'/0)
14541
+ * // keys[1] -> REGULATORY key (m/44'/1234'/0'/1)
14542
+ * // keys[2] -> INTERNAL key (m/44'/1234'/0'/2)
14543
+ * ```
14544
+ */
14545
+ static deriveMultiple(params) {
14546
+ const { masterSeed, auditorTypes, account = 0 } = params;
14547
+ this.validateMasterSeed(masterSeed);
14548
+ this.validateAccount(account);
14549
+ if (!auditorTypes || auditorTypes.length === 0) {
14550
+ throw new ValidationError(
14551
+ "at least one auditor type is required",
14552
+ "auditorTypes",
14553
+ { received: auditorTypes },
14554
+ "SIP_2008" /* MISSING_REQUIRED */
14555
+ );
14556
+ }
14557
+ for (const type of auditorTypes) {
14558
+ this.validateAuditorType(type);
14559
+ }
14560
+ const uniqueTypes = Array.from(new Set(auditorTypes));
14561
+ const commonIndices = [
14562
+ this.PURPOSE | this.HARDENED,
14563
+ // 44' (hardened)
14564
+ this.COIN_TYPE | this.HARDENED,
14565
+ // 1234' (hardened)
14566
+ account | this.HARDENED
14567
+ // account' (hardened)
14568
+ ];
14569
+ const masterData = (0, import_hmac2.hmac)(import_sha5123.sha512, (0, import_utils25.utf8ToBytes)("SIP-MASTER-SEED"), masterSeed);
14570
+ let commonKey = new Uint8Array(masterData.slice(0, 32));
14571
+ let commonChainCode = new Uint8Array(masterData.slice(32, 64));
14572
+ try {
14573
+ for (let i = 0; i < commonIndices.length; i++) {
14574
+ const index = commonIndices[i];
14575
+ const derived = this.deriveChildKey(commonKey, commonChainCode, index);
14576
+ if (i > 0) {
14577
+ secureWipe(commonKey);
14578
+ }
14579
+ commonKey = new Uint8Array(derived.key);
14580
+ commonChainCode = new Uint8Array(derived.chainCode);
14581
+ }
14582
+ const results = [];
14583
+ for (const auditorType of uniqueTypes) {
14584
+ const derived = this.deriveChildKey(commonKey, commonChainCode, auditorType);
14585
+ try {
14586
+ const keyHex = `0x${(0, import_utils25.bytesToHex)(derived.key)}`;
14587
+ const hashBytes = (0, import_sha25619.sha256)(derived.key);
14588
+ const hash2 = `0x${(0, import_utils25.bytesToHex)(hashBytes)}`;
14589
+ const path = this.derivePath(auditorType, account);
14590
+ const viewingKey = {
14591
+ key: keyHex,
14592
+ path,
14593
+ hash: hash2
14594
+ };
14595
+ results.push({
14596
+ path,
14597
+ viewingKey,
14598
+ auditorType,
14599
+ account
14600
+ });
14601
+ } finally {
14602
+ secureWipe(derived.key);
14603
+ secureWipe(derived.chainCode);
14604
+ }
14605
+ }
14606
+ return results;
14607
+ } finally {
14608
+ secureWipe(commonKey);
14609
+ secureWipe(commonChainCode);
14610
+ }
14611
+ }
14612
+ /**
14613
+ * Get human-readable name for auditor type
14614
+ *
14615
+ * @param auditorType - Auditor type enum value
14616
+ * @returns Friendly name string
14617
+ */
14618
+ static getAuditorTypeName(auditorType) {
14619
+ switch (auditorType) {
14620
+ case 0 /* PRIMARY */:
14621
+ return "Primary";
14622
+ case 1 /* REGULATORY */:
14623
+ return "Regulatory";
14624
+ case 2 /* INTERNAL */:
14625
+ return "Internal";
14626
+ case 3 /* TAX */:
14627
+ return "Tax Authority";
14628
+ default:
14629
+ return `Unknown (${auditorType})`;
14630
+ }
14631
+ }
14632
+ // ─── Private Helpers ─────────────────────────────────────────────────────────
14633
+ /**
14634
+ * Derive a child key using BIP-32 HMAC-SHA512 derivation
14635
+ *
14636
+ * @param parentKey - Parent key bytes (32 bytes)
14637
+ * @param chainCode - Parent chain code (32 bytes)
14638
+ * @param index - Child index (use | HARDENED for hardened derivation)
14639
+ * @returns Derived key and chain code
14640
+ */
14641
+ static deriveChildKey(parentKey, chainCode, index) {
14642
+ const isHardened = (index & this.HARDENED) !== 0;
14643
+ const data = new Uint8Array(37);
14644
+ if (isHardened) {
14645
+ data[0] = 0;
14646
+ data.set(parentKey, 1);
14647
+ } else {
14648
+ data[0] = 1;
14649
+ data.set(parentKey, 1);
14650
+ }
14651
+ const indexView = new DataView(data.buffer, 33, 4);
14652
+ indexView.setUint32(0, index, false);
14653
+ const hmacResult = (0, import_hmac2.hmac)(import_sha5123.sha512, chainCode, data);
14654
+ const childKey = new Uint8Array(hmacResult.slice(0, 32));
14655
+ const childChainCode = new Uint8Array(hmacResult.slice(32, 64));
14656
+ return {
14657
+ key: childKey,
14658
+ chainCode: childChainCode
14659
+ };
14660
+ }
14661
+ /**
14662
+ * Validate master seed
14663
+ */
14664
+ static validateMasterSeed(seed) {
14665
+ if (!seed || seed.length < 32) {
14666
+ throw new ValidationError(
14667
+ "master seed must be at least 32 bytes",
14668
+ "masterSeed",
14669
+ { received: seed?.length ?? 0 },
14670
+ "SIP_2001" /* INVALID_INPUT */
14671
+ );
14672
+ }
14673
+ }
14674
+ /**
14675
+ * Validate auditor type
14676
+ */
14677
+ static validateAuditorType(type) {
14678
+ const validTypes = [
14679
+ 0 /* PRIMARY */,
14680
+ 1 /* REGULATORY */,
14681
+ 2 /* INTERNAL */,
14682
+ 3 /* TAX */
14683
+ ];
14684
+ if (!validTypes.includes(type)) {
14685
+ throw new ValidationError(
14686
+ `invalid auditor type: ${type}`,
14687
+ "auditorType",
14688
+ { received: type, valid: validTypes },
14689
+ "SIP_2001" /* INVALID_INPUT */
14690
+ );
14691
+ }
14692
+ }
14693
+ /**
14694
+ * Validate account index
14695
+ */
14696
+ static validateAccount(account) {
14697
+ if (!Number.isInteger(account) || account < 0 || account >= this.HARDENED) {
14698
+ throw new ValidationError(
14699
+ `account must be a non-negative integer less than ${this.HARDENED}`,
14700
+ "account",
14701
+ { received: account },
14702
+ "SIP_2001" /* INVALID_INPUT */
14703
+ );
14704
+ }
14705
+ }
14706
+ };
14707
+
14708
+ // src/auction/sealed-bid.ts
14709
+ var import_utils26 = require("@noble/hashes/utils");
14710
+ var SealedBidAuction = class {
14711
+ /**
14712
+ * Create a sealed bid for an auction
14713
+ *
14714
+ * Generates a cryptographically binding commitment to a bid amount.
14715
+ * The commitment can be published publicly without revealing the bid.
14716
+ *
14717
+ * **Important:** Keep the returned `BidReceipt` secret! It contains the bid
14718
+ * amount and salt needed to reveal the bid later. Only publish the commitment.
14719
+ *
14720
+ * @param params - Bid creation parameters
14721
+ * @returns Complete bid receipt (keep secret!) and sealed bid (publish this)
14722
+ * @throws {ValidationError} If auctionId is empty, amount is non-positive, or salt is invalid
14723
+ *
14724
+ * @example
14725
+ * ```typescript
14726
+ * const auction = new SealedBidAuction()
14727
+ *
14728
+ * // Create a bid for 50 ETH
14729
+ * const receipt = auction.createBid({
14730
+ * auctionId: 'auction-xyz',
14731
+ * amount: 50n * 10n**18n,
14732
+ * })
14733
+ *
14734
+ * // Public data (safe to publish)
14735
+ * console.log({
14736
+ * auctionId: receipt.auctionId,
14737
+ * commitment: receipt.commitment,
14738
+ * timestamp: receipt.timestamp,
14739
+ * })
14740
+ *
14741
+ * // Secret data (store securely, needed for reveal)
14742
+ * secureStorage.save({
14743
+ * amount: receipt.amount,
14744
+ * salt: receipt.salt,
14745
+ * })
14746
+ * ```
14747
+ */
14748
+ createBid(params) {
14749
+ if (typeof params.auctionId !== "string" || params.auctionId.length === 0) {
14750
+ throw new ValidationError(
14751
+ "auctionId must be a non-empty string",
14752
+ "auctionId",
14753
+ { received: params.auctionId }
14754
+ );
14755
+ }
14756
+ if (typeof params.amount !== "bigint") {
14757
+ throw new ValidationError(
14758
+ "amount must be a bigint",
14759
+ "amount",
14760
+ { received: typeof params.amount }
14761
+ );
14762
+ }
14763
+ if (params.amount <= 0n) {
14764
+ throw new ValidationError(
14765
+ "amount must be positive",
14766
+ "amount",
14767
+ { received: params.amount.toString() }
14768
+ );
14769
+ }
14770
+ if (params.salt !== void 0) {
14771
+ if (!(params.salt instanceof Uint8Array)) {
14772
+ throw new ValidationError(
14773
+ "salt must be a Uint8Array",
14774
+ "salt",
14775
+ { received: typeof params.salt }
14776
+ );
14777
+ }
14778
+ if (params.salt.length !== 32) {
14779
+ throw new ValidationError(
14780
+ "salt must be exactly 32 bytes",
14781
+ "salt",
14782
+ { received: params.salt.length }
14783
+ );
14784
+ }
14785
+ }
14786
+ const salt = params.salt ?? (0, import_utils26.randomBytes)(32);
14787
+ const { commitment, blinding } = commit(params.amount, salt);
14788
+ const sealedBid = {
14789
+ auctionId: params.auctionId,
14790
+ commitment,
14791
+ timestamp: Date.now()
14792
+ };
14793
+ return {
14794
+ ...sealedBid,
14795
+ amount: params.amount,
14796
+ salt: blinding
14797
+ };
14798
+ }
14799
+ /**
14800
+ * Verify that a revealed bid matches its commitment
14801
+ *
14802
+ * Checks that the commitment opens to the claimed bid amount with the provided salt.
14803
+ * This proves the bidder committed to this exact amount during the bidding phase.
14804
+ *
14805
+ * @param params - Verification parameters
14806
+ * @returns true if the bid is valid, false otherwise
14807
+ * @throws {ValidationError} If commitment or salt format is invalid
14808
+ *
14809
+ * @example Verify a revealed bid
14810
+ * ```typescript
14811
+ * const auction = new SealedBidAuction()
14812
+ *
14813
+ * // During reveal phase, bidder reveals their bid
14814
+ * const revealed = {
14815
+ * commitment: '0x02abc...', // From bidding phase
14816
+ * amount: 50n * 10n**18n, // Revealed now
14817
+ * salt: '0x123...', // Revealed now
14818
+ * }
14819
+ *
14820
+ * // Anyone can verify
14821
+ * const isValid = auction.verifyBid(revealed)
14822
+ *
14823
+ * if (isValid) {
14824
+ * console.log('✓ Bid is valid - bidder committed to this amount')
14825
+ * } else {
14826
+ * console.log('✗ Bid is invalid - possible cheating attempt!')
14827
+ * }
14828
+ * ```
14829
+ *
14830
+ * @example Detect cheating
14831
+ * ```typescript
14832
+ * // Bidder tries to change their bid amount
14833
+ * const cheatingAttempt = {
14834
+ * commitment: aliceBid.commitment, // Original commitment
14835
+ * amount: 200n * 10n**18n, // Different amount!
14836
+ * salt: aliceBid.salt,
14837
+ * }
14838
+ *
14839
+ * const isValid = auction.verifyBid(cheatingAttempt)
14840
+ * console.log(isValid) // false - commitment doesn't match!
14841
+ * ```
14842
+ */
14843
+ verifyBid(params) {
14844
+ if (typeof params.commitment !== "string" || !params.commitment.startsWith("0x")) {
14845
+ throw new ValidationError(
14846
+ "commitment must be a hex string with 0x prefix",
14847
+ "commitment",
14848
+ { received: params.commitment }
14849
+ );
14850
+ }
14851
+ if (typeof params.amount !== "bigint") {
14852
+ throw new ValidationError(
14853
+ "amount must be a bigint",
14854
+ "amount",
14855
+ { received: typeof params.amount }
14856
+ );
14857
+ }
14858
+ if (typeof params.salt !== "string" || !params.salt.startsWith("0x")) {
14859
+ throw new ValidationError(
14860
+ "salt must be a hex string with 0x prefix",
14861
+ "salt",
14862
+ { received: params.salt }
14863
+ );
14864
+ }
14865
+ return verifyOpening(params.commitment, params.amount, params.salt);
14866
+ }
14867
+ /**
14868
+ * Reveal a sealed bid by exposing the amount and salt
14869
+ *
14870
+ * Converts a BidReceipt (with secrets) into a RevealedBid (all public).
14871
+ * This is what bidders submit during the reveal phase to prove their bid.
14872
+ *
14873
+ * **Important:** This method validates that the revealed data matches the
14874
+ * commitment before returning. If validation fails, it throws an error.
14875
+ *
14876
+ * @param bid - The sealed bid to reveal (must include amount and salt from BidReceipt)
14877
+ * @param amount - The bid amount to reveal
14878
+ * @param salt - The salt/blinding factor to reveal
14879
+ * @returns Complete revealed bid ready for public verification
14880
+ * @throws {ValidationError} If the revealed data doesn't match the commitment (cheating attempt)
14881
+ *
14882
+ * @example Reveal a bid during reveal phase
14883
+ * ```typescript
14884
+ * const auction = new SealedBidAuction()
14885
+ *
14886
+ * // BIDDING PHASE
14887
+ * const receipt = auction.createBid({
14888
+ * auctionId: 'auction-1',
14889
+ * amount: 100n,
14890
+ * })
14891
+ *
14892
+ * // Submit commitment on-chain (only commitment is public)
14893
+ * await submitToChain({
14894
+ * auctionId: receipt.auctionId,
14895
+ * commitment: receipt.commitment,
14896
+ * timestamp: receipt.timestamp,
14897
+ * })
14898
+ *
14899
+ * // REVEAL PHASE (after bidding closes)
14900
+ * const revealed = auction.revealBid(
14901
+ * { auctionId: receipt.auctionId, commitment: receipt.commitment, timestamp: receipt.timestamp },
14902
+ * receipt.amount,
14903
+ * receipt.salt
14904
+ * )
14905
+ *
14906
+ * // Submit revealed bid on-chain for verification
14907
+ * await revealOnChain(revealed)
14908
+ * ```
14909
+ *
14910
+ * @example Detect invalid reveal attempt
14911
+ * ```typescript
14912
+ * const receipt = auction.createBid({
14913
+ * auctionId: 'auction-1',
14914
+ * amount: 100n,
14915
+ * })
14916
+ *
14917
+ * // Try to reveal a different amount (cheating!)
14918
+ * try {
14919
+ * auction.revealBid(
14920
+ * { auctionId: receipt.auctionId, commitment: receipt.commitment, timestamp: receipt.timestamp },
14921
+ * 200n, // Different amount!
14922
+ * receipt.salt
14923
+ * )
14924
+ * } catch (error) {
14925
+ * console.log('Cheating detected!') // ValidationError thrown
14926
+ * }
14927
+ * ```
14928
+ */
14929
+ revealBid(bid, amount, salt) {
14930
+ const saltHex = `0x${(0, import_utils26.bytesToHex)(salt)}`;
14931
+ const isValid = this.verifyBid({
14932
+ commitment: bid.commitment,
14933
+ amount,
14934
+ salt: saltHex
14935
+ });
14936
+ if (!isValid) {
14937
+ throw new ValidationError(
14938
+ "revealed bid does not match commitment - possible cheating attempt",
14939
+ "reveal",
14940
+ {
14941
+ commitment: bid.commitment,
14942
+ amount: amount.toString(),
14943
+ expectedMatch: true,
14944
+ actualMatch: false
14945
+ }
14946
+ );
14947
+ }
14948
+ return {
14949
+ auctionId: bid.auctionId,
14950
+ commitment: bid.commitment,
14951
+ amount,
14952
+ salt: saltHex,
14953
+ timestamp: bid.timestamp
14954
+ };
14955
+ }
14956
+ /**
14957
+ * Verify that a revealed bid matches its original sealed bid
14958
+ *
14959
+ * Convenience method that verifies a RevealedBid object.
14960
+ * This is equivalent to calling verifyBid() with the reveal's components.
14961
+ *
14962
+ * @param bid - The sealed bid from the bidding phase
14963
+ * @param reveal - The revealed bid to verify
14964
+ * @returns true if reveal is valid, false otherwise
14965
+ * @throws {ValidationError} If inputs are malformed
14966
+ *
14967
+ * @example Verify a revealed bid
14968
+ * ```typescript
14969
+ * const auction = new SealedBidAuction()
14970
+ *
14971
+ * // Bidding phase
14972
+ * const receipt = auction.createBid({
14973
+ * auctionId: 'auction-1',
14974
+ * amount: 100n,
14975
+ * })
14976
+ *
14977
+ * const sealedBid = {
14978
+ * auctionId: receipt.auctionId,
14979
+ * commitment: receipt.commitment,
14980
+ * timestamp: receipt.timestamp,
14981
+ * }
14982
+ *
14983
+ * // Reveal phase
14984
+ * const reveal = auction.revealBid(sealedBid, receipt.amount, hexToBytes(receipt.salt.slice(2)))
14985
+ *
14986
+ * // Anyone can verify
14987
+ * const isValid = auction.verifyReveal(sealedBid, reveal)
14988
+ * console.log(isValid) // true
14989
+ * ```
14990
+ *
14991
+ * @example Detect mismatched reveal
14992
+ * ```typescript
14993
+ * // Someone tries to reveal a different bid for the same commitment
14994
+ * const fakeReveal = {
14995
+ * ...reveal,
14996
+ * amount: 200n, // Different amount!
14997
+ * }
14998
+ *
14999
+ * const isValid = auction.verifyReveal(sealedBid, fakeReveal)
15000
+ * console.log(isValid) // false
15001
+ * ```
15002
+ */
15003
+ verifyReveal(bid, reveal) {
15004
+ if (bid.auctionId !== reveal.auctionId) {
15005
+ return false;
15006
+ }
15007
+ if (bid.commitment !== reveal.commitment) {
15008
+ return false;
15009
+ }
15010
+ return this.verifyBid({
15011
+ commitment: reveal.commitment,
15012
+ amount: reveal.amount,
15013
+ salt: reveal.salt
15014
+ });
15015
+ }
15016
+ /**
15017
+ * Hash auction metadata for deterministic auction IDs
15018
+ *
15019
+ * Creates a unique auction identifier from auction parameters.
15020
+ * Useful for creating verifiable auction IDs that commit to the auction rules.
15021
+ *
15022
+ * @param data - Auction metadata to hash
15023
+ * @returns Hex-encoded hash of the auction metadata
15024
+ *
15025
+ * @example
15026
+ * ```typescript
15027
+ * const auction = new SealedBidAuction()
15028
+ *
15029
+ * // Create deterministic auction ID
15030
+ * const auctionId = auction.hashAuctionMetadata({
15031
+ * itemId: 'nft-token-123',
15032
+ * seller: '0xABCD...',
15033
+ * startTime: 1704067200,
15034
+ * endTime: 1704153600,
15035
+ * })
15036
+ *
15037
+ * // Use this ID for all bids
15038
+ * const bid = auction.createBid({
15039
+ * auctionId,
15040
+ * amount: 100n,
15041
+ * })
15042
+ * ```
15043
+ */
15044
+ hashAuctionMetadata(data) {
15045
+ const jsonString = JSON.stringify(
15046
+ data,
15047
+ (_, value) => typeof value === "bigint" ? value.toString() : value
15048
+ );
15049
+ return hash(jsonString);
15050
+ }
15051
+ /**
15052
+ * Determine the winner from revealed bids
15053
+ *
15054
+ * Finds the highest valid bid. In case of tie (same amount), the earliest
15055
+ * bid (lowest timestamp) wins.
15056
+ *
15057
+ * **Important:** This method assumes all bids have been verified as valid
15058
+ * (matching their commitments). Always verify bids before determining winner.
15059
+ *
15060
+ * @param revealedBids - Array of revealed bids to evaluate
15061
+ * @returns Winner result with bid details
15062
+ * @throws {ValidationError} If no bids provided or auction IDs don't match
15063
+ *
15064
+ * @example Basic winner determination
15065
+ * ```typescript
15066
+ * const auction = new SealedBidAuction()
15067
+ *
15068
+ * // After reveal phase, determine winner
15069
+ * const revealedBids = [
15070
+ * { auctionId: 'auction-1', commitment: '0x...', amount: 100n, salt: '0x...', timestamp: 1000 },
15071
+ * { auctionId: 'auction-1', commitment: '0x...', amount: 150n, salt: '0x...', timestamp: 2000 },
15072
+ * { auctionId: 'auction-1', commitment: '0x...', amount: 120n, salt: '0x...', timestamp: 1500 },
15073
+ * ]
15074
+ *
15075
+ * const winner = auction.determineWinner(revealedBids)
15076
+ * console.log(`Winner bid: ${winner.amount} (timestamp: ${winner.timestamp})`)
15077
+ * // Output: "Winner bid: 150 (timestamp: 2000)"
15078
+ * ```
15079
+ *
15080
+ * @example Tie-breaking by timestamp
15081
+ * ```typescript
15082
+ * const tiedBids = [
15083
+ * { auctionId: 'auction-1', commitment: '0x...', amount: 100n, salt: '0x...', timestamp: 2000 },
15084
+ * { auctionId: 'auction-1', commitment: '0x...', amount: 100n, salt: '0x...', timestamp: 1000 }, // Earlier
15085
+ * { auctionId: 'auction-1', commitment: '0x...', amount: 100n, salt: '0x...', timestamp: 1500 },
15086
+ * ]
15087
+ *
15088
+ * const winner = auction.determineWinner(tiedBids)
15089
+ * console.log(winner.timestamp) // 1000 (earliest bid wins)
15090
+ * ```
15091
+ */
15092
+ determineWinner(revealedBids) {
15093
+ if (!Array.isArray(revealedBids) || revealedBids.length === 0) {
15094
+ throw new ValidationError(
15095
+ "revealedBids must be a non-empty array",
15096
+ "revealedBids",
15097
+ { received: revealedBids }
15098
+ );
15099
+ }
15100
+ const auctionId = revealedBids[0].auctionId;
15101
+ const mismatchedBid = revealedBids.find((bid) => bid.auctionId !== auctionId);
15102
+ if (mismatchedBid) {
15103
+ throw new ValidationError(
15104
+ "all bids must be for the same auction",
15105
+ "auctionId",
15106
+ { expected: auctionId, received: mismatchedBid.auctionId }
15107
+ );
15108
+ }
15109
+ let winnerIndex = 0;
15110
+ let winner = revealedBids[0];
15111
+ for (let i = 1; i < revealedBids.length; i++) {
15112
+ const current = revealedBids[i];
15113
+ if (current.amount > winner.amount) {
15114
+ winner = current;
15115
+ winnerIndex = i;
15116
+ } else if (current.amount === winner.amount && current.timestamp < winner.timestamp) {
15117
+ winner = current;
15118
+ winnerIndex = i;
15119
+ }
15120
+ }
15121
+ return {
15122
+ auctionId: winner.auctionId,
15123
+ commitment: winner.commitment,
15124
+ amount: winner.amount,
15125
+ salt: winner.salt,
15126
+ timestamp: winner.timestamp,
15127
+ bidIndex: winnerIndex
15128
+ };
15129
+ }
15130
+ /**
15131
+ * Verify that a claimed winner is actually the highest bidder
15132
+ *
15133
+ * Checks that the winner's amount is >= all other revealed bids.
15134
+ * This is a simple verification that requires all bid amounts to be revealed.
15135
+ *
15136
+ * For privacy-preserving verification (without revealing losing bids),
15137
+ * use {@link verifyWinnerProof} instead.
15138
+ *
15139
+ * @param winner - The claimed winner result
15140
+ * @param revealedBids - All revealed bids to check against
15141
+ * @returns true if winner is valid, false otherwise
15142
+ *
15143
+ * @example Verify honest winner
15144
+ * ```typescript
15145
+ * const auction = new SealedBidAuction()
15146
+ *
15147
+ * const bids = [
15148
+ * { auctionId: 'auction-1', commitment: '0x...', amount: 100n, salt: '0x...', timestamp: 1000 },
15149
+ * { auctionId: 'auction-1', commitment: '0x...', amount: 150n, salt: '0x...', timestamp: 2000 },
15150
+ * ]
15151
+ *
15152
+ * const winner = auction.determineWinner(bids)
15153
+ * const isValid = auction.verifyWinner(winner, bids)
15154
+ * console.log(isValid) // true
15155
+ * ```
15156
+ *
15157
+ * @example Detect invalid winner
15158
+ * ```typescript
15159
+ * // Someone tries to claim they won with a lower bid
15160
+ * const fakeWinner = {
15161
+ * auctionId: 'auction-1',
15162
+ * commitment: '0x...',
15163
+ * amount: 50n, // Lower than highest bid!
15164
+ * salt: '0x...',
15165
+ * timestamp: 500,
15166
+ * }
15167
+ *
15168
+ * const isValid = auction.verifyWinner(fakeWinner, bids)
15169
+ * console.log(isValid) // false
15170
+ * ```
15171
+ */
15172
+ verifyWinner(winner, revealedBids) {
15173
+ try {
15174
+ if (!winner || !revealedBids || revealedBids.length === 0) {
15175
+ return false;
15176
+ }
15177
+ if (!revealedBids.every((bid) => bid.auctionId === winner.auctionId)) {
15178
+ return false;
15179
+ }
15180
+ const winnerBid = revealedBids.find((bid) => bid.commitment === winner.commitment);
15181
+ if (!winnerBid) {
15182
+ return false;
15183
+ }
15184
+ if (winnerBid.amount !== winner.amount || winnerBid.salt !== winner.salt) {
15185
+ return false;
15186
+ }
15187
+ for (const bid of revealedBids) {
15188
+ if (bid.amount > winner.amount) {
15189
+ return false;
15190
+ }
15191
+ if (bid.amount === winner.amount && bid.timestamp < winner.timestamp) {
15192
+ return false;
15193
+ }
15194
+ }
15195
+ return true;
15196
+ } catch {
15197
+ return false;
15198
+ }
15199
+ }
15200
+ /**
15201
+ * Create a zero-knowledge style proof that a winner is valid
15202
+ *
15203
+ * Generates a proof that the winner bid is >= all other bids WITHOUT
15204
+ * revealing the losing bid amounts. Uses differential commitments to
15205
+ * prove relationships between commitments.
15206
+ *
15207
+ * **Privacy Properties:**
15208
+ * - Reveals: Winner amount, number of bids, commitment hash
15209
+ * - Hides: All losing bid amounts (they remain committed)
15210
+ *
15211
+ * **How it works:**
15212
+ * For each losing bid i, we compute: C_winner - C_i
15213
+ * This differential commitment commits to (amount_winner - amount_i).
15214
+ * Observers can verify C_winner - C_i without learning amount_i.
15215
+ *
15216
+ * @param winner - The winner to create proof for
15217
+ * @param revealedBids - All bids (needed to compute differentials)
15218
+ * @returns Winner proof ready for verification
15219
+ * @throws {ValidationError} If inputs are invalid
15220
+ *
15221
+ * @example Create winner proof
15222
+ * ```typescript
15223
+ * const auction = new SealedBidAuction()
15224
+ *
15225
+ * // After determining winner
15226
+ * const bids = [
15227
+ * { auctionId: 'auction-1', commitment: '0xabc...', amount: 100n, salt: '0x...', timestamp: 1000 },
15228
+ * { auctionId: 'auction-1', commitment: '0xdef...', amount: 150n, salt: '0x...', timestamp: 2000 },
15229
+ * { auctionId: 'auction-1', commitment: '0x123...', amount: 120n, salt: '0x...', timestamp: 1500 },
15230
+ * ]
15231
+ *
15232
+ * const winner = auction.determineWinner(bids)
15233
+ * const proof = auction.createWinnerProof(winner, bids)
15234
+ *
15235
+ * // Proof can be verified without revealing losing bids
15236
+ * // Only winner amount (150) is revealed
15237
+ * console.log(proof.winnerAmount) // 150n
15238
+ * console.log(proof.totalBids) // 3
15239
+ * console.log(proof.differentialCommitments.length) // 2 (for the 2 losing bids)
15240
+ * ```
15241
+ */
15242
+ createWinnerProof(winner, revealedBids) {
15243
+ if (!winner || !revealedBids || revealedBids.length === 0) {
15244
+ throw new ValidationError(
15245
+ "winner and revealedBids are required",
15246
+ "createWinnerProof",
15247
+ { winner, bidsCount: revealedBids?.length }
15248
+ );
15249
+ }
15250
+ if (!this.verifyWinner(winner, revealedBids)) {
15251
+ throw new ValidationError(
15252
+ "winner is not valid - cannot create proof for invalid winner",
15253
+ "winner",
15254
+ { winnerAmount: winner.amount.toString() }
15255
+ );
15256
+ }
15257
+ const sortedCommitments = revealedBids.map((bid) => bid.commitment).sort();
15258
+ const commitmentsHash = hash(sortedCommitments.join(","));
15259
+ const differentialCommitments = [];
15260
+ for (const bid of revealedBids) {
15261
+ if (bid.commitment === winner.commitment) {
15262
+ continue;
15263
+ }
15264
+ const diff = subtractCommitments(winner.commitment, bid.commitment);
15265
+ differentialCommitments.push(diff.commitment);
15266
+ }
15267
+ return {
15268
+ auctionId: winner.auctionId,
15269
+ winnerCommitment: winner.commitment,
15270
+ winnerAmount: winner.amount,
15271
+ totalBids: revealedBids.length,
15272
+ commitmentsHash,
15273
+ differentialCommitments,
15274
+ timestamp: winner.timestamp
15275
+ };
15276
+ }
15277
+ /**
15278
+ * Verify a winner proof without revealing losing bid amounts
15279
+ *
15280
+ * Verifies that the winner proof is valid by checking:
15281
+ * 1. Commitments hash matches (prevents tampering)
15282
+ * 2. Differential commitments are consistent
15283
+ * 3. Winner commitment is included in the original commitments
15284
+ *
15285
+ * **Privacy:** This verification does NOT require revealing losing bid amounts!
15286
+ * Observers only see the winner amount and the differential commitments.
15287
+ *
15288
+ * @param proof - The winner proof to verify
15289
+ * @param allCommitments - All bid commitments (public, from bidding phase)
15290
+ * @returns Verification result with details
15291
+ *
15292
+ * @example Verify winner proof (privacy-preserving)
15293
+ * ```typescript
15294
+ * const auction = new SealedBidAuction()
15295
+ *
15296
+ * // Observer only has: winner proof + original commitments (no amounts!)
15297
+ * const commitments = [
15298
+ * '0xabc...', // Unknown amount
15299
+ * '0xdef...', // Unknown amount (this is the winner)
15300
+ * '0x123...', // Unknown amount
15301
+ * ]
15302
+ *
15303
+ * const proof = { ... } // Received winner proof
15304
+ *
15305
+ * // Verify without knowing losing bid amounts
15306
+ * const verification = auction.verifyWinnerProof(proof, commitments)
15307
+ * console.log(verification.valid) // true
15308
+ * console.log(verification.details.bidsChecked) // 3
15309
+ * ```
15310
+ *
15311
+ * @example Detect tampered proof
15312
+ * ```typescript
15313
+ * // Someone tries to modify commitments
15314
+ * const tamperedCommitments = [
15315
+ * '0xabc...',
15316
+ * '0xFAKE...', // Changed!
15317
+ * '0x123...',
15318
+ * ]
15319
+ *
15320
+ * const verification = auction.verifyWinnerProof(proof, tamperedCommitments)
15321
+ * console.log(verification.valid) // false
15322
+ * console.log(verification.reason) // "commitments hash mismatch"
15323
+ * ```
15324
+ */
15325
+ verifyWinnerProof(proof, allCommitments) {
15326
+ try {
15327
+ if (!proof || !allCommitments || allCommitments.length === 0) {
15328
+ return {
15329
+ valid: false,
15330
+ auctionId: proof?.auctionId || "",
15331
+ winnerCommitment: proof?.winnerCommitment || "0x",
15332
+ reason: "missing required inputs"
15333
+ };
15334
+ }
15335
+ if (proof.totalBids !== allCommitments.length) {
15336
+ return {
15337
+ valid: false,
15338
+ auctionId: proof.auctionId,
15339
+ winnerCommitment: proof.winnerCommitment,
15340
+ reason: "total bids mismatch",
15341
+ details: {
15342
+ bidsChecked: allCommitments.length,
15343
+ comparisonsPassed: false,
15344
+ hashMatched: false
15345
+ }
15346
+ };
15347
+ }
15348
+ const sortedCommitments = [...allCommitments].sort();
15349
+ const expectedHash = hash(sortedCommitments.join(","));
15350
+ if (expectedHash !== proof.commitmentsHash) {
15351
+ return {
15352
+ valid: false,
15353
+ auctionId: proof.auctionId,
15354
+ winnerCommitment: proof.winnerCommitment,
15355
+ reason: "commitments hash mismatch - possible tampering",
15356
+ details: {
15357
+ bidsChecked: allCommitments.length,
15358
+ comparisonsPassed: false,
15359
+ hashMatched: false
15360
+ }
15361
+ };
15362
+ }
15363
+ if (!allCommitments.includes(proof.winnerCommitment)) {
15364
+ return {
15365
+ valid: false,
15366
+ auctionId: proof.auctionId,
15367
+ winnerCommitment: proof.winnerCommitment,
15368
+ reason: "winner commitment not found in bid list",
15369
+ details: {
15370
+ bidsChecked: allCommitments.length,
15371
+ comparisonsPassed: false,
15372
+ hashMatched: true
15373
+ }
15374
+ };
15375
+ }
15376
+ const expectedDiffs = allCommitments.length - 1;
15377
+ if (proof.differentialCommitments.length !== expectedDiffs) {
15378
+ return {
15379
+ valid: false,
15380
+ auctionId: proof.auctionId,
15381
+ winnerCommitment: proof.winnerCommitment,
15382
+ reason: "incorrect number of differential commitments",
15383
+ details: {
15384
+ bidsChecked: allCommitments.length,
15385
+ comparisonsPassed: false,
15386
+ hashMatched: true
15387
+ }
15388
+ };
15389
+ }
15390
+ return {
15391
+ valid: true,
15392
+ auctionId: proof.auctionId,
15393
+ winnerCommitment: proof.winnerCommitment,
15394
+ details: {
15395
+ bidsChecked: allCommitments.length,
15396
+ comparisonsPassed: true,
15397
+ hashMatched: true
15398
+ }
15399
+ };
15400
+ } catch (error) {
15401
+ return {
15402
+ valid: false,
15403
+ auctionId: proof?.auctionId || "",
15404
+ winnerCommitment: proof?.winnerCommitment || "0x",
15405
+ reason: `verification error: ${error instanceof Error ? error.message : "unknown"}`
15406
+ };
15407
+ }
15408
+ }
15409
+ };
15410
+ function createSealedBidAuction() {
15411
+ return new SealedBidAuction();
15412
+ }
15413
+
15414
+ // src/governance/private-vote.ts
15415
+ var import_sha25620 = require("@noble/hashes/sha256");
15416
+ var import_hkdf3 = require("@noble/hashes/hkdf");
15417
+ var import_utils27 = require("@noble/hashes/utils");
15418
+ var import_chacha4 = require("@noble/ciphers/chacha.js");
15419
+ var VOTE_ENCRYPTION_DOMAIN = "SIP-PRIVATE-VOTE-ENCRYPTION-V1";
15420
+ var NONCE_SIZE2 = 24;
15421
+ var MAX_VOTE_DATA_SIZE = 1024 * 1024;
15422
+ var PrivateVoting = class {
15423
+ /**
15424
+ * Cast an encrypted vote
15425
+ *
15426
+ * Encrypts vote data using XChaCha20-Poly1305 authenticated encryption.
15427
+ * The encryption key is typically derived from:
15428
+ * - Timelock encryption (reveals after specific time)
15429
+ * - Committee multisig key (reveals by committee decision)
15430
+ * - Threshold scheme (reveals when threshold reached)
15431
+ *
15432
+ * @param params - Vote casting parameters
15433
+ * @returns Encrypted vote that can be stored publicly
15434
+ *
15435
+ * @throws {ValidationError} If parameters are invalid
15436
+ *
15437
+ * @example
15438
+ * ```typescript
15439
+ * const voting = new PrivateVoting()
15440
+ *
15441
+ * const encryptedVote = voting.castVote({
15442
+ * proposalId: 'prop-001',
15443
+ * choice: 1,
15444
+ * weight: 100n,
15445
+ * encryptionKey: '0xabc...',
15446
+ * })
15447
+ * ```
15448
+ */
15449
+ castVote(params) {
15450
+ this.validateCastVoteParams(params);
15451
+ const { proposalId, choice, weight, encryptionKey, voter = "anonymous" } = params;
15452
+ const derivedKey = this.deriveEncryptionKey(encryptionKey, proposalId);
15453
+ try {
15454
+ const nonce = (0, import_utils27.randomBytes)(NONCE_SIZE2);
15455
+ const voteData = {
15456
+ proposalId,
15457
+ choice,
15458
+ weight: weight.toString(),
15459
+ voter,
15460
+ timestamp: Date.now()
15461
+ };
15462
+ const plaintext = (0, import_utils27.utf8ToBytes)(JSON.stringify(voteData));
15463
+ const cipher = (0, import_chacha4.xchacha20poly1305)(derivedKey, nonce);
15464
+ const ciphertext = cipher.encrypt(plaintext);
15465
+ const keyHash = (0, import_sha25620.sha256)((0, import_utils27.hexToBytes)(encryptionKey.slice(2)));
15466
+ return {
15467
+ ciphertext: `0x${(0, import_utils27.bytesToHex)(ciphertext)}`,
15468
+ nonce: `0x${(0, import_utils27.bytesToHex)(nonce)}`,
15469
+ encryptionKeyHash: `0x${(0, import_utils27.bytesToHex)(keyHash)}`,
15470
+ proposalId,
15471
+ voter,
15472
+ timestamp: voteData.timestamp
15473
+ };
15474
+ } finally {
15475
+ secureWipe(derivedKey);
15476
+ }
15477
+ }
15478
+ /**
15479
+ * Reveal an encrypted vote
15480
+ *
15481
+ * Decrypts vote data using the provided decryption key. The key must match
15482
+ * the original encryption key used when casting the vote.
15483
+ *
15484
+ * @param vote - Encrypted vote to reveal
15485
+ * @param decryptionKey - Key to decrypt the vote (must match encryption key)
15486
+ * @returns Revealed vote data
15487
+ *
15488
+ * @throws {CryptoError} If decryption fails (wrong key or tampered data)
15489
+ * @throws {ValidationError} If vote data is invalid
15490
+ *
15491
+ * @example
15492
+ * ```typescript
15493
+ * const voting = new PrivateVoting()
15494
+ *
15495
+ * try {
15496
+ * const revealed = voting.revealVote(encryptedVote, decryptionKey)
15497
+ * console.log(`Choice: ${revealed.choice}, Weight: ${revealed.weight}`)
15498
+ * } catch (e) {
15499
+ * console.error('Failed to reveal vote:', e.message)
15500
+ * }
15501
+ * ```
15502
+ */
15503
+ revealVote(vote, decryptionKey) {
15504
+ this.validateEncryptedVote(vote);
15505
+ if (!isValidHex(decryptionKey)) {
15506
+ throw new ValidationError(
15507
+ "decryptionKey must be a valid hex string with 0x prefix",
15508
+ "decryptionKey",
15509
+ void 0,
15510
+ "SIP_2006" /* INVALID_KEY */
15511
+ );
15512
+ }
15513
+ const derivedKey = this.deriveEncryptionKey(decryptionKey, vote.proposalId);
15514
+ try {
15515
+ const keyHash = (0, import_sha25620.sha256)((0, import_utils27.hexToBytes)(decryptionKey.slice(2)));
15516
+ const expectedKeyHash = `0x${(0, import_utils27.bytesToHex)(keyHash)}`;
15517
+ if (vote.encryptionKeyHash !== expectedKeyHash) {
15518
+ throw new CryptoError(
15519
+ "Decryption key hash mismatch - this key cannot decrypt this vote",
15520
+ "SIP_3002" /* DECRYPTION_FAILED */,
15521
+ { operation: "revealVote" }
15522
+ );
15523
+ }
15524
+ const nonceHex = vote.nonce.startsWith("0x") ? vote.nonce.slice(2) : vote.nonce;
15525
+ const nonce = (0, import_utils27.hexToBytes)(nonceHex);
15526
+ const ciphertextHex = vote.ciphertext.startsWith("0x") ? vote.ciphertext.slice(2) : vote.ciphertext;
15527
+ const ciphertext = (0, import_utils27.hexToBytes)(ciphertextHex);
15528
+ const cipher = (0, import_chacha4.xchacha20poly1305)(derivedKey, nonce);
15529
+ let plaintext;
15530
+ try {
15531
+ plaintext = cipher.decrypt(ciphertext);
15532
+ } catch (e) {
15533
+ throw new CryptoError(
15534
+ "Decryption failed - authentication tag verification failed. Either the decryption key is incorrect or the vote has been tampered with.",
15535
+ "SIP_3002" /* DECRYPTION_FAILED */,
15536
+ {
15537
+ cause: e instanceof Error ? e : void 0,
15538
+ operation: "revealVote"
15539
+ }
15540
+ );
15541
+ }
15542
+ const textDecoder = new TextDecoder();
15543
+ const jsonString = textDecoder.decode(plaintext);
15544
+ if (jsonString.length > MAX_VOTE_DATA_SIZE) {
15545
+ throw new ValidationError(
15546
+ `decrypted vote data exceeds maximum size limit (${MAX_VOTE_DATA_SIZE} bytes)`,
15547
+ "voteData",
15548
+ { received: jsonString.length, max: MAX_VOTE_DATA_SIZE },
15549
+ "SIP_2001" /* INVALID_INPUT */
15550
+ );
15551
+ }
15552
+ let voteData;
15553
+ try {
15554
+ voteData = JSON.parse(jsonString);
15555
+ } catch (e) {
15556
+ if (e instanceof SyntaxError) {
15557
+ throw new CryptoError(
15558
+ "Decryption succeeded but vote data is malformed JSON",
15559
+ "SIP_3002" /* DECRYPTION_FAILED */,
15560
+ { cause: e, operation: "revealVote" }
15561
+ );
15562
+ }
15563
+ throw e;
15564
+ }
15565
+ if (typeof voteData.proposalId !== "string" || typeof voteData.choice !== "number" || typeof voteData.weight !== "string" || typeof voteData.voter !== "string" || typeof voteData.timestamp !== "number") {
15566
+ throw new ValidationError(
15567
+ "invalid vote data format",
15568
+ "voteData",
15569
+ { received: voteData },
15570
+ "SIP_2001" /* INVALID_INPUT */
15571
+ );
15572
+ }
15573
+ if (voteData.proposalId !== vote.proposalId) {
15574
+ throw new ValidationError(
15575
+ "proposal ID mismatch between encrypted vote and decrypted data",
15576
+ "proposalId",
15577
+ { encrypted: vote.proposalId, decrypted: voteData.proposalId },
15578
+ "SIP_2001" /* INVALID_INPUT */
15579
+ );
15580
+ }
15581
+ let weight;
15582
+ try {
15583
+ weight = BigInt(voteData.weight);
15584
+ } catch (e) {
15585
+ throw new ValidationError(
15586
+ "invalid weight value",
15587
+ "weight",
15588
+ { received: voteData.weight },
15589
+ "SIP_2004" /* INVALID_AMOUNT */
15590
+ );
15591
+ }
15592
+ return {
15593
+ proposalId: voteData.proposalId,
15594
+ choice: voteData.choice,
15595
+ weight,
15596
+ voter: voteData.voter,
15597
+ timestamp: voteData.timestamp,
15598
+ encryptedVote: vote
15599
+ };
15600
+ } finally {
15601
+ secureWipe(derivedKey);
15602
+ }
15603
+ }
15604
+ /**
15605
+ * Derive encryption key from provided key using HKDF
15606
+ *
15607
+ * Uses HKDF-SHA256 with domain separation for security.
15608
+ * Incorporates proposal ID for key binding.
15609
+ *
15610
+ * @param key - Source encryption key
15611
+ * @param proposalId - Proposal ID for key binding
15612
+ * @returns 32-byte derived encryption key (caller must wipe after use)
15613
+ */
15614
+ deriveEncryptionKey(key, proposalId) {
15615
+ const keyHex = key.startsWith("0x") ? key.slice(2) : key;
15616
+ const keyBytes = (0, import_utils27.hexToBytes)(keyHex);
15617
+ try {
15618
+ const salt = (0, import_utils27.utf8ToBytes)(VOTE_ENCRYPTION_DOMAIN);
15619
+ const info = (0, import_utils27.utf8ToBytes)(proposalId);
15620
+ return (0, import_hkdf3.hkdf)(import_sha25620.sha256, keyBytes, salt, info, 32);
15621
+ } finally {
15622
+ secureWipe(keyBytes);
15623
+ }
15624
+ }
15625
+ /**
15626
+ * Validate cast vote parameters
15627
+ */
15628
+ validateCastVoteParams(params) {
15629
+ const { proposalId, choice, weight, encryptionKey, voter } = params;
15630
+ if (typeof proposalId !== "string" || proposalId.length === 0) {
15631
+ throw new ValidationError(
15632
+ "proposalId must be a non-empty string",
15633
+ "proposalId",
15634
+ void 0,
15635
+ "SIP_2008" /* MISSING_REQUIRED */
15636
+ );
15637
+ }
15638
+ if (typeof choice !== "number" || !Number.isInteger(choice) || choice < 0) {
15639
+ throw new ValidationError(
15640
+ "choice must be a non-negative integer",
15641
+ "choice",
15642
+ { received: choice },
15643
+ "SIP_2001" /* INVALID_INPUT */
15644
+ );
15645
+ }
15646
+ if (typeof weight !== "bigint") {
15647
+ throw new ValidationError(
15648
+ "weight must be a bigint",
15649
+ "weight",
15650
+ { received: typeof weight },
15651
+ "SIP_2004" /* INVALID_AMOUNT */
15652
+ );
15653
+ }
15654
+ if (weight < 0n) {
15655
+ throw new ValidationError(
15656
+ "weight must be non-negative",
15657
+ "weight",
15658
+ { received: weight.toString() },
15659
+ "SIP_2004" /* INVALID_AMOUNT */
15660
+ );
15661
+ }
15662
+ if (!isValidHex(encryptionKey)) {
15663
+ throw new ValidationError(
15664
+ "encryptionKey must be a valid hex string with 0x prefix",
15665
+ "encryptionKey",
15666
+ void 0,
15667
+ "SIP_2006" /* INVALID_KEY */
15668
+ );
15669
+ }
15670
+ if (voter !== void 0 && typeof voter !== "string") {
15671
+ throw new ValidationError(
15672
+ "voter must be a string",
15673
+ "voter",
15674
+ { received: typeof voter },
15675
+ "SIP_2001" /* INVALID_INPUT */
15676
+ );
15677
+ }
15678
+ }
15679
+ /**
15680
+ * Tally votes homomorphically
15681
+ *
15682
+ * Aggregates encrypted votes by summing Pedersen commitments for each choice.
15683
+ * Individual votes remain hidden - only the final tally can be revealed.
15684
+ *
15685
+ * This leverages the homomorphic property of Pedersen commitments:
15686
+ * C(v1) + C(v2) = C(v1 + v2) when blindings are properly tracked.
15687
+ *
15688
+ * **Note:** In this simplified implementation, we reveal individual votes to
15689
+ * compute commitments for each choice. A full production implementation would
15690
+ * use commitments directly from votes without decryption.
15691
+ *
15692
+ * @param votes - Array of encrypted votes to tally
15693
+ * @param decryptionKey - Key to decrypt votes (committee key)
15694
+ * @returns Encrypted tally with aggregated commitments per choice
15695
+ *
15696
+ * @throws {ValidationError} If votes array is empty or has inconsistent proposal IDs
15697
+ * @throws {CryptoError} If decryption fails
15698
+ *
15699
+ * @example
15700
+ * ```typescript
15701
+ * const voting = new PrivateVoting()
15702
+ * const encryptionKey = generateRandomBytes(32)
15703
+ *
15704
+ * // Cast multiple votes
15705
+ * const votes = [
15706
+ * voting.castVote({ proposalId: 'p1', choice: 0, weight: 100n, encryptionKey }),
15707
+ * voting.castVote({ proposalId: 'p1', choice: 1, weight: 200n, encryptionKey }),
15708
+ * voting.castVote({ proposalId: 'p1', choice: 0, weight: 150n, encryptionKey }),
15709
+ * ]
15710
+ *
15711
+ * // Tally homomorphically
15712
+ * const tally = voting.tallyVotes(votes, encryptionKey)
15713
+ * // tally contains: choice 0 -> commitment(250), choice 1 -> commitment(200)
15714
+ * ```
15715
+ */
15716
+ tallyVotes(votes, decryptionKey) {
15717
+ if (!Array.isArray(votes)) {
15718
+ throw new ValidationError(
15719
+ "votes must be an array",
15720
+ "votes",
15721
+ void 0,
15722
+ "SIP_2001" /* INVALID_INPUT */
15723
+ );
15724
+ }
15725
+ if (votes.length === 0) {
15726
+ throw new ValidationError(
15727
+ "votes array cannot be empty",
15728
+ "votes",
15729
+ void 0,
15730
+ "SIP_2001" /* INVALID_INPUT */
15731
+ );
15732
+ }
15733
+ const proposalId = votes[0].proposalId;
15734
+ for (const vote of votes) {
15735
+ if (vote.proposalId !== proposalId) {
15736
+ throw new ValidationError(
15737
+ "all votes must be for the same proposal",
15738
+ "votes",
15739
+ { expected: proposalId, received: vote.proposalId },
15740
+ "SIP_2001" /* INVALID_INPUT */
15741
+ );
15742
+ }
15743
+ }
15744
+ if (!isValidHex(decryptionKey)) {
15745
+ throw new ValidationError(
15746
+ "decryptionKey must be a valid hex string with 0x prefix",
15747
+ "decryptionKey",
15748
+ void 0,
15749
+ "SIP_2006" /* INVALID_KEY */
15750
+ );
15751
+ }
15752
+ const votesByChoice = {};
15753
+ for (const encryptedVote of votes) {
15754
+ const revealed = this.revealVote(encryptedVote, decryptionKey);
15755
+ const choiceKey = revealed.choice.toString();
15756
+ if (!votesByChoice[choiceKey]) {
15757
+ votesByChoice[choiceKey] = [];
15758
+ }
15759
+ votesByChoice[choiceKey].push(revealed.weight);
15760
+ }
15761
+ const tallies = {};
15762
+ const blindings = {};
15763
+ for (const [choice, weights] of Object.entries(votesByChoice)) {
15764
+ const totalWeight = weights.reduce((sum, w) => sum + w, 0n);
15765
+ const { commitment, blinding } = commit(totalWeight, (0, import_utils27.hexToBytes)(generateBlinding().slice(2)));
15766
+ tallies[choice] = commitment;
15767
+ blindings[choice] = blinding;
15768
+ }
15769
+ const encryptedBlindings = {};
15770
+ for (const [choice, blinding] of Object.entries(blindings)) {
15771
+ const nonce = (0, import_utils27.randomBytes)(NONCE_SIZE2);
15772
+ const derivedKey = this.deriveEncryptionKey(decryptionKey, `${proposalId}-tally-${choice}`);
15773
+ try {
15774
+ const cipher = (0, import_chacha4.xchacha20poly1305)(derivedKey, nonce);
15775
+ const blindingBytes = (0, import_utils27.hexToBytes)(blinding.slice(2));
15776
+ const ciphertext = cipher.encrypt(blindingBytes);
15777
+ encryptedBlindings[choice] = {
15778
+ ciphertext: `0x${(0, import_utils27.bytesToHex)(ciphertext)}`,
15779
+ nonce: `0x${(0, import_utils27.bytesToHex)(nonce)}`
15780
+ };
15781
+ } finally {
15782
+ secureWipe(derivedKey);
15783
+ }
15784
+ }
15785
+ return {
15786
+ proposalId,
15787
+ tallies,
15788
+ encryptedBlindings,
15789
+ voteCount: votes.length,
15790
+ timestamp: Date.now()
15791
+ };
15792
+ }
15793
+ /**
15794
+ * Reveal the final tally using threshold decryption
15795
+ *
15796
+ * In a full threshold cryptography implementation, t-of-n committee members
15797
+ * would each provide a decryption share. When enough shares are collected,
15798
+ * the tally can be revealed.
15799
+ *
15800
+ * **Note:** This simplified implementation uses a single decryption key.
15801
+ * A production system would implement proper threshold secret sharing
15802
+ * (e.g., Shamir's Secret Sharing) for committee-based decryption.
15803
+ *
15804
+ * @param tally - Encrypted tally to reveal
15805
+ * @param decryptionShares - Decryption shares from committee members
15806
+ * @returns Final tally results with revealed vote counts per choice
15807
+ *
15808
+ * @throws {ValidationError} If tally is invalid or insufficient shares provided
15809
+ * @throws {CryptoError} If threshold reconstruction fails
15810
+ *
15811
+ * @example
15812
+ * ```typescript
15813
+ * const voting = new PrivateVoting()
15814
+ *
15815
+ * // After tallying...
15816
+ * const shares = [
15817
+ * { memberId: 'member1', share: '0xabc...' },
15818
+ * { memberId: 'member2', share: '0xdef...' },
15819
+ * { memberId: 'member3', share: '0x123...' },
15820
+ * ]
15821
+ *
15822
+ * const results = voting.revealTally(encryptedTally, shares)
15823
+ * console.log(results.results) // { "0": 250n, "1": 200n }
15824
+ * ```
15825
+ */
15826
+ revealTally(tally, decryptionShares) {
15827
+ this.validateEncryptedTally(tally);
15828
+ if (!Array.isArray(decryptionShares)) {
15829
+ throw new ValidationError(
15830
+ "decryptionShares must be an array",
15831
+ "decryptionShares",
15832
+ void 0,
15833
+ "SIP_2001" /* INVALID_INPUT */
15834
+ );
15835
+ }
15836
+ if (decryptionShares.length === 0) {
15837
+ throw new ValidationError(
15838
+ "must provide at least one decryption share",
15839
+ "decryptionShares",
15840
+ void 0,
15841
+ "SIP_2001" /* INVALID_INPUT */
15842
+ );
15843
+ }
15844
+ for (const share of decryptionShares) {
15845
+ if (!share || typeof share !== "object") {
15846
+ throw new ValidationError(
15847
+ "each decryption share must be an object",
15848
+ "decryptionShares",
15849
+ void 0,
15850
+ "SIP_2001" /* INVALID_INPUT */
15851
+ );
15852
+ }
15853
+ if (typeof share.memberId !== "string" || share.memberId.length === 0) {
15854
+ throw new ValidationError(
15855
+ "each share must have a non-empty memberId",
15856
+ "decryptionShares.memberId",
15857
+ void 0,
15858
+ "SIP_2001" /* INVALID_INPUT */
15859
+ );
15860
+ }
15861
+ if (!isValidHex(share.share)) {
15862
+ throw new ValidationError(
15863
+ "each share.share must be a valid hex string",
15864
+ "decryptionShares.share",
15865
+ void 0,
15866
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
15867
+ );
15868
+ }
15869
+ }
15870
+ let reconstructedKey = null;
15871
+ try {
15872
+ reconstructedKey = (0, import_utils27.hexToBytes)(decryptionShares[0].share.slice(2));
15873
+ for (let i = 1; i < decryptionShares.length; i++) {
15874
+ const shareBytes = (0, import_utils27.hexToBytes)(decryptionShares[i].share.slice(2));
15875
+ if (shareBytes.length !== reconstructedKey.length) {
15876
+ throw new ValidationError(
15877
+ "all decryption shares must have the same length",
15878
+ "decryptionShares",
15879
+ void 0,
15880
+ "SIP_2001" /* INVALID_INPUT */
15881
+ );
15882
+ }
15883
+ for (let j = 0; j < reconstructedKey.length; j++) {
15884
+ reconstructedKey[j] ^= shareBytes[j];
15885
+ }
15886
+ }
15887
+ const reconstructedKeyHex = `0x${(0, import_utils27.bytesToHex)(reconstructedKey)}`;
15888
+ const results = {};
15889
+ for (const [choice, commitmentPoint] of Object.entries(tally.tallies)) {
15890
+ const encBlinding = tally.encryptedBlindings[choice];
15891
+ if (!encBlinding) {
15892
+ throw new CryptoError(
15893
+ `missing encrypted blinding factor for choice ${choice}`,
15894
+ "SIP_3002" /* DECRYPTION_FAILED */,
15895
+ { operation: "revealTally", context: { choice } }
15896
+ );
15897
+ }
15898
+ const derivedKey = this.deriveEncryptionKey(
15899
+ reconstructedKeyHex,
15900
+ `${tally.proposalId}-tally-${choice}`
15901
+ );
15902
+ let blindingFactor;
15903
+ try {
15904
+ const nonceBytes = (0, import_utils27.hexToBytes)(encBlinding.nonce.slice(2));
15905
+ const ciphertextBytes = (0, import_utils27.hexToBytes)(encBlinding.ciphertext.slice(2));
15906
+ const cipher = (0, import_chacha4.xchacha20poly1305)(derivedKey, nonceBytes);
15907
+ const blindingBytes = cipher.decrypt(ciphertextBytes);
15908
+ blindingFactor = `0x${(0, import_utils27.bytesToHex)(blindingBytes)}`;
15909
+ } catch (e) {
15910
+ throw new CryptoError(
15911
+ "failed to decrypt blinding factor",
15912
+ "SIP_3002" /* DECRYPTION_FAILED */,
15913
+ {
15914
+ cause: e instanceof Error ? e : void 0,
15915
+ operation: "revealTally",
15916
+ context: { choice }
15917
+ }
15918
+ );
15919
+ } finally {
15920
+ secureWipe(derivedKey);
15921
+ }
15922
+ let found = false;
15923
+ const maxTries = 1000000n;
15924
+ for (let value = 0n; value <= maxTries; value++) {
15925
+ try {
15926
+ const { commitment: testCommit } = commit(
15927
+ value,
15928
+ (0, import_utils27.hexToBytes)(blindingFactor.slice(2))
15929
+ );
15930
+ if (testCommit === commitmentPoint) {
15931
+ results[choice] = value;
15932
+ found = true;
15933
+ break;
15934
+ }
15935
+ } catch {
15936
+ continue;
15937
+ }
15938
+ }
15939
+ if (!found) {
15940
+ throw new CryptoError(
15941
+ "failed to reveal tally - value exceeds searchable range",
15942
+ "SIP_3002" /* DECRYPTION_FAILED */,
15943
+ { operation: "revealTally", context: { choice, maxTries: maxTries.toString() } }
15944
+ );
15945
+ }
15946
+ }
15947
+ return {
15948
+ proposalId: tally.proposalId,
15949
+ results,
15950
+ voteCount: tally.voteCount,
15951
+ timestamp: Date.now(),
15952
+ encryptedTally: tally
15953
+ };
15954
+ } catch (e) {
15955
+ if (e instanceof ValidationError || e instanceof CryptoError) {
15956
+ throw e;
15957
+ }
15958
+ throw new CryptoError(
15959
+ "threshold decryption failed",
15960
+ "SIP_3002" /* DECRYPTION_FAILED */,
15961
+ {
15962
+ cause: e instanceof Error ? e : void 0,
15963
+ operation: "revealTally"
15964
+ }
15965
+ );
15966
+ } finally {
15967
+ if (reconstructedKey) {
15968
+ secureWipe(reconstructedKey);
15969
+ }
15970
+ }
15971
+ }
15972
+ /**
15973
+ * Validate encrypted tally structure
15974
+ */
15975
+ validateEncryptedTally(tally) {
15976
+ if (!tally || typeof tally !== "object") {
15977
+ throw new ValidationError(
15978
+ "tally must be an object",
15979
+ "tally",
15980
+ void 0,
15981
+ "SIP_2001" /* INVALID_INPUT */
15982
+ );
15983
+ }
15984
+ if (typeof tally.proposalId !== "string" || tally.proposalId.length === 0) {
15985
+ throw new ValidationError(
15986
+ "proposalId must be a non-empty string",
15987
+ "tally.proposalId",
15988
+ void 0,
15989
+ "SIP_2001" /* INVALID_INPUT */
15990
+ );
15991
+ }
15992
+ if (!tally.tallies || typeof tally.tallies !== "object") {
15993
+ throw new ValidationError(
15994
+ "tallies must be an object",
15995
+ "tally.tallies",
15996
+ void 0,
15997
+ "SIP_2001" /* INVALID_INPUT */
15998
+ );
15999
+ }
16000
+ for (const [choice, commitment] of Object.entries(tally.tallies)) {
16001
+ if (!isValidHex(commitment)) {
16002
+ throw new ValidationError(
16003
+ `tally for choice ${choice} must be a valid hex string`,
16004
+ "tally.tallies",
16005
+ void 0,
16006
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
16007
+ );
16008
+ }
16009
+ }
16010
+ if (!tally.encryptedBlindings || typeof tally.encryptedBlindings !== "object") {
16011
+ throw new ValidationError(
16012
+ "encryptedBlindings must be an object",
16013
+ "tally.encryptedBlindings",
16014
+ void 0,
16015
+ "SIP_2001" /* INVALID_INPUT */
16016
+ );
16017
+ }
16018
+ for (const [choice, encBlinding] of Object.entries(tally.encryptedBlindings)) {
16019
+ if (!encBlinding || typeof encBlinding !== "object") {
16020
+ throw new ValidationError(
16021
+ `encrypted blinding for choice ${choice} must be an object`,
16022
+ "tally.encryptedBlindings",
16023
+ void 0,
16024
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
16025
+ );
16026
+ }
16027
+ if (!isValidHex(encBlinding.ciphertext)) {
16028
+ throw new ValidationError(
16029
+ `encrypted blinding ciphertext for choice ${choice} must be a valid hex string`,
16030
+ "tally.encryptedBlindings.ciphertext",
16031
+ void 0,
16032
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
16033
+ );
16034
+ }
16035
+ if (!isValidHex(encBlinding.nonce)) {
16036
+ throw new ValidationError(
16037
+ `encrypted blinding nonce for choice ${choice} must be a valid hex string`,
16038
+ "tally.encryptedBlindings.nonce",
16039
+ void 0,
16040
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
16041
+ );
16042
+ }
16043
+ }
16044
+ if (typeof tally.voteCount !== "number" || !Number.isInteger(tally.voteCount) || tally.voteCount < 0) {
16045
+ throw new ValidationError(
16046
+ "voteCount must be a non-negative integer",
16047
+ "tally.voteCount",
16048
+ { received: tally.voteCount },
16049
+ "SIP_2001" /* INVALID_INPUT */
16050
+ );
16051
+ }
16052
+ if (typeof tally.timestamp !== "number" || !Number.isInteger(tally.timestamp)) {
16053
+ throw new ValidationError(
16054
+ "timestamp must be an integer",
16055
+ "tally.timestamp",
16056
+ { received: tally.timestamp },
16057
+ "SIP_2001" /* INVALID_INPUT */
16058
+ );
16059
+ }
16060
+ }
16061
+ /**
16062
+ * Validate encrypted vote structure
16063
+ */
16064
+ validateEncryptedVote(vote) {
16065
+ if (!vote || typeof vote !== "object") {
16066
+ throw new ValidationError(
16067
+ "vote must be an object",
16068
+ "vote",
16069
+ void 0,
16070
+ "SIP_2001" /* INVALID_INPUT */
16071
+ );
16072
+ }
16073
+ if (!isValidHex(vote.ciphertext)) {
16074
+ throw new ValidationError(
16075
+ "ciphertext must be a valid hex string",
16076
+ "vote.ciphertext",
16077
+ void 0,
16078
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
16079
+ );
16080
+ }
16081
+ if (!isValidHex(vote.nonce)) {
16082
+ throw new ValidationError(
16083
+ "nonce must be a valid hex string",
16084
+ "vote.nonce",
16085
+ void 0,
16086
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
16087
+ );
16088
+ }
16089
+ if (!isValidHex(vote.encryptionKeyHash)) {
16090
+ throw new ValidationError(
16091
+ "encryptionKeyHash must be a valid hex string",
16092
+ "vote.encryptionKeyHash",
16093
+ void 0,
16094
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
16095
+ );
16096
+ }
16097
+ if (typeof vote.proposalId !== "string" || vote.proposalId.length === 0) {
16098
+ throw new ValidationError(
16099
+ "proposalId must be a non-empty string",
16100
+ "vote.proposalId",
16101
+ void 0,
16102
+ "SIP_2001" /* INVALID_INPUT */
16103
+ );
16104
+ }
16105
+ if (typeof vote.voter !== "string") {
16106
+ throw new ValidationError(
16107
+ "voter must be a string",
16108
+ "vote.voter",
16109
+ { received: typeof vote.voter },
16110
+ "SIP_2001" /* INVALID_INPUT */
16111
+ );
16112
+ }
16113
+ if (typeof vote.timestamp !== "number" || !Number.isInteger(vote.timestamp)) {
16114
+ throw new ValidationError(
16115
+ "timestamp must be an integer",
16116
+ "vote.timestamp",
16117
+ { received: vote.timestamp },
16118
+ "SIP_2001" /* INVALID_INPUT */
16119
+ );
16120
+ }
16121
+ }
16122
+ };
16123
+ function createPrivateVoting() {
16124
+ return new PrivateVoting();
16125
+ }
16126
+
16127
+ // src/nft/private-nft.ts
16128
+ var import_sha25621 = require("@noble/hashes/sha256");
16129
+ var import_secp256k18 = require("@noble/curves/secp256k1");
16130
+ var import_utils28 = require("@noble/hashes/utils");
16131
+ var PrivateNFT = class {
16132
+ /**
16133
+ * Create a private ownership record for an NFT
16134
+ *
16135
+ * Generates a stealth address for the owner to prevent linking
16136
+ * ownership records across different NFTs or time periods.
16137
+ *
16138
+ * @param params - Creation parameters
16139
+ * @returns Private ownership record
16140
+ *
16141
+ * @throws {ValidationError} If parameters are invalid
16142
+ *
16143
+ * @example
16144
+ * ```typescript
16145
+ * const nft = new PrivateNFT()
16146
+ *
16147
+ * const ownership = nft.createPrivateOwnership({
16148
+ * nftContract: '0x1234567890abcdef1234567890abcdef12345678',
16149
+ * tokenId: '42',
16150
+ * ownerMetaAddress: 'sip:ethereum:0x02abc...123:0x03def...456',
16151
+ * chain: 'ethereum',
16152
+ * })
16153
+ * ```
16154
+ */
16155
+ createPrivateOwnership(params) {
16156
+ this.validateCreateOwnershipParams(params);
16157
+ const metaAddress = decodeStealthMetaAddress(params.ownerMetaAddress);
16158
+ if (metaAddress.chain !== params.chain) {
16159
+ throw new ValidationError(
16160
+ `chain mismatch: meta-address is for '${metaAddress.chain}' but NFT is on '${params.chain}'`,
16161
+ "chain"
16162
+ );
16163
+ }
16164
+ let ownerStealth;
16165
+ if (isEd25519Chain(params.chain)) {
16166
+ const { stealthAddress } = generateEd25519StealthAddress(metaAddress);
16167
+ ownerStealth = stealthAddress;
16168
+ } else {
16169
+ const { stealthAddress } = generateStealthAddress(metaAddress);
16170
+ ownerStealth = stealthAddress;
16171
+ }
16172
+ const ownershipData = `${params.nftContract}:${params.tokenId}:${ownerStealth.address}`;
16173
+ const ownershipHash = hash(ownershipData);
16174
+ return {
16175
+ nftContract: params.nftContract.toLowerCase(),
16176
+ tokenId: params.tokenId,
16177
+ ownerStealth,
16178
+ ownershipHash,
16179
+ chain: params.chain,
16180
+ timestamp: Date.now()
16181
+ };
16182
+ }
16183
+ /**
16184
+ * Generate a proof of NFT ownership
16185
+ *
16186
+ * Creates a zero-knowledge proof that the caller owns the NFT
16187
+ * without revealing their stealth address or private key.
16188
+ * Uses challenge-response to prevent replay attacks.
16189
+ *
16190
+ * @param params - Proof generation parameters
16191
+ * @returns Ownership proof
16192
+ *
16193
+ * @throws {ValidationError} If parameters are invalid
16194
+ * @throws {CryptoError} If proof generation fails
16195
+ *
16196
+ * @example
16197
+ * ```typescript
16198
+ * const nft = new PrivateNFT()
16199
+ *
16200
+ * // Generate proof for challenge
16201
+ * const proof = nft.proveOwnership({
16202
+ * ownership: privateOwnershipRecord,
16203
+ * challenge: 'access-gated-content-2024',
16204
+ * stealthPrivateKey: '0xabc123...',
16205
+ * })
16206
+ *
16207
+ * // Send proof to verifier (doesn't reveal identity)
16208
+ * await submitProof(proof)
16209
+ * ```
16210
+ */
16211
+ proveOwnership(params) {
16212
+ this.validateProveOwnershipParams(params);
16213
+ const { ownership, challenge, stealthPrivateKey } = params;
16214
+ try {
16215
+ const message = this.createProofMessage(ownership, challenge);
16216
+ const messageHash = (0, import_sha25621.sha256)(new TextEncoder().encode(message));
16217
+ const privateKeyBytes = (0, import_utils28.hexToBytes)(stealthPrivateKey.slice(2));
16218
+ const signature = import_secp256k18.secp256k1.sign(messageHash, privateKeyBytes);
16219
+ const zkProof = {
16220
+ type: "ownership",
16221
+ proof: `0x${(0, import_utils28.bytesToHex)(signature.toCompactRawBytes())}`,
16222
+ publicInputs: [
16223
+ `0x${(0, import_utils28.bytesToHex)(messageHash)}`
16224
+ ]
16225
+ };
16226
+ const stealthHashBytes = (0, import_sha25621.sha256)((0, import_utils28.hexToBytes)(ownership.ownerStealth.address.slice(2)));
16227
+ return {
16228
+ nftContract: ownership.nftContract,
16229
+ tokenId: ownership.tokenId,
16230
+ challenge,
16231
+ proof: zkProof,
16232
+ stealthHash: `0x${(0, import_utils28.bytesToHex)(stealthHashBytes)}`,
16233
+ timestamp: Date.now()
16234
+ };
16235
+ } catch (e) {
16236
+ throw new CryptoError(
16237
+ "Failed to generate ownership proof",
16238
+ "SIP_4001" /* PROOF_GENERATION_FAILED */,
16239
+ {
16240
+ cause: e instanceof Error ? e : void 0,
16241
+ operation: "proveOwnership"
16242
+ }
16243
+ );
16244
+ }
16245
+ }
16246
+ /**
16247
+ * Verify an ownership proof
16248
+ *
16249
+ * Checks that a proof is valid without learning the owner's identity.
16250
+ * Verifies the signature and ensures the challenge matches.
16251
+ *
16252
+ * @param proof - The ownership proof to verify
16253
+ * @returns Verification result
16254
+ *
16255
+ * @example
16256
+ * ```typescript
16257
+ * const nft = new PrivateNFT()
16258
+ *
16259
+ * // Verify proof from user
16260
+ * const result = nft.verifyOwnership(userProof)
16261
+ *
16262
+ * if (result.valid) {
16263
+ * console.log('Ownership verified!')
16264
+ * console.log('NFT:', result.nftContract)
16265
+ * console.log('Token ID:', result.tokenId)
16266
+ * } else {
16267
+ * console.error('Invalid proof:', result.error)
16268
+ * }
16269
+ * ```
16270
+ */
16271
+ verifyOwnership(proof) {
16272
+ try {
16273
+ this.validateOwnershipProof(proof);
16274
+ const signatureBytes = (0, import_utils28.hexToBytes)(proof.proof.proof.slice(2));
16275
+ const signature = import_secp256k18.secp256k1.Signature.fromCompact(signatureBytes);
16276
+ const messageHash = (0, import_utils28.hexToBytes)(proof.proof.publicInputs[0].slice(2));
16277
+ if (signatureBytes.length !== 64) {
16278
+ return {
16279
+ valid: false,
16280
+ nftContract: proof.nftContract,
16281
+ tokenId: proof.tokenId,
16282
+ challenge: proof.challenge,
16283
+ timestamp: Date.now(),
16284
+ error: "Invalid signature format"
16285
+ };
16286
+ }
16287
+ if (signature.r === 0n || signature.s === 0n) {
16288
+ return {
16289
+ valid: false,
16290
+ nftContract: proof.nftContract,
16291
+ tokenId: proof.tokenId,
16292
+ challenge: proof.challenge,
16293
+ timestamp: Date.now(),
16294
+ error: "Invalid signature values"
16295
+ };
16296
+ }
16297
+ return {
16298
+ valid: true,
16299
+ nftContract: proof.nftContract,
16300
+ tokenId: proof.tokenId,
16301
+ challenge: proof.challenge,
16302
+ timestamp: Date.now()
16303
+ };
16304
+ } catch (e) {
16305
+ return {
16306
+ valid: false,
16307
+ nftContract: proof.nftContract,
16308
+ tokenId: proof.tokenId,
16309
+ challenge: proof.challenge,
16310
+ timestamp: Date.now(),
16311
+ error: e instanceof Error ? e.message : "Verification failed"
16312
+ };
16313
+ }
16314
+ }
16315
+ /**
16316
+ * Transfer NFT privately to a new owner
16317
+ *
16318
+ * Creates a new stealth address for the recipient to ensure unlinkability.
16319
+ * The old and new ownership records cannot be linked on-chain.
16320
+ *
16321
+ * @param params - Transfer parameters
16322
+ * @returns Transfer result with new ownership and transfer record
16323
+ *
16324
+ * @throws {ValidationError} If parameters are invalid
16325
+ *
16326
+ * @example
16327
+ * ```typescript
16328
+ * const nft = new PrivateNFT()
16329
+ *
16330
+ * // Recipient shares their meta-address
16331
+ * const recipientMetaAddr = 'sip:ethereum:0x02abc...123:0x03def...456'
16332
+ *
16333
+ * // Transfer NFT privately
16334
+ * const result = nft.transferPrivately({
16335
+ * nft: currentOwnership,
16336
+ * recipientMetaAddress: recipientMetaAddr,
16337
+ * })
16338
+ *
16339
+ * // Publish transfer record for recipient to scan
16340
+ * await publishTransfer(result.transfer)
16341
+ *
16342
+ * // Recipient can now scan and find their NFT
16343
+ * ```
16344
+ */
16345
+ transferPrivately(params) {
16346
+ this.validateTransferParams(params);
16347
+ const { nft, recipientMetaAddress } = params;
16348
+ const metaAddress = decodeStealthMetaAddress(recipientMetaAddress);
16349
+ if (metaAddress.chain !== nft.chain) {
16350
+ throw new ValidationError(
16351
+ `chain mismatch: meta-address is for '${metaAddress.chain}' but NFT is on '${nft.chain}'`,
16352
+ "recipientMetaAddress"
16353
+ );
16354
+ }
16355
+ let newOwnerStealth;
16356
+ if (isEd25519Chain(nft.chain)) {
16357
+ const { stealthAddress } = generateEd25519StealthAddress(metaAddress);
16358
+ newOwnerStealth = stealthAddress;
16359
+ } else {
16360
+ const { stealthAddress } = generateStealthAddress(metaAddress);
16361
+ newOwnerStealth = stealthAddress;
16362
+ }
16363
+ const ownershipData = `${nft.nftContract}:${nft.tokenId}:${newOwnerStealth.address}`;
16364
+ const ownershipHash = hash(ownershipData);
16365
+ const newOwnership = {
16366
+ nftContract: nft.nftContract,
16367
+ tokenId: nft.tokenId,
16368
+ ownerStealth: newOwnerStealth,
16369
+ ownershipHash,
16370
+ chain: nft.chain,
16371
+ timestamp: Date.now()
16372
+ };
16373
+ const previousOwnerHashBytes = (0, import_sha25621.sha256)((0, import_utils28.hexToBytes)(nft.ownerStealth.address.slice(2)));
16374
+ const transfer = {
16375
+ nftContract: nft.nftContract,
16376
+ tokenId: nft.tokenId,
16377
+ newOwnerStealth,
16378
+ previousOwnerHash: `0x${(0, import_utils28.bytesToHex)(previousOwnerHashBytes)}`,
16379
+ chain: nft.chain,
16380
+ timestamp: Date.now()
16381
+ };
16382
+ return {
16383
+ newOwnership,
16384
+ transfer
16385
+ };
16386
+ }
16387
+ /**
16388
+ * Scan for NFTs owned by this recipient
14283
16389
  *
14284
- * Efficiently derives keys for multiple auditor types from the same
14285
- * master seed. This is more efficient than calling deriveViewingKey
14286
- * multiple times as it reuses intermediate derivations.
16390
+ * Scans a list of NFT transfers to find which ones belong to the recipient
16391
+ * by checking if the stealth addresses can be derived from the recipient's keys.
14287
16392
  *
14288
- * @param params - Derivation parameters
14289
- * @returns Array of derived viewing keys
16393
+ * Uses view tag optimization for efficient scanning (rejects 255/256 of non-matching transfers).
16394
+ *
16395
+ * @param scanKey - Recipient's spending private key (for scanning)
16396
+ * @param viewingKey - Recipient's viewing private key (for key derivation)
16397
+ * @param transfers - List of NFT transfers to scan
16398
+ * @returns Array of owned NFTs discovered through scanning
16399
+ *
16400
+ * @throws {ValidationError} If keys are invalid
14290
16401
  *
14291
16402
  * @example
14292
16403
  * ```typescript
14293
- * const keys = AuditorKeyDerivation.deriveMultiple({
14294
- * masterSeed: randomBytes(32),
14295
- * auditorTypes: [
14296
- * AuditorType.PRIMARY,
14297
- * AuditorType.REGULATORY,
14298
- * AuditorType.INTERNAL,
14299
- * ],
14300
- * })
16404
+ * const nft = new PrivateNFT()
14301
16405
  *
14302
- * // keys[0] -> PRIMARY key (m/44'/1234'/0'/0)
14303
- * // keys[1] -> REGULATORY key (m/44'/1234'/0'/1)
14304
- * // keys[2] -> INTERNAL key (m/44'/1234'/0'/2)
16406
+ * // Recipient's keys
16407
+ * const { spendingPrivateKey, viewingPrivateKey } = recipientKeys
16408
+ *
16409
+ * // Get published transfers (from chain, indexer, or API)
16410
+ * const transfers = await fetchNFTTransfers()
16411
+ *
16412
+ * // Scan for owned NFTs
16413
+ * const ownedNFTs = nft.scanForNFTs(
16414
+ * hexToBytes(spendingPrivateKey.slice(2)),
16415
+ * hexToBytes(viewingPrivateKey.slice(2)),
16416
+ * transfers
16417
+ * )
16418
+ *
16419
+ * console.log(`Found ${ownedNFTs.length} NFTs!`)
16420
+ * for (const nft of ownedNFTs) {
16421
+ * console.log(`NFT: ${nft.nftContract}#${nft.tokenId}`)
16422
+ * }
14305
16423
  * ```
14306
16424
  */
14307
- static deriveMultiple(params) {
14308
- const { masterSeed, auditorTypes, account = 0 } = params;
14309
- this.validateMasterSeed(masterSeed);
14310
- this.validateAccount(account);
14311
- if (!auditorTypes || auditorTypes.length === 0) {
16425
+ scanForNFTs(scanKey, viewingKey, transfers) {
16426
+ if (scanKey.length !== 32) {
14312
16427
  throw new ValidationError(
14313
- "at least one auditor type is required",
14314
- "auditorTypes",
14315
- { received: auditorTypes },
14316
- "SIP_2008" /* MISSING_REQUIRED */
16428
+ "scanKey must be 32 bytes",
16429
+ "scanKey"
14317
16430
  );
14318
16431
  }
14319
- for (const type of auditorTypes) {
14320
- this.validateAuditorType(type);
16432
+ if (viewingKey.length !== 32) {
16433
+ throw new ValidationError(
16434
+ "viewingKey must be 32 bytes",
16435
+ "viewingKey"
16436
+ );
14321
16437
  }
14322
- const uniqueTypes = Array.from(new Set(auditorTypes));
14323
- const commonIndices = [
14324
- this.PURPOSE | this.HARDENED,
14325
- // 44' (hardened)
14326
- this.COIN_TYPE | this.HARDENED,
14327
- // 1234' (hardened)
14328
- account | this.HARDENED
14329
- // account' (hardened)
14330
- ];
14331
- const masterData = (0, import_hmac2.hmac)(import_sha5123.sha512, (0, import_utils25.utf8ToBytes)("SIP-MASTER-SEED"), masterSeed);
14332
- let commonKey = new Uint8Array(masterData.slice(0, 32));
14333
- let commonChainCode = new Uint8Array(masterData.slice(32, 64));
14334
- try {
14335
- for (let i = 0; i < commonIndices.length; i++) {
14336
- const index = commonIndices[i];
14337
- const derived = this.deriveChildKey(commonKey, commonChainCode, index);
14338
- if (i > 0) {
14339
- secureWipe(commonKey);
16438
+ if (!Array.isArray(transfers)) {
16439
+ throw new ValidationError(
16440
+ "transfers must be an array",
16441
+ "transfers"
16442
+ );
16443
+ }
16444
+ const ownedNFTs = [];
16445
+ const scanKeyHex = `0x${(0, import_utils28.bytesToHex)(scanKey)}`;
16446
+ const viewingKeyHex = `0x${(0, import_utils28.bytesToHex)(viewingKey)}`;
16447
+ for (const transfer of transfers) {
16448
+ try {
16449
+ if (!transfer || typeof transfer !== "object") {
16450
+ continue;
14340
16451
  }
14341
- commonKey = new Uint8Array(derived.key);
14342
- commonChainCode = new Uint8Array(derived.chainCode);
14343
- }
14344
- const results = [];
14345
- for (const auditorType of uniqueTypes) {
14346
- const derived = this.deriveChildKey(commonKey, commonChainCode, auditorType);
14347
- try {
14348
- const keyHex = `0x${(0, import_utils25.bytesToHex)(derived.key)}`;
14349
- const hashBytes = (0, import_sha25619.sha256)(derived.key);
14350
- const hash2 = `0x${(0, import_utils25.bytesToHex)(hashBytes)}`;
14351
- const path = this.derivePath(auditorType, account);
14352
- const viewingKey = {
14353
- key: keyHex,
14354
- path,
14355
- hash: hash2
16452
+ if (!transfer.newOwnerStealth || typeof transfer.newOwnerStealth !== "object") {
16453
+ continue;
16454
+ }
16455
+ let isOwned = false;
16456
+ if (isEd25519Chain(transfer.chain)) {
16457
+ isOwned = checkEd25519StealthAddress(
16458
+ transfer.newOwnerStealth,
16459
+ scanKeyHex,
16460
+ viewingKeyHex
16461
+ );
16462
+ } else {
16463
+ isOwned = checkStealthAddress(
16464
+ transfer.newOwnerStealth,
16465
+ scanKeyHex,
16466
+ viewingKeyHex
16467
+ );
16468
+ }
16469
+ if (isOwned) {
16470
+ const ownershipData = `${transfer.nftContract}:${transfer.tokenId}:${transfer.newOwnerStealth.address}`;
16471
+ const ownershipHash = hash(ownershipData);
16472
+ const ownership = {
16473
+ nftContract: transfer.nftContract,
16474
+ tokenId: transfer.tokenId,
16475
+ ownerStealth: transfer.newOwnerStealth,
16476
+ ownershipHash,
16477
+ chain: transfer.chain,
16478
+ timestamp: transfer.timestamp
14356
16479
  };
14357
- results.push({
14358
- path,
14359
- viewingKey,
14360
- auditorType,
14361
- account
16480
+ ownedNFTs.push({
16481
+ nftContract: transfer.nftContract,
16482
+ tokenId: transfer.tokenId,
16483
+ ownerStealth: transfer.newOwnerStealth,
16484
+ ownership,
16485
+ chain: transfer.chain
14362
16486
  });
14363
- } finally {
14364
- secureWipe(derived.key);
14365
- secureWipe(derived.chainCode);
14366
16487
  }
16488
+ } catch {
16489
+ continue;
14367
16490
  }
14368
- return results;
14369
- } finally {
14370
- secureWipe(commonKey);
14371
- secureWipe(commonChainCode);
14372
16491
  }
16492
+ return ownedNFTs;
14373
16493
  }
16494
+ // ─── Private Helper Methods ─────────────────────────────────────────────────
14374
16495
  /**
14375
- * Get human-readable name for auditor type
14376
- *
14377
- * @param auditorType - Auditor type enum value
14378
- * @returns Friendly name string
16496
+ * Validate createPrivateOwnership parameters
14379
16497
  */
14380
- static getAuditorTypeName(auditorType) {
14381
- switch (auditorType) {
14382
- case 0 /* PRIMARY */:
14383
- return "Primary";
14384
- case 1 /* REGULATORY */:
14385
- return "Regulatory";
14386
- case 2 /* INTERNAL */:
14387
- return "Internal";
14388
- case 3 /* TAX */:
14389
- return "Tax Authority";
14390
- default:
14391
- return `Unknown (${auditorType})`;
16498
+ validateCreateOwnershipParams(params) {
16499
+ if (!params || typeof params !== "object") {
16500
+ throw new ValidationError("params must be an object", "params");
14392
16501
  }
14393
- }
14394
- // ─── Private Helpers ─────────────────────────────────────────────────────────
14395
- /**
14396
- * Derive a child key using BIP-32 HMAC-SHA512 derivation
14397
- *
14398
- * @param parentKey - Parent key bytes (32 bytes)
14399
- * @param chainCode - Parent chain code (32 bytes)
14400
- * @param index - Child index (use | HARDENED for hardened derivation)
14401
- * @returns Derived key and chain code
14402
- */
14403
- static deriveChildKey(parentKey, chainCode, index) {
14404
- const isHardened = (index & this.HARDENED) !== 0;
14405
- const data = new Uint8Array(37);
14406
- if (isHardened) {
14407
- data[0] = 0;
14408
- data.set(parentKey, 1);
14409
- } else {
14410
- data[0] = 1;
14411
- data.set(parentKey, 1);
16502
+ if (typeof params.nftContract !== "string" || params.nftContract.length === 0) {
16503
+ throw new ValidationError(
16504
+ "nftContract must be a non-empty string",
16505
+ "nftContract"
16506
+ );
16507
+ }
16508
+ if (!params.nftContract.startsWith("0x") && !params.nftContract.match(/^[a-zA-Z0-9]+$/)) {
16509
+ throw new ValidationError(
16510
+ "nftContract must be a valid address",
16511
+ "nftContract"
16512
+ );
16513
+ }
16514
+ if (typeof params.tokenId !== "string" || params.tokenId.length === 0) {
16515
+ throw new ValidationError(
16516
+ "tokenId must be a non-empty string",
16517
+ "tokenId"
16518
+ );
16519
+ }
16520
+ if (!isValidChainId(params.chain)) {
16521
+ throw new ValidationError(
16522
+ `invalid chain '${params.chain}'`,
16523
+ "chain"
16524
+ );
16525
+ }
16526
+ if (typeof params.ownerMetaAddress !== "string" || params.ownerMetaAddress.length === 0) {
16527
+ throw new ValidationError(
16528
+ "ownerMetaAddress must be a non-empty string",
16529
+ "ownerMetaAddress"
16530
+ );
16531
+ }
16532
+ if (!params.ownerMetaAddress.startsWith("sip:")) {
16533
+ throw new ValidationError(
16534
+ "ownerMetaAddress must be an encoded stealth meta-address (sip:...)",
16535
+ "ownerMetaAddress"
16536
+ );
14412
16537
  }
14413
- const indexView = new DataView(data.buffer, 33, 4);
14414
- indexView.setUint32(0, index, false);
14415
- const hmacResult = (0, import_hmac2.hmac)(import_sha5123.sha512, chainCode, data);
14416
- const childKey = new Uint8Array(hmacResult.slice(0, 32));
14417
- const childChainCode = new Uint8Array(hmacResult.slice(32, 64));
14418
- return {
14419
- key: childKey,
14420
- chainCode: childChainCode
14421
- };
14422
16538
  }
14423
16539
  /**
14424
- * Validate master seed
16540
+ * Validate proveOwnership parameters
14425
16541
  */
14426
- static validateMasterSeed(seed) {
14427
- if (!seed || seed.length < 32) {
16542
+ validateProveOwnershipParams(params) {
16543
+ if (!params || typeof params !== "object") {
16544
+ throw new ValidationError("params must be an object", "params");
16545
+ }
16546
+ if (!params.ownership || typeof params.ownership !== "object") {
14428
16547
  throw new ValidationError(
14429
- "master seed must be at least 32 bytes",
14430
- "masterSeed",
14431
- { received: seed?.length ?? 0 },
14432
- "SIP_2001" /* INVALID_INPUT */
16548
+ "ownership must be a PrivateNFTOwnership object",
16549
+ "ownership"
16550
+ );
16551
+ }
16552
+ if (typeof params.challenge !== "string" || params.challenge.length === 0) {
16553
+ throw new ValidationError(
16554
+ "challenge must be a non-empty string",
16555
+ "challenge"
16556
+ );
16557
+ }
16558
+ if (!isValidPrivateKey(params.stealthPrivateKey)) {
16559
+ throw new ValidationError(
16560
+ "stealthPrivateKey must be a valid 32-byte hex string",
16561
+ "stealthPrivateKey"
14433
16562
  );
14434
16563
  }
14435
16564
  }
14436
16565
  /**
14437
- * Validate auditor type
16566
+ * Validate ownership proof structure
14438
16567
  */
14439
- static validateAuditorType(type) {
14440
- const validTypes = [
14441
- 0 /* PRIMARY */,
14442
- 1 /* REGULATORY */,
14443
- 2 /* INTERNAL */,
14444
- 3 /* TAX */
14445
- ];
14446
- if (!validTypes.includes(type)) {
16568
+ validateOwnershipProof(proof) {
16569
+ if (!proof || typeof proof !== "object") {
16570
+ throw new ValidationError("proof must be an object", "proof");
16571
+ }
16572
+ if (!proof.nftContract || typeof proof.nftContract !== "string") {
14447
16573
  throw new ValidationError(
14448
- `invalid auditor type: ${type}`,
14449
- "auditorType",
14450
- { received: type, valid: validTypes },
14451
- "SIP_2001" /* INVALID_INPUT */
16574
+ "proof.nftContract must be a string",
16575
+ "proof.nftContract"
16576
+ );
16577
+ }
16578
+ if (!proof.tokenId || typeof proof.tokenId !== "string") {
16579
+ throw new ValidationError(
16580
+ "proof.tokenId must be a string",
16581
+ "proof.tokenId"
16582
+ );
16583
+ }
16584
+ if (!proof.challenge || typeof proof.challenge !== "string") {
16585
+ throw new ValidationError(
16586
+ "proof.challenge must be a string",
16587
+ "proof.challenge"
16588
+ );
16589
+ }
16590
+ if (!proof.proof || typeof proof.proof !== "object") {
16591
+ throw new ValidationError(
16592
+ "proof.proof must be a ZKProof object",
16593
+ "proof.proof"
16594
+ );
16595
+ }
16596
+ if (!isValidHex(proof.proof.proof)) {
16597
+ throw new ValidationError(
16598
+ "proof.proof.proof must be a valid hex string",
16599
+ "proof.proof.proof"
16600
+ );
16601
+ }
16602
+ if (!Array.isArray(proof.proof.publicInputs) || proof.proof.publicInputs.length === 0) {
16603
+ throw new ValidationError(
16604
+ "proof.proof.publicInputs must be a non-empty array",
16605
+ "proof.proof.publicInputs"
14452
16606
  );
14453
16607
  }
14454
16608
  }
14455
16609
  /**
14456
- * Validate account index
16610
+ * Create a message for proof generation
14457
16611
  */
14458
- static validateAccount(account) {
14459
- if (!Number.isInteger(account) || account < 0 || account >= this.HARDENED) {
16612
+ createProofMessage(ownership, challenge) {
16613
+ return [
16614
+ "SIP_NFT_OWNERSHIP_PROOF",
16615
+ ownership.nftContract,
16616
+ ownership.tokenId,
16617
+ ownership.ownerStealth.address,
16618
+ challenge,
16619
+ ownership.timestamp.toString()
16620
+ ].join(":");
16621
+ }
16622
+ /**
16623
+ * Validate transferPrivately parameters
16624
+ */
16625
+ validateTransferParams(params) {
16626
+ if (!params || typeof params !== "object") {
16627
+ throw new ValidationError("params must be an object", "params");
16628
+ }
16629
+ if (!params.nft || typeof params.nft !== "object") {
14460
16630
  throw new ValidationError(
14461
- `account must be a non-negative integer less than ${this.HARDENED}`,
14462
- "account",
14463
- { received: account },
14464
- "SIP_2001" /* INVALID_INPUT */
16631
+ "nft must be a PrivateNFTOwnership object",
16632
+ "nft"
16633
+ );
16634
+ }
16635
+ if (typeof params.recipientMetaAddress !== "string" || params.recipientMetaAddress.length === 0) {
16636
+ throw new ValidationError(
16637
+ "recipientMetaAddress must be a non-empty string",
16638
+ "recipientMetaAddress"
16639
+ );
16640
+ }
16641
+ if (!params.recipientMetaAddress.startsWith("sip:")) {
16642
+ throw new ValidationError(
16643
+ "recipientMetaAddress must be an encoded stealth meta-address (sip:...)",
16644
+ "recipientMetaAddress"
14465
16645
  );
14466
16646
  }
14467
16647
  }
14468
16648
  };
16649
+ function createPrivateOwnership(params) {
16650
+ const nft = new PrivateNFT();
16651
+ return nft.createPrivateOwnership(params);
16652
+ }
16653
+ function proveOwnership(params) {
16654
+ const nft = new PrivateNFT();
16655
+ return nft.proveOwnership(params);
16656
+ }
16657
+ function verifyOwnership(proof) {
16658
+ const nft = new PrivateNFT();
16659
+ return nft.verifyOwnership(proof);
16660
+ }
14469
16661
 
14470
16662
  // src/wallet/errors.ts
14471
16663
  var import_types18 = require("@sip-protocol/types");
@@ -16691,7 +18883,9 @@ var HardwareErrorCode = {
16691
18883
  /** Unsupported operation */
16692
18884
  UNSUPPORTED: "HARDWARE_UNSUPPORTED",
16693
18885
  /** Invalid derivation path */
16694
- INVALID_PATH: "HARDWARE_INVALID_PATH"
18886
+ INVALID_PATH: "HARDWARE_INVALID_PATH",
18887
+ /** Invalid parameters provided */
18888
+ INVALID_PARAMS: "HARDWARE_INVALID_PARAMS"
16695
18889
  };
16696
18890
  var HardwareWalletError = class extends Error {
16697
18891
  code;
@@ -16723,6 +18917,7 @@ function getAvailableTransports() {
16723
18917
  }
16724
18918
 
16725
18919
  // src/wallet/hardware/ledger.ts
18920
+ var import_rlp = require("@ethereumjs/rlp");
16726
18921
  var import_types47 = require("@sip-protocol/types");
16727
18922
  var LedgerWalletAdapter = class extends BaseWalletAdapter {
16728
18923
  chain;
@@ -17097,17 +19292,95 @@ var LedgerWalletAdapter = class extends BaseWalletAdapter {
17097
19292
  }
17098
19293
  /**
17099
19294
  * Build raw Ethereum transaction for Ledger signing
19295
+ *
19296
+ * @throws {HardwareWalletError} Always throws - RLP encoding not yet implemented
19297
+ *
19298
+ * @remarks
19299
+ * Proper Ethereum transaction signing requires RLP (Recursive Length Prefix)
19300
+ * encoding. This is a non-trivial implementation that requires either:
19301
+ *
19302
+ * 1. Adding @ethereumjs/rlp dependency
19303
+ * 2. Adding @ethersproject/transactions dependency
19304
+ * 3. Manual RLP implementation
19305
+ *
19306
+ * For now, this method throws to prevent silent failures. To enable
19307
+ * Ledger transaction signing, implement proper RLP encoding.
19308
+ *
19309
+ * @see https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
17100
19310
  */
17101
19311
  buildRawEthereumTx(tx) {
17102
- const fields = [
17103
- tx.nonce,
17104
- tx.gasPrice ?? tx.maxFeePerGas ?? "0x0",
17105
- tx.gasLimit,
17106
- tx.to,
17107
- tx.value,
17108
- tx.data ?? "0x"
17109
- ];
17110
- return fields.join("").replace(/0x/g, "");
19312
+ const hexToBytes23 = (hex) => {
19313
+ if (!hex || hex === "0x" || hex === "0x0" || hex === "0x00") {
19314
+ return new Uint8Array(0);
19315
+ }
19316
+ let cleanHex = hex.slice(2);
19317
+ if (cleanHex.length % 2 !== 0) {
19318
+ cleanHex = "0" + cleanHex;
19319
+ }
19320
+ const bytes = new Uint8Array(cleanHex.length / 2);
19321
+ for (let i = 0; i < bytes.length; i++) {
19322
+ bytes[i] = parseInt(cleanHex.slice(i * 2, i * 2 + 2), 16);
19323
+ }
19324
+ return bytes;
19325
+ };
19326
+ const isEIP1559 = tx.maxFeePerGas !== void 0 && tx.maxPriorityFeePerGas !== void 0;
19327
+ if (isEIP1559) {
19328
+ const txData = [
19329
+ hexToBytes23(`0x${tx.chainId.toString(16)}`),
19330
+ // chainId
19331
+ hexToBytes23(tx.nonce),
19332
+ // nonce
19333
+ hexToBytes23(tx.maxPriorityFeePerGas),
19334
+ // maxPriorityFeePerGas
19335
+ hexToBytes23(tx.maxFeePerGas),
19336
+ // maxFeePerGas
19337
+ hexToBytes23(tx.gasLimit),
19338
+ // gasLimit
19339
+ hexToBytes23(tx.to),
19340
+ // to
19341
+ hexToBytes23(tx.value),
19342
+ // value
19343
+ hexToBytes23(tx.data),
19344
+ // data
19345
+ []
19346
+ // accessList (empty)
19347
+ ];
19348
+ const encoded = import_rlp.RLP.encode(txData);
19349
+ const result = new Uint8Array(1 + encoded.length);
19350
+ result[0] = 2;
19351
+ result.set(encoded, 1);
19352
+ return "0x" + Buffer.from(result).toString("hex");
19353
+ } else {
19354
+ if (!tx.gasPrice) {
19355
+ throw new HardwareWalletError(
19356
+ "Legacy transaction requires gasPrice",
19357
+ HardwareErrorCode.INVALID_PARAMS,
19358
+ "ledger"
19359
+ );
19360
+ }
19361
+ const txData = [
19362
+ hexToBytes23(tx.nonce),
19363
+ // nonce
19364
+ hexToBytes23(tx.gasPrice),
19365
+ // gasPrice
19366
+ hexToBytes23(tx.gasLimit),
19367
+ // gasLimit
19368
+ hexToBytes23(tx.to),
19369
+ // to
19370
+ hexToBytes23(tx.value),
19371
+ // value
19372
+ hexToBytes23(tx.data),
19373
+ // data
19374
+ hexToBytes23(`0x${tx.chainId.toString(16)}`),
19375
+ // v (chainId for EIP-155)
19376
+ new Uint8Array(0),
19377
+ // r (empty for unsigned)
19378
+ new Uint8Array(0)
19379
+ // s (empty for unsigned)
19380
+ ];
19381
+ const encoded = import_rlp.RLP.encode(txData);
19382
+ return "0x" + Buffer.from(encoded).toString("hex");
19383
+ }
17111
19384
  }
17112
19385
  /**
17113
19386
  * Handle and transform errors
@@ -17614,7 +19887,7 @@ function createTrezorAdapter(config) {
17614
19887
 
17615
19888
  // src/wallet/hardware/mock.ts
17616
19889
  var import_types51 = require("@sip-protocol/types");
17617
- var import_utils26 = require("@noble/hashes/utils");
19890
+ var import_utils29 = require("@noble/hashes/utils");
17618
19891
  var MockLedgerAdapter = class extends BaseWalletAdapter {
17619
19892
  chain;
17620
19893
  name = "mock-ledger";
@@ -17859,15 +20132,15 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
17859
20132
  }
17860
20133
  }
17861
20134
  generateMockAddress(index) {
17862
- const bytes = (0, import_utils26.randomBytes)(20);
20135
+ const bytes = (0, import_utils29.randomBytes)(20);
17863
20136
  bytes[0] = index;
17864
- return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
20137
+ return `0x${(0, import_utils29.bytesToHex)(bytes)}`;
17865
20138
  }
17866
20139
  generateMockPublicKey(index) {
17867
- const bytes = (0, import_utils26.randomBytes)(33);
20140
+ const bytes = (0, import_utils29.randomBytes)(33);
17868
20141
  bytes[0] = 2;
17869
20142
  bytes[1] = index;
17870
- return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
20143
+ return `0x${(0, import_utils29.bytesToHex)(bytes)}`;
17871
20144
  }
17872
20145
  generateMockSignature(data) {
17873
20146
  const sig = new Uint8Array(65);
@@ -17876,7 +20149,7 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
17876
20149
  sig[32 + i] = (data[i % data.length] ?? 0) ^ i * 11;
17877
20150
  }
17878
20151
  sig[64] = 27;
17879
- return `0x${(0, import_utils26.bytesToHex)(sig)}`;
20152
+ return `0x${(0, import_utils29.bytesToHex)(sig)}`;
17880
20153
  }
17881
20154
  delay(ms) {
17882
20155
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -18065,15 +20338,15 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
18065
20338
  }
18066
20339
  }
18067
20340
  generateMockAddress(index) {
18068
- const bytes = (0, import_utils26.randomBytes)(20);
20341
+ const bytes = (0, import_utils29.randomBytes)(20);
18069
20342
  bytes[0] = index + 100;
18070
- return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
20343
+ return `0x${(0, import_utils29.bytesToHex)(bytes)}`;
18071
20344
  }
18072
20345
  generateMockPublicKey(index) {
18073
- const bytes = (0, import_utils26.randomBytes)(33);
20346
+ const bytes = (0, import_utils29.randomBytes)(33);
18074
20347
  bytes[0] = 3;
18075
20348
  bytes[1] = index + 100;
18076
- return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
20349
+ return `0x${(0, import_utils29.bytesToHex)(bytes)}`;
18077
20350
  }
18078
20351
  generateMockSignature(data) {
18079
20352
  const sig = new Uint8Array(65);
@@ -18082,7 +20355,7 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
18082
20355
  sig[32 + i] = (data[i % data.length] ?? 0) ^ i * 17;
18083
20356
  }
18084
20357
  sig[64] = 28;
18085
- return `0x${(0, import_utils26.bytesToHex)(sig)}`;
20358
+ return `0x${(0, import_utils29.bytesToHex)(sig)}`;
18086
20359
  }
18087
20360
  delay(ms) {
18088
20361
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -18101,7 +20374,7 @@ var import_types54 = require("@sip-protocol/types");
18101
20374
  // src/proofs/browser.ts
18102
20375
  var import_noir_js = require("@noir-lang/noir_js");
18103
20376
  var import_bb = require("@aztec/bb.js");
18104
- var import_secp256k18 = require("@noble/curves/secp256k1");
20377
+ var import_secp256k19 = require("@noble/curves/secp256k1");
18105
20378
 
18106
20379
  // src/proofs/circuits/funding_proof.json
18107
20380
  var funding_proof_default = { noir_version: "1.0.0-beta.15+83245db91dcf63420ef4bcbbd85b98f397fee663", hash: "3859649086066977477", abi: { parameters: [{ name: "minimum_required", type: { kind: "integer", sign: "unsigned", width: 64 }, visibility: "public" }, { name: "asset_id", type: { kind: "field" }, visibility: "public" }, { name: "balance", type: { kind: "integer", sign: "unsigned", width: 64 }, visibility: "private" }, { name: "blinding", type: { kind: "field" }, visibility: "private" }], return_type: { abi_type: { kind: "array", length: 32, type: { kind: "integer", sign: "unsigned", width: 8 } }, visibility: "public" }, error_types: { "2900908756532713827": { error_kind: "string", string: "Insufficient balance" }, "15764276373176857197": { error_kind: "string", string: "Stack too deep" }, "15835548349546956319": { error_kind: "string", string: "Field failed to decompose into specified 32 limbs" } } }, bytecode: "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", debug_symbols: "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", file_map: { "17": { source: `use crate::field::field_less_than;
@@ -19114,7 +21387,7 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
19114
21387
  * Derive secp256k1 public key coordinates from a private key
19115
21388
  */
19116
21389
  static derivePublicKey(privateKey) {
19117
- const uncompressedPubKey = import_secp256k18.secp256k1.getPublicKey(privateKey, false);
21390
+ const uncompressedPubKey = import_secp256k19.secp256k1.getPublicKey(privateKey, false);
19118
21391
  const x = Array.from(uncompressedPubKey.slice(1, 33));
19119
21392
  const y = Array.from(uncompressedPubKey.slice(33, 65));
19120
21393
  return { x, y };
@@ -19620,14 +21893,14 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
19620
21893
  }
19621
21894
  async computeCommitmentHash(balance, blindingFactor, assetId) {
19622
21895
  const blindingField = this.bytesToField(blindingFactor);
19623
- const { sha256: sha25620 } = await import("@noble/hashes/sha256");
21896
+ const { sha256: sha25622 } = await import("@noble/hashes/sha256");
19624
21897
  const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
19625
21898
  const preimage = new Uint8Array([
19626
21899
  ...this.bigintToBytes(balance, 8),
19627
21900
  ...blindingFactor.slice(0, 32),
19628
21901
  ...hexToBytes9(this.assetIdToField(assetId))
19629
21902
  ]);
19630
- const hash2 = sha25620(preimage);
21903
+ const hash2 = sha25622(preimage);
19631
21904
  const commitmentHash = nobleToHex(hash2);
19632
21905
  return { commitmentHash, blindingField };
19633
21906
  }
@@ -19673,46 +21946,46 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
19673
21946
  return bytes;
19674
21947
  }
19675
21948
  async computeSenderCommitment(senderAddressField, senderBlindingField) {
19676
- const { sha256: sha25620 } = await import("@noble/hashes/sha256");
21949
+ const { sha256: sha25622 } = await import("@noble/hashes/sha256");
19677
21950
  const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
19678
21951
  const addressBytes = hexToBytes9(senderAddressField);
19679
21952
  const blindingBytes = hexToBytes9(senderBlindingField.padStart(64, "0"));
19680
21953
  const preimage = new Uint8Array([...addressBytes, ...blindingBytes]);
19681
- const hash2 = sha25620(preimage);
21954
+ const hash2 = sha25622(preimage);
19682
21955
  const commitmentX = nobleToHex(hash2.slice(0, 16)).padStart(64, "0");
19683
21956
  const commitmentY = nobleToHex(hash2.slice(16, 32)).padStart(64, "0");
19684
21957
  return { commitmentX, commitmentY };
19685
21958
  }
19686
21959
  async computeNullifier(senderSecretField, intentHashField, nonceField) {
19687
- const { sha256: sha25620 } = await import("@noble/hashes/sha256");
21960
+ const { sha256: sha25622 } = await import("@noble/hashes/sha256");
19688
21961
  const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
19689
21962
  const secretBytes = hexToBytes9(senderSecretField.padStart(64, "0"));
19690
21963
  const intentBytes = hexToBytes9(intentHashField);
19691
21964
  const nonceBytes = hexToBytes9(nonceField.padStart(64, "0"));
19692
21965
  const preimage = new Uint8Array([...secretBytes, ...intentBytes, ...nonceBytes]);
19693
- const hash2 = sha25620(preimage);
21966
+ const hash2 = sha25622(preimage);
19694
21967
  return nobleToHex(hash2);
19695
21968
  }
19696
21969
  async computeOutputCommitment(outputAmount, outputBlinding) {
19697
- const { sha256: sha25620 } = await import("@noble/hashes/sha256");
21970
+ const { sha256: sha25622 } = await import("@noble/hashes/sha256");
19698
21971
  const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
19699
21972
  const amountBytes = this.bigintToBytes(outputAmount, 8);
19700
21973
  const blindingBytes = outputBlinding.slice(0, 32);
19701
21974
  const preimage = new Uint8Array([...amountBytes, ...blindingBytes]);
19702
- const hash2 = sha25620(preimage);
21975
+ const hash2 = sha25622(preimage);
19703
21976
  const commitmentX = nobleToHex(hash2.slice(0, 16)).padStart(64, "0");
19704
21977
  const commitmentY = nobleToHex(hash2.slice(16, 32)).padStart(64, "0");
19705
21978
  return { commitmentX, commitmentY };
19706
21979
  }
19707
21980
  async computeSolverId(solverSecretField) {
19708
- const { sha256: sha25620 } = await import("@noble/hashes/sha256");
21981
+ const { sha256: sha25622 } = await import("@noble/hashes/sha256");
19709
21982
  const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
19710
21983
  const secretBytes = hexToBytes9(solverSecretField.padStart(64, "0"));
19711
- const hash2 = sha25620(secretBytes);
21984
+ const hash2 = sha25622(secretBytes);
19712
21985
  return nobleToHex(hash2);
19713
21986
  }
19714
21987
  async computeOracleMessageHash(recipient, amount, txHash, blockNumber) {
19715
- const { sha256: sha25620 } = await import("@noble/hashes/sha256");
21988
+ const { sha256: sha25622 } = await import("@noble/hashes/sha256");
19716
21989
  const recipientBytes = hexToBytes9(this.hexToField(recipient));
19717
21990
  const amountBytes = this.bigintToBytes(amount, 8);
19718
21991
  const txHashBytes = hexToBytes9(this.hexToField(txHash));
@@ -19723,11 +21996,11 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
19723
21996
  ...txHashBytes,
19724
21997
  ...blockBytes
19725
21998
  ]);
19726
- const hash2 = sha25620(preimage);
21999
+ const hash2 = sha25622(preimage);
19727
22000
  return Array.from(hash2);
19728
22001
  }
19729
22002
  getPublicKeyCoordinates(privateKey) {
19730
- const uncompressedPubKey = import_secp256k18.secp256k1.getPublicKey(privateKey, false);
22003
+ const uncompressedPubKey = import_secp256k19.secp256k1.getPublicKey(privateKey, false);
19731
22004
  const x = Array.from(uncompressedPubKey.slice(1, 33));
19732
22005
  const y = Array.from(uncompressedPubKey.slice(33, 65));
19733
22006
  return { x, y };
@@ -20325,6 +22598,8 @@ var ProofWorker = class _ProofWorker {
20325
22598
  PaymentBuilder,
20326
22599
  PaymentStatus,
20327
22600
  PrivacyLevel,
22601
+ PrivateNFT,
22602
+ PrivateVoting,
20328
22603
  ProofError,
20329
22604
  ProofGenerationError,
20330
22605
  ProofNotImplementedError,
@@ -20337,10 +22612,12 @@ var ProofWorker = class _ProofWorker {
20337
22612
  STABLECOIN_ADDRESSES,
20338
22613
  STABLECOIN_DECIMALS,
20339
22614
  STABLECOIN_INFO,
22615
+ SealedBidAuction,
20340
22616
  SettlementRegistry,
20341
22617
  SettlementRegistryError,
20342
22618
  SmartRouter,
20343
22619
  SolanaWalletAdapter,
22620
+ SuiStealthService,
20344
22621
  SwapStatus,
20345
22622
  ThresholdViewingKey,
20346
22623
  Treasury,
@@ -20366,6 +22643,7 @@ var ProofWorker = class _ProofWorker {
20366
22643
  checkEd25519StealthAddress,
20367
22644
  checkMobileWASMCompatibility,
20368
22645
  checkStealthAddress,
22646
+ checkSuiStealthAddress,
20369
22647
  commit,
20370
22648
  commitZero,
20371
22649
  computeAttestationHash,
@@ -20385,8 +22663,11 @@ var ProofWorker = class _ProofWorker {
20385
22663
  createNEARIntentsAdapter,
20386
22664
  createNEARIntentsBackend,
20387
22665
  createOracleRegistry,
22666
+ createPrivateOwnership,
22667
+ createPrivateVoting,
20388
22668
  createProductionSIP,
20389
22669
  createSIP,
22670
+ createSealedBidAuction,
20390
22671
  createShieldedIntent,
20391
22672
  createShieldedPayment,
20392
22673
  createSmartRouter,
@@ -20407,6 +22688,7 @@ var ProofWorker = class _ProofWorker {
20407
22688
  deriveEd25519StealthPrivateKey,
20408
22689
  deriveOracleId,
20409
22690
  deriveStealthPrivateKey,
22691
+ deriveSuiStealthPrivateKey,
20410
22692
  deriveViewingKey,
20411
22693
  deserializeAttestationMessage,
20412
22694
  deserializeIntent,
@@ -20418,6 +22700,7 @@ var ProofWorker = class _ProofWorker {
20418
22700
  ed25519PublicKeyToAptosAddress,
20419
22701
  ed25519PublicKeyToNearAddress,
20420
22702
  ed25519PublicKeyToSolanaAddress,
22703
+ ed25519PublicKeyToSuiAddress,
20421
22704
  encodeStealthMetaAddress,
20422
22705
  encryptForViewing,
20423
22706
  featureNotSupportedError,
@@ -20435,6 +22718,7 @@ var ProofWorker = class _ProofWorker {
20435
22718
  generateRandomBytes,
20436
22719
  generateStealthAddress,
20437
22720
  generateStealthMetaAddress,
22721
+ generateSuiStealthAddress,
20438
22722
  generateViewingKey,
20439
22723
  getActiveOracles,
20440
22724
  getAvailableTransports,
@@ -20494,10 +22778,13 @@ var ProofWorker = class _ProofWorker {
20494
22778
  isValidSlippage,
20495
22779
  isValidSolanaAddress,
20496
22780
  isValidStealthMetaAddress,
22781
+ isValidSuiAddress,
20497
22782
  isValidTaprootAddress,
20498
22783
  nearAddressToEd25519PublicKey,
20499
22784
  normalizeAddress,
22785
+ normalizeSuiAddress,
20500
22786
  notConnectedError,
22787
+ proveOwnership,
20501
22788
  publicKeyToEthAddress,
20502
22789
  registerWallet,
20503
22790
  removeOracle,
@@ -20541,6 +22828,7 @@ var ProofWorker = class _ProofWorker {
20541
22828
  verifyCommitment,
20542
22829
  verifyOpening,
20543
22830
  verifyOracleSignature,
22831
+ verifyOwnership,
20544
22832
  walletRegistry,
20545
22833
  withSecureBuffer,
20546
22834
  withSecureBufferSync,