Foundry基本操作

  • owen
  • 发布于 1天前
  • 阅读 20

原链接:https://www.learnblockchain.cn/article/9606#ABI

根据上面的blog,亲自实践操作下Foundry,并做了些详细的注释和更正了原博主的一些错误;以此记录; 基于foundry默认初始化项目Counter为例;项目名test;

1. 测试

1.1 测试命令

# 运行所有测试
forge test

# 单独运行匹配前缀为 `CounterTest` 的单元测试
forge test --match-contract CounterTest

# 单独运行 `CounterTest` 的单元测试中的测试用例 `test_Increment` (match 同样是匹配前缀)
forge test --match-contract CounterTest --match-test test_Increment
forge test --match-test test_Increment

# 当合约内容有变动时,保存后就会重新运行所有的单元测试
forge test --watch 

1.2 测试日志详细级别

forge test -vv 是 Foundry 测试命令的一部分,v 的数量决定了输出的详细程度,具体如下:

  1. -v: 显示基本的测试信息,包括测试结果(通过/失败)和失败的堆栈跟踪。默认级别;
  2. -vv: 显示更详细的信息,包括测试的输入、事件日志和 console.log 输出。
  3. -vvv: 显示更高级别的调试信息,例如 EVM 操作码执行的详细信息。
  4. -vvvv: 显示最详细的调试信息,包括每个操作码的执行细节和存储变化。

1.3 覆盖测试

执行 forge coverage ,<font style="color:rgb(51, 51, 51);">显示代码的行覆盖率和分支覆盖率的表格;</font>

wushu@whd-asr MINGW64 /d/dapp/test (master)
$ forge coverage
Warning: optimizer settings have been disabled for accurate coverage reports, if you encounter "stack too deep" errors, consider using `--ir-minimum` which enables viaIR with minimum optimization resolving most of the errors
[⠊] Compiling...
[⠢] Compiling 23 files with Solc 0.8.28
[⠰] Solc 0.8.28 finished in 4.10s
Compiler run successful!
Analysing contracts...
Running tests...

Ran 3 tests for test/Counter.t.sol:CounterTest
[PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: 32043, ~: 32354)
[PASS] testFuzz_SetNumber2(uint256) (runs: 256, μ: 32064, ~: 32375)
[PASS] test_Increment() (gas: 31851)
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 33.70ms (64.64ms CPU time)

Ran 1 test suite in 35.91ms (33.70ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests)

╭----------------------+---------------+---------------+---------------+---------------╮
| File                 | % Lines       | % Statements  | % Branches    | % Funcs       |
+======================================================================================+
| script/Counter.s.sol | 0.00% (0/5)   | 0.00% (0/3)   | 100.00% (0/0) | 0.00% (0/2)   |
|----------------------+---------------+---------------+---------------+---------------|
| src/Counter.sol      | 100.00% (4/4) | 100.00% (2/2) | 100.00% (0/0) | 100.00% (2/2) |
|----------------------+---------------+---------------+---------------+---------------|
| Total                | 44.44% (4/9)  | 40.00% (2/5)  | 100.00% (0/0) | 50.00% (2/4)  |
╰----------------------+---------------+---------------+---------------+---------------╯

<font style="color:rgb(51, 51, 51);">更直观的显示对应代码行:可以使用 forge coverage --report lcov</font>

wushu@whd-asr MINGW64 /d/dapp/test (master)
$ forge coverage --report lcov
Warning: optimizer settings have been disabled for accurate coverage reports, if you encounter "stack too deep" errors, consider using `--ir-minimum` which enables viaIR with minimum optimization resolving most of the errors
[⠊] Compiling...
[⠢] Compiling 23 files with Solc 0.8.28
[⠔] Solc 0.8.28 finished in 2.19s
Compiler run successful!
Analysing contracts...
Running tests...

Ran 3 tests for test/Counter.t.sol:CounterTest
[PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: 32043, ~: 32354)
[PASS] testFuzz_SetNumber2(uint256) (runs: 256, μ: 32064, ~: 32375)
[PASS] test_Increment() (gas: 31851)
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 19.11ms (31.26ms CPU time)

