diff --git a/src/generateEphemeralPrivateKey.ts b/src/generateEphemeralPrivateKey.ts index 55a4096..305b40e 100644 --- a/src/generateEphemeralPrivateKey.ts +++ b/src/generateEphemeralPrivateKey.ts @@ -18,7 +18,7 @@ export function generateEphemeralPrivateKey({ coinType, }: { viewingPrivateKeyNode: HDKey; - nonce: number; + nonce: bigint; chainId?: number; coinType?: number; }): { ephemeralPrivateKey: `0x${string}` } { @@ -55,16 +55,19 @@ export function generateEphemeralPrivateKey({ // the server then generates pseudo-random addresses by incrementing n // since each value cannot be larger than 2^31 - 1 (see HARDENED_OFFSET - 0x7FFFFFF) // we therefore introduce a parent nonce p to allow for more addresses to be generated. - // If the nonce is bigger than n, we put the overflow part into a parentNonce. To simplify - // the creation of parentNonce, we set MAX_N to be 0xFFFFFFF. With this schema the combination of nonce - // and parent nonce has a max value of 0x7FFFFFFFFFFFFFF = 576460752303423487₁₀ + // If the nonce is bigger than MAX_NONCE, we put the overflow part into a parentNonce. To simplify + // the creation of parentNonce, we set MAX_NONCE to be 0xFFFFFFF. With this schema the combination of nonce + // and parent nonce has a max value of 0x7FFFFFFFFFFFFFF-1 = 576460752303423486₁₀ // Split the nonce into two parts to ensure no number above 0x80000000 is used in the derivation path - const MAX_NONCE = 0xfffffff; - let parentNonce = 0; + if (nonce >= BigInt(0x7FFFFFFFFFFFFFF)) { + throw new Error('Nonce is too large. Max value is 0x7FFFFFFFFFFFFFF.'); + } + const MAX_NONCE = BigInt(0xfffffff); + let parentNonce = BigInt(0); if (nonce > MAX_NONCE) { - parentNonce = Math.floor(nonce / (MAX_NONCE + 1)); - nonce = nonce % (MAX_NONCE + 1); + parentNonce = nonce / (MAX_NONCE + BigInt(1)); + nonce = nonce % (MAX_NONCE + BigInt(1)); } // Create the derivation path diff --git a/src/predictStealthSafeAddress.ts b/src/predictStealthSafeAddress.ts index 09cbd9f..5d506e2 100644 --- a/src/predictStealthSafeAddress.ts +++ b/src/predictStealthSafeAddress.ts @@ -25,6 +25,7 @@ import { SafeVersion } from './predictStealthSafeAddressTypes'; * @param useDefaultAddress {boolean} (optional) if true, the Safe default address will be used - see DefaultAddress inside https://github.com/safe-global/safe-deployments * @param chainId {number} (optional) the chainId of the network where the Safe will be deployed * @param transport (optional) a custom viem transport to use for the simulation + * @param safeVersion {SafeVersion} the Safe version to use * @return Promise<{ stealthSafeAddress }> the predicted Safe address (not deployed) */ export async function predictStealthSafeAddressWithClient({ @@ -175,6 +176,7 @@ export function predictStealthSafeAddressWithBytecode({ * @param stealthAddresses {string[]} the stealth addresses controlling the Safe * @param chainId {number} (optional) the chainId of the network where the Safe will be deployed * @param useDefaultAddress {boolean} (optional) if true, the Safe default address will be used - see DefaultAddress inside https://github.com/safe-global/safe-deployments + * @param safeVersion {SafeVersion} the Safe version to use */ function getSafeInitializerData ({ chainId, diff --git a/test/generateEphemeralPrivateKey.test.ts b/test/generateEphemeralPrivateKey.test.ts index 977bc15..732b330 100644 --- a/test/generateEphemeralPrivateKey.test.ts +++ b/test/generateEphemeralPrivateKey.test.ts @@ -9,7 +9,7 @@ describe('generateEphemeralPrivateKey', () => { const { ephemeralPrivateKey } = generateEphemeralPrivateKey({ viewingPrivateKeyNode, - nonce: 0, + nonce: BigInt(0), chainId: 10, }); @@ -23,7 +23,7 @@ describe('generateEphemeralPrivateKey', () => { const { ephemeralPrivateKey } = generateEphemeralPrivateKey({ viewingPrivateKeyNode, - nonce: 2147483649, + nonce: BigInt(2147483649), chainId: 10, }); @@ -37,7 +37,7 @@ describe('generateEphemeralPrivateKey', () => { const { ephemeralPrivateKey } = generateEphemeralPrivateKey({ viewingPrivateKeyNode, - nonce: 0, + nonce: BigInt(0), coinType: 2147483658, }); @@ -46,7 +46,17 @@ describe('generateEphemeralPrivateKey', () => { ); }); - // TO-DO throw an error if the nonce is too high + it('should throw an error if the nonce is too high', () => { + const viewingPrivateKeyNode = extractViewingPrivateKeyNode(privateViewingKey); + + expect(() => + generateEphemeralPrivateKey({ + viewingPrivateKeyNode, + nonce: BigInt('576460752303423488'), + chainId: 10, + }), + ).toThrow('Nonce is too large. Max value is 0x7FFFFFFFFFFFFFF.'); + }); it('should throw an error if no coinType or chainId is provided', () => { const viewingPrivateKeyNode = extractViewingPrivateKeyNode(privateViewingKey); @@ -54,7 +64,7 @@ describe('generateEphemeralPrivateKey', () => { expect(() => generateEphemeralPrivateKey({ viewingPrivateKeyNode, - nonce: 0, + nonce: BigInt(0), }), ).toThrow('coinType or chainId must be defined.'); }); @@ -65,7 +75,7 @@ describe('generateEphemeralPrivateKey', () => { const viewingPrivateKeyNode = extractViewingPrivateKeyNode(randomPrivateViewingKey); const { ephemeralPrivateKey } = generateEphemeralPrivateKey({ viewingPrivateKeyNode, - nonce, + nonce: BigInt(nonce), chainId: 10, }); expect(ephemeralPrivateKey).toMatch(/^0x[0-9a-fA-F]{64}$/);