Trong quá trình phát triển và kiểm thử các ứng dụng phi tập trung (DApps), việc mô phỏng các giao dịch trên mạng lưới chính (mainnet) là một nhu cầu thiết yếu để đảm bảo tính chính xác và hiệu quả trước khi triển khai thực tế. Tuy nhiên, việc thử nghiệm trực tiếp trên mainnet có thể gây tốn kém và rủi ro không đáng có. Đây là lý do tại sao các công cụ như Hardhat và Ganache trở thành lựa chọn hàng đầu của các nhà phát triển.
“Forking mainnet” là một tính năng mạnh mẽ cho phép bạn sao chép trạng thái hiện tại của mạng chính vào một mạng thử nghiệm cục bộ. Với phương pháp này, bạn có thể tái hiện các giao dịch, kiểm tra các hợp đồng thông minh và thử nghiệm các chiến lược mà không gây ảnh hưởng đến mạng chính.
Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách sử dụng Hardhat và Ganache để tạo fork mainnet, đồng thời áp dụng phương pháp này để kiểm tra lại các giao dịch trên các mạng EVM. Hãy cùng khám phá!
Mục lục
Sử dụng Hardhat để tạo “Forking mainnet”
Hardhat Forking là gì?
Hardhat Network có khả năng sao chép trạng thái của chuỗi khối mainnet vào môi trường cục bộ của bạn, bao gồm tất cả số dư và hợp đồng đã triển khai, để tạo nên mạng gọi là “forking mainnet“. Và quá trình này gọi là Hardhat forking.
Trong môi trường cục bộ được phân nhánh từ mạng chính, bạn có thể thực hiện các giao dịch để gọi các hợp đồng được triển khai trên mạng chính hoặc tương tác với mạng theo bất kỳ cách nào khác mà bạn thực hiện với mạng chính. Ngoài ra, bạn có thể làm bất cứ điều gì được hỗ trợ bởi Mạng Hardhat không phân nhánh: xem nhật ký bảng điều khiển, lấy dấu vết ngăn xếp hoặc sử dụng tài khoản mặc định để triển khai hợp đồng mới.
Tổng quát hơn, Hardhat Network có thể được sử dụng để rẽ nhánh bất kỳ mạng nào, không chỉ mạng chính. Hơn nữa, Hardhat có thể được sử dụng để rẽ nhánh bất kỳ chuỗi khối tương thích với EVM nào , không chỉ Ethereum. Chi tiết bạn xem bài viết: Forking other networks
Lệnh cài đặt và tạo bản “forking mainnet”
Các lệnh tạo “forking mainnet” cho Arbitrum (Yêu cầu NodeJs phải từ 16 trở lên):
// Cài đặt hardhat, yêu cầu phải cài hardhat 2.12 trở lên
// Xem https://docs.arbitrum.io/faqs/tooling-faqs#q-hardhat
npm install --save-dev "hardhat@2.17.1"
// Nếu không được thì chạy lệnh sau:
yarn add hardhat@2.17.1
// Forking ở block hiện tại
// Hiện dùng public node, bạn nên sử dụng node của Alchemy hoặc Infura
npx hardhat node --fork https://rpc.ankr.com/arbitrum
// Nếu chạy lần đầu có thể hỏi thêm một số thứ, làm theo hướng dẫn là được
// Forking ở block xác định
npx hardhat node --fork https://rpc.ankr.com/arbitrum --fork-block-number 121570800
Bạn có thể chạy node thông qua cấu hình trong tệp hardhat.config.js với nội dung như sau:
module.exports = {
networks: {
hardhat: {
forking: {
url: "https://rpc.ankr.com/arbitrum",
blockNumber: 121570800
}
}
},
solidity: "0.8.19",
};
Sau đó bạn đánh lệnh:
npx hardhat node
Forking node sẽ có thông tin như sau:
- ChainID: 31337
- RPC: http://127.0.0.1:8545
- WSS: ws://127.0.0.1:8545
Với node Forking này, mỗi giao dịch ghi lên blockchain thì sẽ sang 1 block mới.
Sử dụng console.log để debug trong Hardhat forking
Đầu tiên chúng ta chạy bản forking bằng lệnh sau:
npx hardhat node --fork https://developer-access-mainnet.base.org
Bây giờ chúng ta sẽ viết một contract sử dụng thư viện console.sol để sử dụng hàm console.log. Để đơn giản, chúng ta vào Remix tạo contract tên Storage.sol với nội dung như dưới:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
library console {
address constant CONSOLE_ADDRESS =
0x000000000000000000636F6e736F6c652e6c6f67;
function _sendLogPayloadImplementation(bytes memory payload) internal view {
address consoleAddress = CONSOLE_ADDRESS;
/// @solidity memory-safe-assembly
assembly {
pop(
staticcall(
gas(),
consoleAddress,
add(payload, 32),
mload(payload),
0,
0
)
)
}
}
function _castToPure(
function(bytes memory) internal view fnIn
) internal pure returns (function(bytes memory) pure fnOut) {
assembly {
fnOut := fnIn
}
}
function _sendLogPayload(bytes memory payload) internal pure {
_castToPure(_sendLogPayloadImplementation)(payload);
}
function log(string memory p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function log(string memory p0, uint256 p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
}
function log(string memory p0, uint256 p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2));
}
}
contract Storage {
uint256 number;
function store(uint256 num) public {
console.log("Store", num);
number = num;
}
function retrieve() public view returns (uint256){
console.log("Retrieve", number);
return number;
}
}
Chú ý: Trong contract tôi, tôi đã rút gọn thư viện console.sol chỉ giữ lại ba hàm log hay dùng cho các bạn dễ nhìn. Trong contract trên tôi có sử dụng console.log ở hai chỗ:
console.log("Store", num);
...
console.log("Retrieve", number);
Bây giờ chúng ta sẽ triển khai contract này trên mạng Hardhat Forking, sau đó gọi hàm retrieve(), store(10) và retrieve(), tiếp theo bạn nhìn sang phần log của Hardhat Forking, bạn sẽ thấy ba log tương ứng này (Xem ảnh dưới). Log hiển thị trên Hardhat khá dễ nhìn.

