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