Skip to content

Commit

Permalink
Merge pull request #4822 from NomicFoundation/cancun
Browse files Browse the repository at this point in the history
Add support for the Cancun hardfork
  • Loading branch information
fvictorio committed Feb 14, 2024
2 parents 4250635 + e34908f commit ebcc283
Show file tree
Hide file tree
Showing 108 changed files with 5,579 additions and 3,624 deletions.
5 changes: 5 additions & 0 deletions .changeset/nasty-lies-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hardhat": minor
---

Add support for the Cancun hardfork
3 changes: 2 additions & 1 deletion docs/src/content/hardhat-network/docs/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- grayGlacier
- merge
- shanghai
- cancun

## Config

Expand Down Expand Up @@ -65,7 +66,7 @@ The block gas limit to use in Hardhat Network's blockchain. Default value: `30_0

#### `hardfork`

This setting changes how Hardhat Network works, to mimic Ethereum's mainnet at a given hardfork. It must be one of `"byzantium"`, `"constantinople"`, `"petersburg"`, `"istanbul"`, `"muirGlacier"`, `"berlin"`, `"london"`, `"arrowGlacier"`, `"grayGlacier"`, `"merge"` and `"shanghai"`. Default value: `"shanghai"`
This setting changes how Hardhat Network works, to mimic Ethereum's mainnet at a given hardfork. It must be one of `"byzantium"`, `"constantinople"`, `"petersburg"`, `"istanbul"`, `"muirGlacier"`, `"berlin"`, `"london"`, `"arrowGlacier"`, `"grayGlacier"`, `"merge"`, `"shanghai"` and `"cancun"`. Default value: `"shanghai"`