Sử dụng Ganache để tạo “Forking mainnet”
Sử dụng Ganache-Cli để tạo forking mainnet
Thực ra ganache-cli là phiên bản cũ, có nhiều tính năng mở rộng không được hỗ trợ. Đầu tiên bạn phải cài đặt Ganache-Cli bằng lệnh sau:
npm install -g ganache-cli
Sau đó bạn chạy bản forking bằng lệnh sau:
# Forking từ block mới nhất
ganache-cli --fork https://developer-access-mainnet.base.org
# Forking tại block 24119769
ganache-cli --fork https://developer-access-mainnet.base.org@24119769
# Forking tại block 24119768 với nhiều log hơn
ganache-cli --fork https://developer-access-mainnet.base.org@24119768 --verbose
Forking node sẽ có thông tin như sau:
- ChainID: 1337
- RPC: http://127.0.0.1:8545
- WSS: ws://127.0.0.1:8545
Nhiều khi bạn cài bản Ganache mới thì Ganache-Cli cũng bị cập nhật theo, bạn muốn chạy bản cũ bạn làm như sau:
# Cài đặt bản ganache-cli trong thư mục hiện tại
npm install ganache-cli
# Thực hiện forking
./node_modules/ganache-cli/cli.js --fork https://testnet-rpc.monad.xyz
Chú ý: Trong nhiều network nhiều khi bản Ganache và Hardhat không chạy được, dùng Ganache-Cli lại chạy được.
Sử dụng Ganache để tạo forking mainnet
Ganache hỗ trợ nhiều tính năng mới hơn, đặc biệt hỗ trợ hiển thị log từ lệnh console.log. Đầu tiên chúng ta cài đặt Ganache bằng lệnh sau:
npm install ganache --global
Sau đó bạn chạy bản forking bằng lệnh sau:
# Forking từ block mới nhất
ganache -f https://developer-access-mainnet.base.org
# Forking tại block 24119769
ganache -f https://developer-access-mainnet.base.org@24119769
Sử dung console.log với Ganache
Hiển thị log chỉ hỗ trợ trên bản Ganache mới, còn bản Ganache-Cli là bản cũ sẽ không hỗ trợ.
Đầu tiên chúng ta chạy bản forking bằng lệnh sau:
ganache -f https://developer-access-mainnet.base.org
Bây giờ chúng ta sẽ viết một contract sử dụng thư viện console.sol để sử dụng hàm console.log. Để đơn giản, chúng ta vào Remix tạo contract tên Storage.sol với nội dung như dưới:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
library console {
address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);
function _sendLogPayload(bytes memory payload) private view {
address consoleAddress = CONSOLE_ADDRESS;
assembly {
let argumentsLength := mload(payload)
let argumentsOffset := add(payload, 32)
pop(staticcall(gas(), consoleAddress, argumentsOffset, argumentsLength, 0, 0))
}
}
function log(string memory p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function log(string memory p0, uint256 p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
}
function log(string memory p0, uint256 p1, uint256 p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2));
}
}
contract Storage {
uint256 number;
function store(uint256 num) public {
console.log("Store", num);
number = num;
}
function retrieve() public view returns (uint256){
console.log("Retrieve", number);
return number;
}
}
Chú ý: Trong contract tôi, tôi đã rút gọn thư viện console.sol chỉ giữ lại ba hàm log hay dùng cho các bạn dễ nhìn. Trong contract trên tôi có sử dụng console.log ở hai chỗ:
console.log("Store", num);
...
console.log("Retrieve", number);
Bây giờ chúng ta sẽ triển khai contract này trên mạng Ganache Forking, sau đó gọi hàm retrieve() và store(10), tiếp theo bạn nhìn sang phần log của Ganache Forking, bạn sẽ thấy hai log tương ứng này (Xem ảnh dưới). Log hiển thị không được dễ nhìn như Hardhat.