Ran 1 test suite in 21.03ms (19.11ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests)
Wrote LCOV report.

会在根目录下生成 lcov.info 文件

TN:
SF:script/Counter.s.sol
DA:12,0
FN:12,CounterScript.setUp
FNDA:0,CounterScript.setUp
DA:14,0
FN:14,CounterScript.run
FNDA:0,CounterScript.run
DA:16,0
DA:18,0
DA:20,0
FNF:2
FNH:0
LF:5
LH:0
BRF:0
BRH:0
end_of_record
TN:
SF:src/Counter.sol
DA:7,515
FN:7,Counter.setNumber
FNDA:515,Counter.setNumber
DA:8,515
DA:11,1
FN:11,Counter.increment
FNDA:1,Counter.increment
DA:12,1
FNF:2
FNH:2
LF:4
LH:4
BRF:0
BRH:0
end_of_record

1.4 单元测试

1.4.1 模拟调用者

vm.prank 是 Foundry 提供的一个测试工具,用于模拟某个地址调用合约函数。它可以改变 msg.sender,从而模拟不同的调用者行为,常用于权限管理相关的测试。

假设有个修改拥有者的函数需要测试;

// 改变拥有者,仅有拥有者才能替换
function changeOwner(address newOwner) public {
  require(msg.sender == owner, "Not the owner");
  owner = newOwner;
}

可以通过模拟拥有者来对其进行测试;

  • vm.prank(address caller):将下一个合约调用的 msg.sender 设置为 caller。
function test_ChangeOwner() public {
    address newOwner = address(0x123);
    // 模拟非 owner 调用,应该失败
    vm.prank(newOwner);
    // vm.expectRevert(string reason):预期调用会失败,并指定失败原因
    vm.expectRevert("Not the owner");
    counter.changeOwner(newOwner);

    assertEq(counter.owner(), newOwner);
}
  • vm.prank(caller, origin):将下一次调用的 msg.sender 设置为 caller,tx.origin 设置为 origin。
vm.prank(msgSender, txOrigin);
  • vm.startPrank 和 vm.stopPrank:希望对一系列交易都使用相同的地址,则可使用 vm.startPrank 开始,并用 vm.stopPrank 结束;
function test_ChangeOwner() public {
    address newOwner = address(0x123);
    vm.startPrank(newOwner);
    vm.expectRevert("Not the owner");
    counter.changeOwner(newOwner);

    // 还要其他交易都要验证

    vm.stopPrank();
    assertEq(counter.owner(), newOwner);
}

1.4.2 检查余额

contract Deposit {
    event Deposited(address indexed);

    function buyerDeposit() external payable {
        require(msg.value == 1 ether, "incorrect amount");
        emit Deposited(msg.sender);
    }
}

测试函数

function test_BuyerDeposit() public {
    uint256 balanceBefore = address(depositContract).balance;
    depositContract.buyerDeposit{value: 1 ether}();
    uint256 balanceAfter = address(depositContract).balance;

    assertEq(balanceAfter - balanceBefore, 1 ether, "expect increase of 1 ether");
}

1.4.3 设置余额

vm.deal 和 vm.hoax 是 Foundry 提供的虚拟机工具,用于在测试中模拟账户余额和交易行为。它们的主要作用和使用场景如下:

vm.deal

  1. 作用: <u>设置指定地址的余额</u>。
  2. 使用场景: 用于在测试中为某个地址分配特定的余额,模拟账户的资金状态。
  3. 语法: vm.deal(address addr, uint256 amount);

vm.hoax

  1. 作用: <u>设置指定地址的余额并将后续的合约调用伪装成由该地址发起</u>。
  2. 使用场景: 用于模拟某个地址发起交易的场景,尤其是需要测试合约的权限或行为时。
  3. 语法: vm.hoax(address addr); vm.hoax(address addr, uint256 amount); (同时设置余额)