#### `throwOnTransactionFailures`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* @author Nomic Foundation
*/
"use strict";
const fs = require("node:fs");
const { isBuiltin } = require("node:module");
const fs = require("fs");
const { isBuiltin } = require("module");
const { relative } = require("eslint-module-utils/resolve");
const parse = require("eslint-module-utils/parse").default;
const visit = require("eslint-module-utils/visit").default;
Expand Down
21 changes: 11 additions & 10 deletions packages/hardhat-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,17 @@
"dependencies": {
"@ethersproject/abi": "^5.1.2",
"@metamask/eth-sig-util": "^4.0.0",
"@nomicfoundation/ethereumjs-block": "5.0.2",
"@nomicfoundation/ethereumjs-blockchain": "7.0.2",
"@nomicfoundation/ethereumjs-common": "4.0.2",
"@nomicfoundation/ethereumjs-evm": "2.0.2",
"@nomicfoundation/ethereumjs-rlp": "5.0.2",
"@nomicfoundation/ethereumjs-statemanager": "2.0.2",
"@nomicfoundation/ethereumjs-trie": "6.0.2",
"@nomicfoundation/ethereumjs-tx": "5.0.2",
"@nomicfoundation/ethereumjs-util": "9.0.2",
"@nomicfoundation/ethereumjs-vm": "7.0.2",
"@nomicfoundation/ethereumjs-block": "5.0.4",
"@nomicfoundation/ethereumjs-blockchain": "7.0.4",
"@nomicfoundation/ethereumjs-common": "4.0.4",
"@nomicfoundation/ethereumjs-evm": "2.0.4",
"@nomicfoundation/ethereumjs-rlp": "5.0.4",
"@nomicfoundation/ethereumjs-statemanager": "2.0.4",
"@nomicfoundation/ethereumjs-trie": "6.0.4",
"@nomicfoundation/ethereumjs-tx": "5.0.4",
"@nomicfoundation/ethereumjs-util": "9.0.4",
"@nomicfoundation/ethereumjs-verkle": "0.0.2",
"@nomicfoundation/ethereumjs-vm": "7.0.4",
"@nomicfoundation/solidity-analyzer": "^0.1.0",
"@sentry/node": "^5.18.1",
"@types/bn.js": "^5.1.0",
Expand Down
12 changes: 8 additions & 4 deletions packages/hardhat-core/src/builtin-tasks/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ function logHardhatNetworkAccounts(networkConfig: HardhatNetworkConfig) {
!Array.isArray(networkConfig.accounts) &&
networkConfig.accounts.mnemonic === HARDHAT_NETWORK_MNEMONIC;

const { bufferToHex, privateToAddress, toBuffer, toChecksumAddress } =
require("@nomicfoundation/ethereumjs-util") as typeof EthereumjsUtilT;
const {
bytesToHex: bufferToHex,
privateToAddress,
toBytes,
toChecksumAddress,
} = require("@nomicfoundation/ethereumjs-util") as typeof EthereumjsUtilT;

console.log("Accounts");
console.log("========");
Expand All @@ -69,15 +73,15 @@ function logHardhatNetworkAccounts(networkConfig: HardhatNetworkConfig) {

for (const [index, account] of accounts.entries()) {
const address = toChecksumAddress(
bufferToHex(privateToAddress(toBuffer(account.privateKey)))
bufferToHex(privateToAddress(toBytes(account.privateKey)))
);

const balance = (BigInt(account.balance) / 10n ** 18n).toString(10);

let entry = `Account #${index}: ${address} (${balance} ETH)`;

if (isDefaultConfig) {
const privateKey = bufferToHex(toBuffer(account.privateKey));
const privateKey = bufferToHex(toBytes(account.privateKey));
entry += `
Private Key: ${privateKey}`;
}
Expand Down
1 change: 1 addition & 0 deletions packages/hardhat-core/src/internal/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const HARDHAT_NETWORK_SUPPORTED_HARDFORKS = [
"grayGlacier",
"merge",
"shanghai",
"cancun",
];

export const HARDHAT_MEMPOOL_SUPPORTED_ORDERS = ["fifo", "priority"] as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@ function resolveHardhatNetworkConfig(
delete config.initialBaseFeePerGas;
}

if (
hardhatNetworkConfig.enableTransientStorage === true &&
hardhatNetworkConfig.hardfork === undefined
) {
config.hardfork = "cancun";
}
if (
hardhatNetworkConfig.enableTransientStorage === false &&
hardhatNetworkConfig.hardfork === undefined
) {
config.hardfork = "shanghai";
}

return config;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,25 @@ export function getValidationErrors(config: any): string[] {
}
});
}

if (hardhatNetwork.hardfork !== undefined) {
if (
!hardforkGte(hardhatNetwork.hardfork, HardforkName.CANCUN) &&
hardhatNetwork.enableTransientStorage === true
) {
errors.push(
`'enableTransientStorage' cannot be enabled if the hardfork is explicitly set to a pre-cancun value. If you want to use transient storage, use 'cancun' as the hardfork.`
);
}
if (
hardforkGte(hardhatNetwork.hardfork, HardforkName.CANCUN) &&
hardhatNetwork.enableTransientStorage === false
) {
errors.push(
`'enableTransientStorage' cannot be disabled if the hardfork is explicitly set to cancun or later. If you want to disable transient storage, use a hardfork before 'cancun'.`
);
}
}
}

for (const [networkName, netConfig] of Object.entries<any>(
Expand Down
11 changes: 11 additions & 0 deletions packages/hardhat-core/src/internal/core/config/default-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,17 @@ export const defaultHardhatNetworkParams: Omit<
]),
},
],
[
11155111, // sepolia
{
hardforkHistory: new Map([
[HardforkName.GRAY_GLACIER, 0],
[HardforkName.MERGE, 1_450_409],
[HardforkName.SHANGHAI, 2_990_908],
[HardforkName.CANCUN, 5_187_023],
]),
},
],
]),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
bufferToHex,
bytesToHex as bufferToHex,
isValidAddress,
toBuffer,
toBytes,
} from "@nomicfoundation/ethereumjs-util";
import * as t from "io-ts";

