LapTrinhBlockchain

Chia sẻ kiến thức về Lập Trình Blockchain

Kiến thức Blockchain, Nâng cao Kiến thức

Hướng dẫn sử dụng Hardhat, Ganache tạo các “Forking mainnet” để test lại các giao dịch trên các mạng dạng EVM

Hướng dẫn tạo các “Forking mainnet” để sử dụng trên local cho các mạng dạng EVM

Hướng dẫn tạo các “Forking mainnet” để sử dụng trên local cho các mạng dạng EVM

Chia sẻ bài viết
5
(86)

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ư HardhatGanache 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 HardhatGanache để 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) 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.

Hardhat Forking hiển thị log từ lệnh console.log

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()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.

Ganache Forking hiển thị log từ lệnh console.log

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: EthernalBlockscout.

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:

Thông tin giao dịch trên giao diện Ethernal
Thông tin giao dịch trên giao diện Ethernal

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:

  1. 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
  2. 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ạng Mainnet (Trái) và Mạng Forking (Phải)
Mạng Mainnet (Trái) và Mạng Forking (Phải)

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);

Bài viết này có hữu ích với bạn?

Kích vào một biểu tượng ngôi sao để đánh giá bài viết!

Xếp hạng trung bình 5 / 5. Số phiếu: 86

Bài viết chưa có đánh giá! Hãy là người đầu tiên đánh giá bài viết này.

Trả lời

Giao diện bởi Anders Norén