Skip to content

Commit

Permalink
create2 create (#7129)
Browse files Browse the repository at this point in the history
* rlp lib

* CREATE support

* test data

* contract deployment & addr test on node

* local tests

* updated tests

* create2ContractAddress

* test data

* tests
  • Loading branch information
jdevcs authored Jul 2, 2024
1 parent 6454202 commit 280b8f0
Show file tree
Hide file tree
Showing 7 changed files with 394 additions and 11 deletions.
1 change: 1 addition & 0 deletions packages/web3-eth-contract/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"test:e2e:firefox": "npx cypress run --headless --browser firefox --env grep='ignore',invert=true"
},
"dependencies": {
"@ethereumjs/rlp": "^5.0.2",
"web3-core": "^4.4.0",
"web3-errors": "^1.2.0",
"web3-eth": "^4.7.0",
Expand Down
46 changes: 44 additions & 2 deletions packages/web3-eth-contract/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { Web3ContractError } from 'web3-errors';
import { RLP } from '@ethereumjs/rlp';
import { InvalidAddressError, InvalidMethodParamsError, InvalidNumberError, Web3ContractError } from 'web3-errors';
import {
TransactionForAccessList,
AbiFunctionFragment,
Expand All @@ -26,8 +27,10 @@ import {
NonPayableCallOptions,
PayableCallOptions,
ContractOptions,
Numbers,
} from 'web3-types';
import { isNullish, mergeDeep, isContractInitOptions } from 'web3-utils';
import { isNullish, mergeDeep, isContractInitOptions, keccak256, toChecksumAddress, hexToNumber } from 'web3-utils';
import { isAddress, isHexString } from 'web3-validator';
import { encodeMethodABI } from './encoding.js';
import { Web3ContractContext } from './types.js';

Expand Down Expand Up @@ -210,3 +213,42 @@ export const getCreateAccessListParams = ({

return txParams;
};


export const createContractAddress = (from: Address, nonce: Numbers): Address => {
if(!isAddress(from))
throw new InvalidAddressError(`Invalid address given ${from}`);

let nonceValue = nonce;
if(typeof nonce === "string" && isHexString(nonce))
nonceValue = hexToNumber(nonce);
else if(typeof nonce === "string" && !isHexString(nonce))
throw new InvalidNumberError("Invalid nonce value format");

const rlpEncoded = RLP.encode(
[from, nonceValue]
);
const result = keccak256(rlpEncoded);

const contractAddress = '0x'.concat(result.substring(26));

return toChecksumAddress(contractAddress);
}

export const create2ContractAddress = (from: Address, salt: HexString, initCode: HexString): Address => {
if(!isAddress(from))
throw new InvalidAddressError(`Invalid address given ${from}`);

if(!isHexString(salt))
throw new InvalidMethodParamsError(`Invalid salt value ${salt}`);

if(!isHexString(initCode))
throw new InvalidMethodParamsError(`Invalid initCode value ${initCode}`);

const initCodeHash = keccak256(initCode);
const initCodeHashPadded = initCodeHash.padStart(64, '0'); // Pad to 32 bytes (64 hex characters)
const create2Params = ['0xff', from, salt, initCodeHashPadded].map(x => x.replace(/0x/, ''));
const create2Address = `0x${ create2Params.join('')}`;

return toChecksumAddress(`0x${ keccak256(create2Address).slice(26)}`); // Slice to get the last 20 bytes (40 hex characters) & checksum
}
134 changes: 134 additions & 0 deletions packages/web3-eth-contract/test/fixtures/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { Numbers } from "web3-types";

export interface CreateTestData {
address: string;
input: {
from: string;
nonce: Numbers;
};
}

export const testData: CreateTestData[] = [
{
address: '0x0C1B54fb6fdf63DEe15e65CAdBA8F2e028E26Bd0',

input: {
from: '0xe2597eb05cf9a87eb1309e86750c903ec38e527e',
nonce: 0,
}
},
{
address: '0x0C1B54fb6fdf63DEe15e65CAdBA8F2e028E26Bd0',

input: {
from: '0xe2597eb05cf9a87eb1309e86750c903ec38e527e',
nonce: BigInt(0),
}
},
{
address: '0x0C1B54fb6fdf63DEe15e65CAdBA8F2e028E26Bd0',

input: {
from: '0xe2597eb05cf9a87eb1309e86750c903ec38e527e',
nonce: "0x0",
}
},
{
address: '0x3474627D4F63A678266BC17171D87f8570936622',

input: {
from: '0xb2682160c482eb985ec9f3e364eec0a904c44c23',
nonce: 10,
}
},

{
address: '0x3474627D4F63A678266BC17171D87f8570936622',

input: {
from: '0xb2682160c482eb985ec9f3e364eec0a904c44c23',
nonce: "0xa",
}
},

{
address: '0x3474627D4F63A678266BC17171D87f8570936622',

input: {
from: '0xb2682160c482eb985ec9f3e364eec0a904c44c23',
nonce: "0x0a",
}
},

{
address: '0x271300790813f82638A8A6A8a86d65df6dF33c17',

input: {
from: '0x8ba1f109551bd432803012645ac136ddd64dba72',
nonce: "0x200",
}
},

{
address: '0x271300790813f82638A8A6A8a86d65df6dF33c17',

input: {
from: '0x8ba1f109551bd432803012645ac136ddd64dba72',
nonce: "0x0200",
}
},

{
address: '0x995C25706C407a1F1E84b3777775e3e619764933',

input: {
from: '0x8ba1f109551bd432803012645ac136ddd64dba72',
nonce: "0x1d",
}
},

{
address: '0x995C25706C407a1F1E84b3777775e3e619764933',

input: {
from: '0x8ba1f109551bd432803012645ac136ddd64dba72',
nonce: "0x001d",
}
},

{
address: '0x995C25706C407a1F1E84b3777775e3e619764933',

input: {
from: '0x8ba1f109551bd432803012645ac136ddd64dba72',
nonce: 29,
}
},


{
address: '0x0CcCC7507aEDf9FEaF8C8D731421746e16b4d39D',

input: {
from: '0xc6af6e1a78a6752c7f8cd63877eb789a2adb776c',
nonce: 0
}
},
];
70 changes: 70 additions & 0 deletions packages/web3-eth-contract/test/fixtures/create2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { Address, HexString } from "web3-types";

export interface Create2TestData {
address: Address;
salt: HexString;
init_code: HexString;
result: Address;
}

export const create2TestData: Create2TestData[] = [
{
address: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000000000000000000000000000",
init_code: "0x00",
result: "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"
},
{
address: "0xdeadbeef00000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000000000000000000000000000",
init_code: "0x00",
result: "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3"
},
{
address: "0xdeadbeef00000000000000000000000000000000",
salt: "0x000000000000000000000000feed000000000000000000000000000000000000",
init_code: "0x00",
result: "0xD04116cDd17beBE565EB2422F2497E06cC1C9833"
},
{
address: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000000000000000000000000000",
init_code: "0xdeadbeef",
result: "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e"
},
{
address: "0x00000000000000000000000000000000deadbeef",
salt: "0x00000000000000000000000000000000000000000000000000000000cafebabe",
init_code: "0xdeadbeef",
result: "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7"
},
{
address: "0x00000000000000000000000000000000deadbeef",
salt: "0x00000000000000000000000000000000000000000000000000000000cafebabe",
init_code: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
result: "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C"
},
{
address: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000000000000000000000000000",
init_code: "0x",
result: "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0"
}
];
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
import { Web3Eth } from 'web3-eth';
import { FMT_BYTES, FMT_NUMBER } from 'web3-types';
import { Contract } from '../../src';
import { Contract, createContractAddress } from '../../src';
import { sleep } from '../shared_fixtures/utils';
import { ERC721TokenAbi, ERC721TokenBytecode } from '../shared_fixtures/build/ERC721Token';
import { GreeterBytecode, GreeterAbi } from '../shared_fixtures/build/Greeter';
Expand Down Expand Up @@ -59,6 +59,18 @@ describe('contract', () => {
sendOptions = { from: acc.address, gas: '1000000' };
});

it('should get correct contract address before deploymet using CREATE', async () => {
const nonce = await web3Eth.getTransactionCount(sendOptions.from as string);

// get contract address before deployment
const address = createContractAddress(sendOptions.from as string, nonce);

const deployedContract = await contract.deploy(deployOptions).send(sendOptions);

expect(deployedContract).toBeDefined();
expect(deployedContract.options.address).toEqual(address);
});

afterAll(async () => {
await closeOpenConnection(web3Eth);
});
Expand Down
Loading

1 comment on commit 280b8f0

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 280b8f0 Previous: 6454202 Ratio
processingTx 8643 ops/sec (±4.01%) 8763 ops/sec (±4.06%) 1.01
processingContractDeploy 37706 ops/sec (±7.16%) 37748 ops/sec (±7.88%) 1.00
processingContractMethodSend 15011 ops/sec (±8.43%) 14867 ops/sec (±8.97%) 0.99
processingContractMethodCall 26199 ops/sec (±7.75%) 25442 ops/sec (±7.88%) 0.97
abiEncode 40879 ops/sec (±7.43%) 40333 ops/sec (±5.55%) 0.99
abiDecode 30556 ops/sec (±6.68%) 27687 ops/sec (±9.66%) 0.91
sign 1503 ops/sec (±3.72%) 1472 ops/sec (±3.58%) 0.98
verify 363 ops/sec (±0.57%) 364 ops/sec (±0.56%) 1.00

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.