Expand All @@ -22,14 +22,16 @@ export const rpcQuantity = new t.Type<bigint>(
export const rpcData = new t.Type<Buffer>(
"DATA",
Buffer.isBuffer,
(u, c) => (isRpcDataString(u) ? t.success(toBuffer(u)) : t.failure(u, c)),
(u, c) =>
isRpcDataString(u) ? t.success(Buffer.from(toBytes(u))) : t.failure(u, c),
t.identity
);

export const rpcHash = new t.Type<Buffer>(
"HASH",
(v): v is Buffer => Buffer.isBuffer(v) && v.length === HASH_LENGTH_BYTES,
(u, c) => (isRpcHashString(u) ? t.success(toBuffer(u)) : t.failure(u, c)),
(u, c) =>
isRpcHashString(u) ? t.success(Buffer.from(toBytes(u))) : t.failure(u, c),
t.identity
);

Expand Down Expand Up @@ -96,7 +98,10 @@ function validateStorageSlot(u: unknown, c: t.Context): t.Validation<bigint> {
export const rpcAddress = new t.Type<Buffer>(
"ADDRESS",
(v): v is Buffer => Buffer.isBuffer(v) && v.length === ADDRESS_LENGTH_BYTES,
(u, c) => (isRpcAddressString(u) ? t.success(toBuffer(u)) : t.failure(u, c)),
(u, c) =>
isRpcAddressString(u)
? t.success(Buffer.from(toBytes(u)))
: t.failure(u, c),
t.identity
);

Expand Down Expand Up @@ -173,7 +178,7 @@ export function rpcDataToBigInt(data: string): bigint {
}

export function bufferToRpcData(
buffer: Buffer,
buffer: Uint8Array,
padToBytes: number = 0
): string {
let s = bufferToHex(buffer);
Expand All @@ -191,7 +196,7 @@ export function rpcDataToBuffer(data: string): Buffer {
});
}

return toBuffer(data);
return Buffer.from(toBytes(data));
}

// Type guards
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { rpcAccessList } from "../access-list";
import {
rpcAddress,
rpcData,
rpcHash,
rpcQuantity,
rpcStorageSlot,
rpcStorageSlotHexString,
Expand All @@ -23,6 +24,8 @@ export const rpcCallRequest = t.type(
accessList: optionalOrNullable(rpcAccessList),
maxFeePerGas: optionalOrNullable(rpcQuantity),
maxPriorityFeePerGas: optionalOrNullable(rpcQuantity),
blobs: optionalOrNullable(t.array(rpcData)),
blobVersionedHashes: optionalOrNullable(t.array(rpcHash)),
},
"RpcCallRequest"
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as t from "io-ts";

import { optionalOrNullable } from "../../../../util/io-ts";
import { rpcAccessList } from "../access-list";
import { rpcAddress, rpcData, rpcQuantity } from "../base-types";
import { rpcAddress, rpcData, rpcHash, rpcQuantity } from "../base-types";

// Type used by eth_sendTransaction
export const rpcTransactionRequest = t.type(
Expand All @@ -18,6 +18,8 @@ export const rpcTransactionRequest = t.type(
chainId: optionalOrNullable(rpcQuantity),
maxFeePerGas: optionalOrNullable(rpcQuantity),
maxPriorityFeePerGas: optionalOrNullable(rpcQuantity),
blobs: optionalOrNullable(t.array(rpcData)),
blobVersionedHashes: optionalOrNullable(t.array(rpcHash)),
},
"RpcTransactionRequest"
);
Expand All @@ -38,6 +40,8 @@ export interface RpcTransactionRequestInput {
}>;
maxFeePerGas?: string;
maxPriorityFeePerGas?: string;
blobs?: string[];
blobVersionedHashes?: string[];
}

export type RpcTransactionRequest = t.TypeOf<typeof rpcTransactionRequest>;
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const baseBlockResponse = {
baseFeePerGas: optional(rpcQuantity),
withdrawals: optional(t.array(rpcWithdrawalItem)),
withdrawalsRoot: optional(rpcHash),
parentBeaconBlockRoot: optional(rpcHash),
blobGasUsed: optional(rpcQuantity),
excessBlobGas: optional(rpcQuantity),
};