Lập trình NodeJs kết nối tới Forking node
Đang cập nhật…
Triển khai Explorer giúp xem thông tin dễ dàng hơn
Sau khi Forking chúng ta vẫn cần một giao diện kiểu Explore giống như Etherscan hay Arbiscan để dễ dàng kiểm tra thông tin giao dịch. Sau thời gian tìm kiếm tôi thấy có hai thằng: Ethernal và Blockscout.
Sử dụng Ethernal
Ethernal là một công cụ mã nguồn mở giúp bạn xem thông tin giao dịch trên các nền tảng dạng EVM blockchain. Toàn bộ mã nguồn bạn có thể xem trên Github: Ethernal Source.
Bạn có thể cài đặt trực tiếp trên máy thông qua hướng dẫn từ tài liệu: Ethernal Documentation. Nhưng cách đơn giản nhất là tạo account trên website Ethernal, đăng nhập và tạo một workspace mới (Ví dụ tôi tạo Arbitrum-Forking theo kiểu Ethereum) và sử dụng RPC: http://127.0.0.1:8545. Lúc này Ethernal sẽ kết nối trực tiếp tới RPC để lấy thông tin và hiển thị trên giao diện. Đây là giao diện sau khi kết nối:

Khá tiện và đơn giản để sử dụng không cần phải cài đặt thêm gì, nhưng dữ liệu sẽ không được đồng bộ nếu trình duyệt bị tắt. Nếu muốn đồng bộ ngay cả khi trình duyệt bị tắt thì bạn phải sử dụng thêm plugin hardhat-ethernal.
Thông tin chi tiết các giao dịch hiện thị khá tốt đặt biệt là các event Transfer, tiếc là chỉ hiện address mà không hiện symbol. Nói chung mình thấy Ethernal phù hợp cho nhu cầu hiện tại của mình khi sử dụng node Forking. Nhưng nhiều khi bạn phải refesh lại trang hoặc switch giữa các link mới thấy cập nhật thông tin giao dịch.
Thử một lượt thấy Dev dùng khá tiện:
- Accounts: Mục này cho phép thêm account vào, có thể dùng private key hoặc address. Account này sẽ được dùng cho các giao dịch ghi lên blockchain.
- Blocks: Hiển thị các block gần đây.
- Transaction: Hiển thị các giao dịch gần đây
- Contract: Có thể import các contract vào đây sau đó cập nhật ABI cho contract. Sau khi cập nhật xong bạn sẽ có giao diện để gọi các hàm của contract và giao dịch để gửi các giao dịch lên blockchain.
- …
Sử dụng Blockscout
Blockscout cũng là một công cụ mã nguồn mở giúp bạn xem thông tin giao dịch trên các nền tảng dạng EVM blockchain. Toàn bộ mã nguồn bạn có thể xem trên Github: Blockscout Source
Với Blockscout bạn có thể triển khai riêng bản trên máy của bạn theo hướng dẫn: Blockscout Manual Deployment. Việc triển khai cũng khá phức tạp đòi hỏi bạn phải cài đặt PostgreSQL.
Do sử dụng được Ethernal rùi nên tôi không cài đặt thằng này. Các bạn nếu muốn có thể cài đặt thử nhé!
Một số vấn đề chú ý với bản Forking
Việc này không đòi hỏi phải đồng bộ dữ liệu về local, do đó bạn không cần phải lo về HDD. Việc Forking này ban đầu để hỗ trợ cho các L1 (Layer 1) dạng EVM, nhưng có thể sử dụng cho L2 (Layer 2), nhưng bạn phải chú ý một số vấn đề. Dưới đây là sự khác nhau khi Forking mạng Arbitrum:
- Một số thông tin block giữa mạng Mainnet và mạng Forking thì có sự khác nhau đôi chút. Ví dụ (Có thể còn có nhiều thông tin khác nữa):
- Mạng Forking không có thông tin L1
- Giao dịch đầu tiên trong mỗi block của Mainnet và Forking khác nhau
- Liên quan tới lới giá trị BlockNumber hiện tại:
- Trên Mainnet, nếu gọi hàm getBlockNumber() của contract 0xcA11bde05977b3631167028862bE2a173976CA11 nó chỉ trả về BlockNumber của Layer 1. Còn muốn lấy block đúng trên Arbitrum thì phải gọi hàm getBlockNumber() của contract 0x842eC2c7D803033Edf55E478F461FC547Bc54EB2, nó sẽ trả về đúng block number trên Layer 2.
- Trên mạng lưới Forking thì gọi hàm getBlockNumber() của contract 0x842eC2c7D803033Edf55E478F461FC547Bc54EB2 sẽ bị lỗi luôn, mà phải gọi hàm getBlockNumber() của 0xcA11bde05977b3631167028862bE2a173976CA11