function testDeal() public {
    address user = address(0x123);
    uint256 initialBalance = 10 ether;

    // 设置 user 的余额为 10 ether
    vm.deal(user, initialBalance);

    // 验证余额是否正确
    assertEq(user.balance, initialBalance);
}
function testHoax() public {
    address user = address(0x123);
    uint256 initialBalance = 5 ether;

    // 设置 user 的余额为 5 ether,并伪装后续调用由 user 发起
    vm.hoax(user, initialBalance);

    // 模拟 user 调用合约的函数
    payable(address(this)).transfer(1 ether);

    // 验证余额变化
    assertEq(user.balance, initialBalance - 1 ether);
    assertEq(address(this).balance, 1 ether);
}

1.4.4 预期错误

vm.expectRevert 在测试中用于验证合约的错误处理逻辑是否符合预期,例如权限检查或输入验证。

  1. vm.expectRevert():预期下一次调用会触发 revert,不关心具体的错误消息。
  2. vm.expectRevert(bytes memory reason):预期下一次调用会触发 revert,并验证错误消息是否与指定的 reason 匹配。
  3. vm.expectRevert(string memory reason):预期下一次调用会触发 revert,并验证错误消息是否与指定的字符串匹配。
function changeOwner(address newOwner) public {
    require(msg.sender == owner, "Not the owner");
    owner = newOwner;
}

测试函数

function test_ChangeOwner() public {
    address newOwner = address(0x123);
    // 模拟非 owner 调用,应该失败
    vm.prank(newOwner);
    vm.expectRevert("Not the owner");
    counter.changeOwner(newOwner);

    assertEq(counter.owner(), newOwner);
}
// 测试结果
Ran 1 test for test/Counter.t.sol:CounterTest
[FAIL: revert: Not the owner] test_ChangeOwner() (gas: 15679)  错误结果和预期错误结果一致
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 507.80µs (109.50µs CPU time)

//=================================================================

function test_ChangeOwner() public {
    address newOwner = address(0x123);
    // 模拟非 owner 调用,应该失败
    vm.prank(newOwner);
    vm.expectRevert("Not the owner+++");
    counter.changeOwner(newOwner);

    assertEq(counter.owner(), newOwner);
}
//测试结果
Ran 1 test for test/Counter.t.sol:CounterTest
[FAIL: Error != expected error: Not the owner != Not the owner+++] test_ChangeOwner() (gas: 12024) 错误结果和预期错误结果不一致
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 674.50µs (191.80µs CPU time)

1.4.5 自定义预期错误

contract CustomErrorContract {
    error SomeError(uint256);

    function revertError(uint256 x) public pure {
        // 自定义错误如何测试
        revert SomeError(x);
    }
}

测试函数

// 测试合约中,同样自定义错误
error SomeError(uint256);

function test_Revert() public {
    // revert返回的错误 和 这里预期错误进行比较
    vm.expectRevert(abi.encodeWithSelector(SomeError.selector, 7));
    customErrorContract.revertError(6);
}

// 结果
Ran 1 test for test/Counter.t.sol:CounterTest
[FAIL: Error != expected error: SomeError(6) != SomeError(7)] test_Revert() (gas: 9850)
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 516.80µs (142.00µs CPU time)

1.4.6 测试事件

vm.expectEmit() 的作用是验证合约在执行过程中是否正确触发了指定的事件,并且可以选择性地验证事件的参数。

  1. 验证事件是否被正确触发:确保合约在特定操作后,按预期触发了事件。例如,用户存款后触发 Deposited 事件。
  2. 验证事件参数是否正确:检查事件的参数是否符合预期,例如 msg.sender 或 msg.value 是否正确。
  3. 调试复杂逻辑:在复杂的合约逻辑中,事件可以帮助确认某些状态变化是否发生。
  4. 权限管理:验证只有特定用户或角色触发的操作会生成事件。
event Deposited(address indexed);

function buyerDeposit() external payable {
    require(msg.value == 1 ether, "incorrect amount");
    emit Deposited(msg.sender);
}

