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