Một số lỗi có thể gặp khi cài đặt
Phiên bản NodeJs không đúng
Để cài đặt được yêu cầu bạn phải cài NodeJs từ phiên bản 16 trở lên.
Error: C83B0000:error:1C800066:Provider routines:ossl_gcm_stream_update:cipher operation
Trường hợp cài đặt gặp lỗi “Error: C83B0000:error:1C800066:Provider routines:ossl_gcm_stream_update:cipher operation” (Mình gặp trên Windows) thì đánh lệnh sau trước rùi cài lại:
npm cache clear --force
Lỗi: code ERR_SSL_CIPHER_OPERATION_FAILED
Nếu gặp lỗi “code ERR_SSL_CIPHER_OPERATION_FAILED” thì đánh lệnh sau:
npm install -g https://tls-test.npmjs.com/tls-test-1.0.0.tgz
Error HH801: Plugin @nomicfoundation/hardhat-toolbox requires the following dependencies to be installed
Khi chạy lệnh mà báo lỗi “Error HH801: Plugin @nomicfoundation/hardhat-toolbox requires the following dependencies to be installed” thì thực hiện cài đặt các gói thiếu theo yêu cầu bằng lệnh yarn add hoặc npm install.
Một số lỗi có thể gặp khi kết nối tới Forking Mainnet
Hardhat Error: VM Exception while processing transaction: reverted with reason string ‘Multicall3: call failed’
Khi tôi tạo “Forking mainnet” cho Arbitrum sử dụng Hardhat, và gọi hàm getBlockNumber() của contract 0x842eC2c7D803033Edf55E478F461FC547Bc54EB2 để lấy block number của Layer 2, tức là block number trên Arbitrum thì trả về lỗi:
Error: Returned error: Error: VM Exception while processing transaction: reverted with reason string 'Multicall3: call failed'
at Object.ErrorResponse (/home/arbitest/dexscanner/collector/node_modules/web3-core-helpers/lib/errors.js:28:19)
at /home/arbitest/dexscanner/collector/node_modules/web3-core-requestmanager/lib/index.js:300:36
at /home/arbitest/dexscanner/collector/node_modules/web3-providers-http/lib/index.js:127:13
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
data: {
message: "Error: VM Exception while processing transaction: reverted with reason string 'Multicall3: call failed'",
data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000174d756c746963616c6c333a2063616c6c206661694000000000000000000'
}
}
Sau khi tìm hiểu tôi phát hiện ra là mạng lưới Forking hoạt động giống như mạng lưới L1 vì thế nếu bạn gọi các hàm mới chỉ có trên L2 thì sẽ bị lỗi. Hàm ArbSys(address(100)) chỉ hỗ trợ trên L2 của Arbitrum:
function getBlockNumber() public view returns (uint256 blockNumber) {
blockNumber = ArbSys(address(100)).arbBlockNumber();
}
function getL1BlockNumber() public view returns (uint256 l1BlockNumber) {
l1BlockNumber = block.number;
}
Khi làm việc với mạng Forking thì phải sử dụng hàm getBlockNumber() của contract 0xcA11bde05977b3631167028862bE2a173976CA11 hoặc hàm getL1BlockNumber() của 0x842eC2c7D803033Edf55E478F461FC547Bc54EB2.
Hardhat Error: Trying to send an incompatible EIP-155 transaction, signed for another chain
Khi tôi gửi một giao dịch thực hiện chuyển ETH sử dụng thư viện Web cũ, giao dịch này có Type=0 trên một Hardhat Forking thì báo lỗi như sau:
Error: Returned error: Trying to send an incompatible EIP-155 transaction, signed for another chain.
at Object.ErrorResponse (/home/arbitest/dexscanner/collector/node_modules/web3-core-helpers/lib/errors.js:28:19)
at /home/arbitest/dexscanner/collector/node_modules/web3-core-requestmanager/lib/index.js:300:36
at /home/arbitest/dexscanner/collector/node_modules/web3-providers-http/lib/index.js:127:13
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
data: {
message: 'Trying to send an incompatible EIP-155 transaction, signed for another chain.'
}
}
Lỗi này do sai chainId. Khi Forking thì mạng Forking sẽ có chainId mới, chainId này có thể do bạn cấu hình, nếu không hệ thống sẽ tự sinh ra chainId mới. Để biết chainId là bao nhiêu bạn gọi lệnh RPC như sau:
// Lấy chainId
curl --data '{"method":"eth_chainId","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545
// Transaction Receipt
curl --data '{"method":"eth_getTransactionReceipt","params":["0xc1b56de56e24fa1f4d361ad293f9b598e1adf712605d5bbe55ba3cffc35f541a"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545
Lệnh sau giúp bạn debug 1 transaction:
// Debug một transaction
curl --data '{"method":"debug_traceTransaction","params":["0xc1b56de56e24fa1f4d361ad293f9b598e1adf712605d5bbe55ba3cffc35f541a"], "id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545
Mình thử sử dụng hàm này nhưng thực sự dữ liệu rất nhiều lên đến 90M dữ liệu và khá rối. Lệnh sau sẽ giúp lấy dữ liệu quan trọng giống “Geth Debug Trace_2” trên etherscan (Rất tiếc Hardhat chưa hỗ trợ):
// Hardhat không hỗ trợ tham số tracer
curl --data '{"method":"debug_traceTransaction","params":["0xc1b56de56e24fa1f4d361ad293f9b598e1adf712605d5bbe55ba3cffc35f541a", {"tracer":"callTracer"}], "id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545
// Thử tham số khác, nhưng vẫn trả về dữ liệu rất lớn
curl --data '{"method":"debug_traceTransaction","params":["0x0e8f5d1aa145d5dfb80750901614494d7e5598272eef084ae134ef02636f85d0",{"disableStorage":true,"disableStack":true,"enableMemory":false,"enableReturnData":false}], "id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://localhost:8545
Hardhat Errors: Invalid value undefined supplied to : RpcBlockWithTransactions | null/totalDifficulty: QUANTITY
Trước đây việc chạy Hardhat Forking trên Base khá đơn giản và không gặp lỗi gì cả. Sau một thời gian tôi chạy lại lệnh để hardfork Base, thì lại thấy hiển thị thông báo lỗi như sau:
npx hardhat node --fork https://developer-access-mainnet.base.org
-------------------------------
Error HH604: Error running JSON-RPC server: Invalid JSON-RPC response's result.
Errors: Invalid value undefined supplied to : RpcBlockWithTransactions | null/totalDifficulty: QUANTITY
For more info go to https://hardhat.org/HH604 or run Hardhat with --show-stack-traces
Trong khi trước đây vẫn chạy bình thường. Tôi nghĩ do cách nâng cấp gần đây trên Base Node nên dữ liệu RPC gửi lại đã thiếu trường nào đó mà Hardhat sử dụng. Sau một thời gian tìm hiểu, tôi đã tìm được cách fix lỗi này dựa theo bản vá hardhat+2.13.0.dev.patch. Cách sửa chi tiết như sau:
+ B1: Mở tệp node_modules/hardhat/internal/hardhat-network/jsonrpc/client.js, thêm hàm _patchRawResult() vào cuối của class:
_patchRawResult(method, rawResult) {
/**
* Patch the raw results returned by _send or _sendBatch to fix RSK/Hardhat compatibility issues.
*/
if (
method.startsWith('eth_getBlock') &&
rawResult &&
rawResult.transactions && rawResult.transactions.length
) {
/*
* Calling a forked node (hardhat node --fork MY_RSK_NODE_RPC_URL) fails with:
* eth_call
* Invalid JSON-RPC response's result.
* Errors: Invalid value null supplied to : RpcBlockWithTransactions | null/transactions:
* RpcTransaction Array/0: RpcTransaction/v: QUANTITY, Invalid value null supplied to :
* RpcBlockWithTransactions | null/transactions: RpcTransaction Array/0: RpcTransaction/r:
* QUANTITY, Invalid value null supplied to : RpcBlockWithTransactions | null/transactions:
* RpcTransaction Array/0: RpcTransaction/s: QUANTITY
*
* This patch is based on:
* https://gist.github.com/0x0scion/0422f9135bc37642ba36d55b59e8b424
*
* More reading:
* https://github.com/NomicFoundation/hardhat/issues/2395
* https://github.com/NomicFoundation/hardhat/pull/2313/files
* https://github.com/NomicFoundation/hardhat/issues/2106
*/
if (!rawResult.totalDifficulty) rawResult.totalDifficulty = '0x0';
rawResult.transactions.forEach((t) => {
// Accesslist is sometimes missing, for other networks and maybe for RSK too
if (!t.accessList) t.accessList = [];
// Some RSK txs have null vrs
// from: '0x0000000000000000000000000000000000000000',
// to: '0x0000000000000000000000000000000001000008',
// gas: '0x0',
// gasPrice: '0x0',
// value: '0x0',
// input: '0x',
// v: null,
// r: null,
// s: null
if (!t.v) t.v = '0x0';
if (!t.r) t.r = '0x0';
if (!t.s) t.s = '0x0';
});
} else if (method === 'eth_getStorageAt') {
/*
* This fixes the error in eth_getStorageAt that says 0x0 is an invalid value for DATA
*/
if (rawResult === '0x0') {
rawResult = '0x0000000000000000000000000000000000000000000000000000000000000000';
}
}
return rawResult;
}
+ B2: Vẫn trong tệp node_modules/hardhat/internal/hardhat-network/jsonrpc/client.js, chúng ta tìm hàm async _perform(method, params, tType, getMaxAffectedBlockNumber) sau đó thực hiện:
# Tìm dòng code
const rawResult = await this._send(method, params);
# Đổi sang code mới
let rawResult = await this._send(method, params);
rawResult = this._patchRawResult(method, rawResult);
+ B3: Vẫn trong tệp node_modules/hardhat/internal/hardhat-network/jsonrpc/client.js, chúng ta tìm hàm async _performBatch(batch, getMaxAffectedBlockNumber) sau đó thực hiện:
# Tìm dòng code
const decodedResults = rawResults.map((result, i) => (0, decodeJsonRpcResponse_1.decodeJsonRpcResponse)(result, batch[i].tType));
# Đổi thành code
const decodedResults = rawResults.map((result, i) => {
result = this._patchRawResult(batch[i].method, result);
return (0, decodeJsonRpcResponse_1.decodeJsonRpcResponse)(
result,
batch[i].tType
);
});
Bây giờ chúng ta chạy lại lệnh forking và mọi thứ OKIE:
npx hardhat node --fork https://developer-access-mainnet.base.org
Một giải pháp khác là sử dụng ganache, mình chạy thấy okie:
ganache -f https://developer-access-mainnet.base.org
Hardhat Error: Error running JSON-RPC server: A withdrawalsRoot for a header can only be provided with EIP4895 being activated
Khi tôi chạy Hardhat Forking cho mạng Monad Testnet thì xuất hiện lỗi như sau:
npx hardhat node --fork https://testnet-rpc.monad.xyz
Error HH604: Error running JSON-RPC server: A withdrawalsRoot for a header can only be provided with EIP4895 being activated
For more info go to https://hardhat.org/HH604 or run Hardhat with --show-stack-traces
Tôi cũng chưa biết cách sửa thế nào, và tôi nhận thấy các mạng mới gần đây hay gặp lỗi.
Sau một thời gian nghiên cứu, tôi đã tìm ra “TIP & TRICK” để fix lỗi này. Vì là “TIP & TRICK” nên nó không đảm bảo chạy được các mạng khác, nên sau khi dùng xong bạn nên khôi phục về code cũ để đảm bảo an toàn. Nhưng cách fix này yêu cầu bạn phải fix lỗi “Hardhat Errors: Invalid value undefined supplied to : RpcBlockWithTransactions | null/totalDifficulty: QUANTITY” ở trên trước đã. Các fix cụ thể như sau:
Mở tệp node_modules/hardhat/internal/hardhat-network/jsonrpc/client.js, trong hàm _patchRawResult(method, rawResult) ta thêm code như hướng dẫn dưới:
# Tìm dòng code
if (!rawResult.totalDifficulty) rawResult.totalDifficulty = '0x0';
# Thêm dòng code sau vào ngay sau dòng code ở trên
# Khi không dùng nữa nhớ xóa đi
if (rawResult.withdrawalsRoot) delete rawResult.withdrawalsRoot;
Giờ chạy lại lệnh forking thì okie:
npx hardhat node --fork https://testnet-rpc.monad.xyz
Hardhat Error: Transaction maxFeePerGas (2533273) is too low for the next block, which has a baseFeePerGas of 5878238
Khi tôi sử dụng hardhat để fork block 24156819 bằng lệnh sau:
npx hardhat node --fork https://developer-access-mainnet.base.org --fork-block-number 24156819
Và sau đó viết code để triển khai một contract và test:
let contract = await hre.ethers.deployContract("MyContract", []);
Thì trên Hardhat console báo lỗi như sau:
eth_blockNumber
eth_estimateGas
eth_feeHistory
eth_getTransactionCount
eth_sendRawTransaction
Transaction maxFeePerGas (2533273) is too low for the next block, which has a baseFeePerGas of 5878238
Lỗi này có vẻ giống lỗi: Error on sendTransaction: “Transaction gasPrice (8000000000) is too low for the next block, which has a baseFeePerGas of xxxx”
Sau khi nhờ sự giúp đỡ của ChatGPT tôi đã tìm ra giải pháp để sửa lỗi bằng code. Chúng ta thay code triển khai bằng code sau:
let opts = { maxFeePerGas: 10_000_000 };
let contract = await hre.ethers.deployContract("MyContract", [], opts);
1 Pingbacks