测试函数

vm.expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData):

  1. checkTopic1: 是否验证第一个索引参数(indexed)
  2. checkTopic2: 是否验证第二个索引参数(indexed)
  3. checkTopic3: 是否验证第三个索引参数(indexed)
  4. checkData: 是否验证事件的非索引参数(data)
function test_BuyerDepositEvent() public {
    counter.buyerDeposit{value: 1 ether}();
}

// 测试结果;未测试事件
Ran 1 test for test/Counter.t.sol:CounterTest
[PASS] test_BuyerDepositEvent() (gas: 13217)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 763.60µs (111.80µs CPU time)

// =====================================================

function test_BuyerDepositEvent() public {
    vm.expectEmit(true,false,false,false);
    emit Deposited(msg.sender);
    counter.buyerDeposit{value: 1 ether}();
}
// 测试结果  forge test --match-test test_BuyerDepositEvent
Ran 1 test for test/Counter.t.sol:CounterTest
[FAIL: log != expected log] test_BuyerDepositEvent() (gas: 17691)
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 1.14ms (224.30µs CPU time)

1.4.7 修改时间戳测试

vm.warp 用于在测试中修改测试环境中区块的时间戳(block.timestamp)。它可以模拟时间的流逝,方便测试与时间相关的合约逻辑

  1. 测试时间锁(Timelock):验证合约在特定时间后是否允许操作。
  2. 测试过期逻辑:模拟时间流逝,检查合约是否正确处理过期状态。
  3. 测试奖励分发:验证基于时间的奖励计算是否正确。

假设有一个简单的时间锁合约,只有在特定时间之后才能提取资金:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Timelock {
    uint256 public unlockTime;
    address public owner;

    constructor(uint256 _unlockTime) {
        unlockTime = _unlockTime;
        owner = msg.sender;
    }

    function withdraw() external {
        require(block.timestamp >= unlockTime, "Funds are locked");
        require(msg.sender == owner, "Not the owner");
        // 提取逻辑
    }
}

测试代码

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test} from "forge-std/Test.sol";
import {Timelock} from "../src/Timelock.sol";

contract TimelockTest is Test {
    Timelock public timelock;

    function setUp() public {
        timelock = new Timelock(block.timestamp + 1 days);
    }

    function test_WithdrawAfterUnlock() public {
        // 模拟时间流逝
        vm.warp(block.timestamp + 1 days + 1 seconds);

        // 测试提取逻辑
        timelock.withdraw();
    }

    function test_WithdrawBeforeUnlock() public {
        // 测试在解锁时间之前提取,应该失败
        vm.expectRevert("Funds are locked");
        timelock.withdraw();
    }
}

1.4.8 修改区块高度测试</h3>

vm.roll 用于在测试中修改当前区块高度(block.number)。它可以模拟区块的推进,方便测试与区块高度相关的合约逻辑;

假设有一个简单的合约,只有在特定区块高度之后才能执行某些操作:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract BlockLock {
    uint256 public unlockBlock;
    address public owner;

    constructor(uint256 _unlockBlock) {
        unlockBlock = _unlockBlock;
        owner = msg.sender;
    }

    function execute() external {
        require(block.number >= unlockBlock, "Block not reached");
        require(msg.sender == owner, "Not the owner");
        // 执行逻辑
    }
}

测试代码

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test} from "forge-std/Test.sol";
import {BlockLock} from "../src/BlockLock.sol";

contract BlockLockTest is Test {
    BlockLock public blockLock;

    function setUp() public {
        blockLock = new BlockLock(block.number + 10);
    }

    function test_ExecuteAfterUnlockBlock() public {
        // 模拟区块高度推进
        vm.roll(block.number + 10);

        // 测试执行逻辑
        blockLock.execute();
    }

    function test_ExecuteBeforeUnlockBlock() public {
        // 测试在解锁区块之前执行,应该失败
        vm.expectRevert("Block not reached");
        blockLock.execute();
    }
}

1.4.9 模糊测试</h3>