export type RpcBlock = t.TypeOf<typeof rpcBlock>;
Expand Down
37 changes: 24 additions & 13 deletions packages/hardhat-core/src/internal/core/providers/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,13 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
}

public async request(args: RequestArguments): Promise<unknown> {
const { ecsign, hashPersonalMessage, toRpcSig, toBuffer, bufferToHex } =
await import("@nomicfoundation/ethereumjs-util");
const {
ecsign,
hashPersonalMessage,
toRpcSig,
toBytes,
bytesToHex: bufferToHex,
} = await import("@nomicfoundation/ethereumjs-util");

if (
args.method === "eth_accounts" ||
Expand All @@ -65,7 +70,7 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
}

const privateKey = this._getPrivateKeyForAddress(address);
const messageHash = hashPersonalMessage(toBuffer(data));
const messageHash = hashPersonalMessage(toBytes(data));
const signature = ecsign(messageHash, privateKey);
return toRpcSig(signature.v, signature.r, signature.s);
}
Expand All @@ -84,7 +89,7 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
}

const privateKey = this._getPrivateKeyForAddress(address);
const messageHash = hashPersonalMessage(toBuffer(data));
const messageHash = hashPersonalMessage(toBytes(data));
const signature = ecsign(messageHash, privateKey);
return toRpcSig(signature.v, signature.r, signature.s);
}
Expand Down Expand Up @@ -189,13 +194,13 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {

private _initializePrivateKeys(localAccountsHexPrivateKeys: string[]) {
const {
bufferToHex,
toBuffer,
bytesToHex: bufferToHex,
toBytes,
privateToAddress,
} = require("@nomicfoundation/ethereumjs-util");

const privateKeys: Buffer[] = localAccountsHexPrivateKeys.map((h) =>
toBuffer(h)
toBytes(h)
);

for (const pk of privateKeys) {
Expand All @@ -205,7 +210,9 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
}

private _getPrivateKeyForAddress(address: Buffer): Buffer {
const { bufferToHex } = require("@nomicfoundation/ethereumjs-util");
const {
bytesToHex: bufferToHex,
} = require("@nomicfoundation/ethereumjs-util");
const pk = this._addressToPrivateKey.get(bufferToHex(address));
if (pk === undefined) {
throw new HardhatError(ERRORS.NETWORK.NOT_LOCAL_ACCOUNT, {
Expand All @@ -225,7 +232,9 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
}

private async _getNonce(address: Buffer): Promise<bigint> {
const { bufferToHex } = await import("@nomicfoundation/ethereumjs-util");
const { bytesToHex: bufferToHex } = await import(
"@nomicfoundation/ethereumjs-util"
);

const response = (await this._wrappedProvider.request({
method: "eth_getTransactionCount",
Expand All @@ -239,8 +248,8 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
transactionRequest: RpcTransactionRequest,
chainId: number,
privateKey: Buffer
): Promise<Buffer> {
const { AccessListEIP2930Transaction, Transaction } = await import(
): Promise<Uint8Array> {
const { AccessListEIP2930Transaction, LegacyTransaction } = await import(
"@nomicfoundation/ethereumjs-tx"
);

Expand Down Expand Up @@ -282,7 +291,7 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
{ common }
);
} else {
transaction = Transaction.fromTxData(txData, { common });
transaction = LegacyTransaction.fromTxData(txData, { common });
}

const signedTransaction = transaction.sign(privateKey);
Expand Down Expand Up @@ -311,7 +320,9 @@ export class HDWalletProvider extends LocalAccountsProvider {
passphrase
);

const { bufferToHex } = require("@nomicfoundation/ethereumjs-util");
const {
bytesToHex: bufferToHex,
} = require("@nomicfoundation/ethereumjs-util");
const privateKeysAsHex = privateKeys.map((pk) => bufferToHex(pk));
super(provider, privateKeysAsHex);
}
Expand Down

0 comments on commit ebcc283

Please sign in to comment.