diff --git a/__tests__/utils/address.test.ts b/__tests__/utils/address.test.ts index 69687fdab..b3c346f37 100644 --- a/__tests__/utils/address.test.ts +++ b/__tests__/utils/address.test.ts @@ -1,3 +1,4 @@ +import { constants, num } from '../../src'; import { addAddressPadding, getChecksumAddress, @@ -17,6 +18,11 @@ describe('validateAndParseAddress', () => { return expect(validateAndParseAddress(addr)).toEqual(`${addAddressPadding(addr)}`); }); + + test('should fail for out of bound address', () => { + const addr = num.toHex(constants.ADDR_BOUND + 1n); + expect(() => validateAndParseAddress(addr)).toThrow(/^Message not signable/); + }); }); describe('address checksums', () => { diff --git a/__tests__/utils/utils.test.ts b/__tests__/utils/utils.test.ts index e41e8375b..f0019a405 100644 --- a/__tests__/utils/utils.test.ts +++ b/__tests__/utils/utils.test.ts @@ -1,3 +1,5 @@ +import * as starkCurve from '@scure/starknet'; + import { constants, ec, hash, num, stark } from '../../src'; import { Block } from '../../src/provider/utils'; @@ -78,18 +80,15 @@ describe('estimatedFeeToMaxFee()', () => { }); describe('calculateContractAddressFromHash()', () => { - // This test just show how to use calculateContractAddressFromHash for new devs - + const ethAddress = '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7'; + const daiAddress = '0x03e85bfbb8e2a42b7bead9e88e9a1b19dbccf661471061807292120462396ec9'; + const factoryAddress = '0x249827618A01858A72B7D04339C47195A324D20D6037033DFE2829F98AFF4FC'; + const classHash = '0x55187E68C60664A947048E0C9E5322F9BF55F7D435ECDCF17ED75724E77368F'; + // Any type of salt can be used. It depends on the dApp what kind of salt it wants to use. + const salt = ec.starkCurve.pedersen(ethAddress, daiAddress); + + // This test just shows how to use calculateContractAddressFromHash for new devs test('calculated contract address should match the snapshot', () => { - const ethAddress = '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7'; - - const daiAddress = '0x03e85bfbb8e2a42b7bead9e88e9a1b19dbccf661471061807292120462396ec9'; - const factoryAddress = '0x249827618A01858A72B7D04339C47195A324D20D6037033DFE2829F98AFF4FC'; - const classHash = '0x55187E68C60664A947048E0C9E5322F9BF55F7D435ECDCF17ED75724E77368F'; - - // Any type of salt can be used. It depends on the dApp what kind of salt it wants to use. - const salt = ec.starkCurve.pedersen(ethAddress, daiAddress); - const res = hash.calculateContractAddressFromHash( salt, classHash, @@ -101,6 +100,20 @@ describe('calculateContractAddressFromHash()', () => { `"0x36dc8dcb3440596472ddde11facacc45d0cd250df764ae7c3d1a360c853c324"` ); }); + + test('output should be bound', () => { + const starkCurveSpy = jest.spyOn(starkCurve, 'pedersen'); + starkCurveSpy.mockReturnValue(num.toHex(constants.ADDR_BOUND + 1n)); + const res = hash.calculateContractAddressFromHash( + salt, + classHash, + [ethAddress, daiAddress, factoryAddress], + factoryAddress + ); + expect(starkCurveSpy).toHaveBeenCalled(); + expect(BigInt(res)).toBeLessThan(constants.ADDR_BOUND); + starkCurveSpy.mockRestore(); + }); }); describe('new Block()', () => { diff --git a/src/constants.ts b/src/constants.ts index 60a9303b1..d90b97a73 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -14,9 +14,12 @@ export const BN_FEE_TRANSACTION_VERSION_2 = 2n ** 128n + BN_TRANSACTION_VERSION_ export const ZERO = 0n; export const MASK_250 = 2n ** 250n - 1n; // 2 ** 250 - 1 -export const MASK_251 = 2n ** 251n; export const API_VERSION = ZERO; +// based on: https://github.com/starkware-libs/cairo-lang/blob/v0.12.3/src/starkware/starknet/common/storage.cairo#L3 +export const MAX_STORAGE_ITEM_SIZE = 256n; +export const ADDR_BOUND = 2n ** 251n - MAX_STORAGE_ITEM_SIZE; + export enum BaseUrl { SN_MAIN = 'https://alpha-mainnet.starknet.io', SN_GOERLI = 'https://alpha4.starknet.io', diff --git a/src/utils/address.ts b/src/utils/address.ts index 310bbfae0..e0647df86 100644 --- a/src/utils/address.ts +++ b/src/utils/address.ts @@ -1,7 +1,7 @@ /* eslint-disable no-bitwise */ import { hexToBytes } from '@noble/curves/abstract/utils'; -import { MASK_251, ZERO } from '../constants'; +import { ADDR_BOUND, ZERO } from '../constants'; import { BigNumberish } from '../types'; import { addHexPrefix, removeHexPrefix } from './encode'; import { keccakBn } from './hash'; @@ -12,7 +12,7 @@ export function addAddressPadding(address: BigNumberish): string { } export function validateAndParseAddress(address: BigNumberish): string { - assertInRange(address, ZERO, MASK_251, 'Starknet Address'); + assertInRange(address, ZERO, ADDR_BOUND - 1n, 'Starknet Address'); const result = addAddressPadding(address); diff --git a/src/utils/hash.ts b/src/utils/hash.ts index 205a37599..3c8268d2c 100644 --- a/src/utils/hash.ts +++ b/src/utils/hash.ts @@ -3,6 +3,7 @@ import { poseidonHashMany } from '@scure/starknet'; import { + ADDR_BOUND, API_VERSION, BN_FEE_TRANSACTION_VERSION_1, BN_FEE_TRANSACTION_VERSION_2, @@ -205,13 +206,14 @@ export function calculateContractAddressFromHash( const CONTRACT_ADDRESS_PREFIX = felt('0x535441524b4e45545f434f4e54524143545f41444452455353'); // Equivalent to 'STARKNET_CONTRACT_ADDRESS' - return computeHashOnElements([ + const hash = computeHashOnElements([ CONTRACT_ADDRESS_PREFIX, deployerAddress, salt, classHash, constructorCalldataHash, ]); + return toHex(BigInt(hash) % ADDR_BOUND); } function nullSkipReplacer(key: string, value: any) {