Skip to content

Commit

Permalink
fix: apply bound for contract address from hash calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
penovicp committed Dec 10, 2023
1 parent 89715da commit 6d8c291
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 15 deletions.
6 changes: 6 additions & 0 deletions __tests__/utils/address.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { constants, num } from '../../src';
import {
addAddressPadding,
getChecksumAddress,
Expand All @@ -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', () => {
Expand Down
35 changes: 24 additions & 11 deletions __tests__/utils/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as starkCurve from '@scure/starknet';

import { constants, ec, hash, num, stark } from '../../src';
import { Block } from '../../src/provider/utils';

Expand Down Expand Up @@ -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,
Expand All @@ -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()', () => {
Expand Down
5 changes: 4 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 2 additions & 2 deletions src/utils/address.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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);

Expand Down
4 changes: 3 additions & 1 deletion src/utils/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { poseidonHashMany } from '@scure/starknet';

import {
ADDR_BOUND,
API_VERSION,
BN_FEE_TRANSACTION_VERSION_1,
BN_FEE_TRANSACTION_VERSION_2,
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 6d8c291

Please sign in to comment.