vm.assume 用于在模糊测试(fuzz testing)中设置输入的约束条件。它可以限制模糊测试生成的输入数据范围,确保测试只在满足特定条件的情况下运行;

  1. 限制输入范围:确保模糊测试只生成符合业务逻辑的输入。
  2. 避免无效测试:跳过不符合条件的输入,减少无意义的测试运行。
  3. 提高测试效率:专注于测试特定条件下的逻辑,避免浪费资源。

注意:如果 vm.assume 的条件过于严格,可能导致测试无法生成有效输入,从而跳过所有测试。

vm.assume(bool condition)

  1. 当条件为 true 时,模糊测试才会继续执行。
  2. 当条件为 false,测试会跳过当前输入,继续生成新的输入

待测试函数

function isLargeNumber(uint256 x) public pure returns (bool) {
    return x > 100;
}

测试代码

function testFuzz_IsLargeNumber(uint256 x) public view{
    // 限制输入范围,小于100的会跳过
    vm.assume(x > 100);

    // 测试逻辑
    assertTrue(example.isLargeNumber(x));
}

// 执行结果
Ran 1 test for test/Counter.t.sol:CounterTest
[PASS] test_IsLargeNumber(uint256) (runs: 256, μ: 9921, ~: 9821) 运行了256次,平均执行时间为9921,中位数执行时间为9821
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 13.27ms (12.74ms CPU time)

1.5 切换网络测试</h2>

方式一:创建.env配置文件,并修改测试合约

// .env文件
MAINNET_RPC=
// 测试合约
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";

contract CounterTest is Test {
    uint256 mainnetFork;
    string mainnetRPC = vm.envString("MAINNET_RPC");
    Counter public counter;

    function setUp() public {
        mainnetFork = vm.createFork(mainnetRPC);    // 创建主网分叉
        vm.selectFork(mainnetFork);         // 切换到分叉环境
        counter = new Counter();
        counter.setNumber(0);
    }

    function testActiveFort() public {
        assertEq(vm.activeFork(), mainnetFork);
    }
}

方式二:也可以使用命令指定fork的网络和指定区块号;

<font style="color:rgb(51, 51, 51);">对应的区块会缓存到: /用户/.foundry/cache/rpc/mainnet中

forge test --match-contract CounterTest --fork-url &lt;RPC_URL> --fork-block-number &lt;BLOCK_NUMBER>

2. 导出ABI</h1>

wushu@whd-asr MINGW64 /d/dapp/test (master)
$ forge inspect Counter abi > Counter.abi

打开Counter.abi文件

╭----------+---------------------------------+------------╮
| Type     | Signature                       | Selector   |
+=========================================================+
| function | increment() nonpayable          | 0xd09de08a |
|----------+---------------------------------+------------|
| function | number() view returns (uint256) | 0x8381f58a |
|----------+---------------------------------+------------|
| function | setNumber(uint256) nonpayable   | 0x3fb5c1cb |
╰----------+---------------------------------+------------╯

3. 部署</h1>

3.1 本地部署

启动本地节点

