@sip-protocol/sdk 0.6.0 → 0.6.2

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