wushu@whd-asr MINGW64 /d/dapp/test (master)
$ anvil

                             _   _
                            (_) | |
      __ _   _ __   __   __  _  | |
     / _` | | '_ \  \ \ / / | | | |
    | (_| | | | | |  \ V /  | | | |
     \__,_| |_| |_|   \_/   |_| |_|

    1.0.0-stable (e144b82070 2025-02-13T20:02:16.393821500Z)
    https://github.com/foundry-rs/foundry

默认账户
Available Accounts
==================

(0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH)
(1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH)
(2) 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC (10000.000000000000000000 ETH)
(3) 0x90F79bf6EB2c4f870365E785982E1f101E93b906 (10000.000000000000000000 ETH)
(4) 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (10000.000000000000000000 ETH)
(5) 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc (10000.000000000000000000 ETH)
(6) 0x976EA74026E726554dB657fA54763abd0C3a0aa9 (10000.000000000000000000 ETH)
(7) 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 (10000.000000000000000000 ETH)
(8) 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f (10000.000000000000000000 ETH)
(9) 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000.000000000000000000 ETH)

默认私钥,和上面的账户一一对应
Private Keys
==================

(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
(1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
(2) 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
(3) 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
(4) 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
(5) 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
(6) 0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e
(7) 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
(8) 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97
(9) 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6

默认钱包,助记词,
Wallet
==================
Mnemonic:          test test test test test test test test test test test junk
Derivation path:   m/44'/60'/0'/0/

链id
Chain ID
==================

31337

Base Fee
==================

1000000000

Gas Limit
==================

30000000

Genesis Timestamp
==================

1745993379

# 本地节点的url
Listening on 127.0.0.1:8545

新建终端,在部署合约

wushu@whd-asr MINGW64 /d/dapp/test (master)
$ forge script script/Counter.s.sol --rpc-url 127.0.0.1:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast
[⠊] Compiling...
[⠑] Compiling 1 files with Solc 0.8.28
[⠃] Solc 0.8.28 finished in 663.09ms
Compiler run successful!
Script ran successfully.

## Setting up 1 EVM.

==========================

Chain 31337

Estimated gas price: 1.534175043 gwei

Estimated total gas used for script: 203856

Estimated amount required: 0.000312750787565808 ETH

==========================

##### anvil-hardhat
✅  [Success] Hash: 0x10b51baa3497c2428240b1501d74d8d33a4ff87fd7366cc9c0b209d3b71b19e6
Contract Address: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9        合约地址
Block: 4
Paid: 0.000105279579406551 ETH (156813 gas * 0.671370227 gwei)

✅ Sequence #1 on anvil-hardhat | Total Paid: 0.000105279579406551 ETH (156813 gas * avg 0.671370227 gwei)

==========================

ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.

Transactions saved to: D:/dapp/test/broadcast\Counter.s.sol\31337\run-latest.json

Sensitive values saved to: D:/dapp/test/cache\Counter.s.sol\31337\run-latest.json

4. Cast命令

4.1 Chain

4.1.1 获取 chain ID

# 获取 chain ID
cast chain-id --rpc-url http://127.0.0.1:8545
31337

4.1.2 获取节点版本信息

# 获取该节点使用的客户端软件的类型和版本信息
cast client --rpc-url http://127.0.0.1:8545
anvil/v1.0.0

4.2 Block

https://developer.metamask.io/key/active-endpoints

// 从infura获取RPC节点的URL,这里以ETH的主网为例
export mainnet=https://mainnet.infura.io/v3/你的key

4.2.1 获取最新区块号

# 获取最新区块号
cast block-number --rpc-url $mainnet
22381664

4.2.2 获取区块出块时间

# 获取区块出块时间,默认最新,也可指定区块
cast age --rpc-url $mainnet
Wed Apr 30 11:49:35 2025

cast age --rpc-url $mainnet 1
Thu Jul 30 15:26:28 2015

4.2.3 根据时间戳获取区块

# 根据时间戳获取区块
cast find-block --rpc-url $mainnet 1729072888
20977392

4.2.4 获取区块内容

# 获取区块内容
cast block --rpc-url $mainnet
cast block --rpc-url $mainnet 20990795
cast block --rpc-url $mainnet --json
cast block --rpc-url $mainnet --field number
cast block --rpc-url $mainnet --field hash
cast block --rpc-url $mainnet pending
cast block --rpc-url $mainnet --full

4.2.5 获取价格

# 获取当前 gas 价格
cast gas-price --rpc-url $mainnet
826505153

# 获取 basefee
cast base-fee --rpc-url $mainnet
932153978
cast base-fee --rpc-url $mainnet 22381664
686242841

4.3 Account

4.3.1 获取余额

# 根据域名获取余额
cast balance --rpc-url $mainnet vitalik.eth
237570648344516640858

# 根据地址获取余额,并指定余额单位为 Ether(以太币)
cast balance --rpc-url $mainnet 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --ether
237.570648344516640858

4.3.2 获取 nonce 值

# 获取 nonce 值
cast nonce --rpc-url $mainnet 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
1549

4.3.3 获取 ENS域名

# 根据地址获取 ENS
cast lookup-address  --rpc-url $mainnet 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
vitalik.eth

4.3.4 获取 ENS 对应地址

# 获取 ENS 对应地址
cast resolve-name --rpc-url $mainnet vitalik.eth
0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045

4.3.5 获取存储槽 slot 数据

# 获取存储槽 slot 数据
cast storage --rpc-url $mainnet 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 0
0x577261707065642045746865720000000000000000000000000000000000001a

4.3.6 获取合约的 bytescode

# 获取合约的 bytescode
cast code --rpc-url $mainnet 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
0x6060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313c
e5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b60405180806020018281038252838181518152602001
91508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b6101
87600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b604051808281526020019150506040518091
0390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191
505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ff
ffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381
101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffff
ffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff16906020
01909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16
8152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181
600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b
81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffff
ffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d
1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ff
ffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffff
ffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffff
ffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffff
ff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffff
ffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020
01600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180
910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffff
ffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab4
57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b6003602052806000
5260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b576101008083540402835291
60200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029

4.4 Transation交易

# 本地网络提供的私钥
# 设置为当前终端的变量,方便后续$privateKey引用
export privateKey=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

4.4.1 转账

cast send --private-key <私钥> <目标地址> --value <金额>

# 转账
cast send --private-key $privateKey 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --value 10ether
blockHash            0x1790cc4aea79b93becc08d70f32b2bbead272c592f41e6f6ed88d92b1516121b
blockNumber          2
contractAddress      
cumulativeGasUsed    21000
effectiveGasPrice    878457601
from                 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
gasUsed              21000
logs                 []
logsBloom            0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
root
status               1 (success)
transactionHash      0xc2ea17a11e972682631bef60dc64b2f7444345668198b8f18836221477a5e88f
transactionIndex     0
type                 2
blobGasPrice         1
blobGasUsed
authorizationList
to                   0x70997970C51812dc3A010C7d01b50e0d17dc79C8

4.4.2 获取账号余额

# 获取账号余额
cast balance 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --ether
10010.000000000000000000

4.4.3 创建合约

# 创建合约
forge create --private-key $privateKey src/Counter.sol:Counter --broadcast

[⠊] Compiling...
No files changed, compilation skipped
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
Transaction hash: 0xb4ba08ea399d47511396386f2b5368029ba3d998c26d108b3e46b7f58413dada

4.4.4 调用合约方法

# 调用合约方法
cast send --private-key $privateKey 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 "setNumber(uint256)" 100

blockHash            0x434e52ca0e2746687aa0394a22132d9361d1fa694cd30f60e0658ccf33f12430
blockNumber          4
contractAddress
cumulativeGasUsed    21204
effectiveGasPrice    675361833
from                 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
gasUsed              21204
logs                 []
logsBloom            0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
root
status               1 (success)
transactionHash      0xe7a1e5b7ae1ac5ede1975f5c4ad3648da48be4f8cb08f1238c62501c0d58bb33
transactionIndex     0
type                 2
blobGasPrice         1
blobGasUsed
authorizationList
to                   0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9

4.4.5 调用 static 合约方法

合约中的状态变量:uint256 public number; solidity会默认提供get方法

# 调用 static 合约方法
cast call --private-key $privateKey 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 "number()(uint256)"
100

4.4.6 获取 Transation 信息

cast tx <交易哈希>

# 获取 Transation 信息
cast tx 0xe7a1e5b7ae1ac5ede1975f5c4ad3648da48be4f8cb08f1238c62501c0d58bb33

blockHash            0x434e52ca0e2746687aa0394a22132d9361d1fa694cd30f60e0658ccf33f12430
blockNumber          4
from                 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
transactionIndex     0
effectiveGasPrice    675361833

accessList           []
chainId              31337
gasLimit             21204
hash                 0xe7a1e5b7ae1ac5ede1975f5c4ad3648da48be4f8cb08f1238c62501c0d58bb33
input                0x3fb5c1cb0000000000000000000000000000000000000000000000000000000000000064
maxFeePerGas         1537608263
maxPriorityFeePerGas 1
nonce                3
r                    0xefd2a5a1b798bc9ca9cbe688c7e3587b3bd9d78edac9484c40839b8c42914d3d
s                    0x2bb9843f1acec7630fdbe02f4c08a86b63482542bfb3edab2c014e01618d7ddc
to                   0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
type                 2
value                0
yParity              0

4.5 Wallet钱包

4.5.1 创建钱包

# 创建钱包方式一
cast wallet new

Successfully created new keypair.
Address:     0xB930c4F004216640a2f22797bA9A86c6664c0496
Private key: 0xc9f83fffd1ba54b57d09b8c31bb8b26f966a8420516eb753347654df984a9b86

# 创建钱包方式二
# 在项目的根目录下创建密钥库文件夹,并将其加密后的私钥存储在 keystore 文件夹中,成功后会在文件夹中创建私钥文件名为UUID
mkdir keystore
cast wallet new keystore

Enter secret: 123456  请输入密码
Created new encrypted keystore file: D:\dapp\test\keystore\c6976752-603f-4e4b-80f8-316caad70a91
Address: 0xE7ca4cbcE29034807EB777DdE0D2e81F1aFC3d2a
文件内容:
{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"5387124bb1795c41a7459997196f8258"},"ciphertext":"4a650924f84b27de101b4cdb77117131ab9295cca2199abaf43ef38edfd0d340","kdf":"scrypt","kdfparams":{"dklen":32,"n":8192,"p":1,"r":8,"salt":"bd4506b01411ba0f1659486d5ac9d4fbf57cef0f898d0ca1c0b05246f6f875c4"},"mac":"5f542b512eb7c64e8f5fdff8f8b441b9c963b91e9bfdac73d605da7a479216a7"},"id":"c6976752-603f-4e4b-80f8-316caad70a91","version":3}

# 根据 json 钱包获取地址; 绝对路径;注意windows是反斜杠
cast wallet addr --keystore D://dapp//test//keystore//c6976752-603f-4e4b-80f8-316caad70a91 --password 123456
结果
0xE7ca4cbcE29034807EB777DdE0D2e81F1aFC3d2a

4.5.2 签名/验签

验签:cast wallet verify --address <地址> <消息> <签名>

# 签名
export privateKey=0x113d463b15d61eb6df9182a7c45c8b952a9768b7a84e1d4e471731c271360ca6
cast wallet sign --private-key $privateKey "hello"
结果
0x8b3c393dcea9794ad7aebe10436b9cdaea6efef3841cee59aae38e5ccaf1fd33105aa97a838b92e50d28e65828e7483f14f857bdb1e10d51e05a8769bc50f20b1c

# 验签
cast wallet verify --address 0xEF9D0359bD4Ade81386C49e91D3dB75c2b75A1C8 "hello" 0x8b3c393dcea9794ad7aebe10436b9cdaea6efef3841cee59aae38e5ccaf1fd33105aa97a838b92e50d28e65828e7483f14f857bdb1e10d51e05a8769bc50f20b1c
结果
Validation succeeded. Address 0xEF9D0359bD4Ade81386C49e91D3dB75c2b75A1C8 signed this message.

4.5.3 生成靓号

cast wallet vanity --starts-with <前缀> --ends-with <后缀>

# 生成靓号
cast wallet vanity --starts-with 00 --ends-with 00

Starting to generate vanity address...
Successfully found vanity address in 0.841 seconds.
Address: 0x0023c76EB1abF0731F0db3e3659fbC4c376B8A00
Private Key: 0xb3afaf62c4623e63093cf36895f283da65278ce0a96f9364c13ffd22e2b85772
点赞 0
收藏 0
分享

0 条评论

请先 登录 后评论
owen
owen
hello