Vyper 支持

Foundry 支持编译和测试 Vyper 合约。

1. 编译

你可以按照 这里 的说明安装 Vyper。如果你的 PATH 中有 vyper,foundry 将自动使用它。

否则,你可以通过在 foundry.toml 中添加以下内容来设置 vyper 的路径:

[vyper]
path = "/path/to/vyper"

2. Solidity 测试

让我们为这个简单的计数器合约编写一个测试:

number: public(uint256)

@deploy
@payable
def __init__(initial_number: uint256):
    self.number = initial_number

@external
def set_number(new_number: uint256):
    self.number = new_number

@external
def increment():
    self.number += 1

我们可以使用 forge-std 中的 deployCode cheatcode 部署它,并用以下 Solidity 测试进行测试:

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

interface ICounter {
    function increment() external;
    function number() external view returns (uint256);
    function set_number(uint256 newNumber) external;
}

contract CounterTest is Test {
    ICounter public counter;
    uint256 initialNumber = 5;

    function setUp() public {
        counter = ICounter(deployCode("Counter", abi.encode(initialNumber)));
        assertEq(counter.number(), initialNumber);
    }

    function test_Increment() public {
        counter.increment();
        assertEq(counter.number(), initialNumber + 1);
    }

    function testFuzz_SetNumber(uint256 x) public {
        counter.set_number(x);
        assertEq(counter.number(), x);
    }
}

3. 部署

你可以通过 forge create 命令部署 Vyper 合约:

forge create Counter --constructor-args '1' --rpc-url $RPC_URL --private-key $PRIVATE_KEY

使用 deployCode,你也可以在脚本中部署 Vyper 合约:

import {Script} from "forge-std/Script.sol";

contract CounterScript is Script {
    function run() public {
        vm.broadcast();
        deployCode("src/Counter.vy", abi.encode(1));
    }
}

4. Vyper 脚本

你可以像编写 Solidity 脚本一样编写 Vyper 脚本:

interface Vm:
    def startBroadcast(): nonpayable

interface ICounter:
    def increment(): nonpayable
    def number() -> uint256: view

vm: constant(Vm) = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)

@external
def run(counter: address):
    number_before: uint256 = staticcall ICounter(counter).number()

    extcall vm.startBroadcast()
    extcall ICounter(counter).increment()

    number_after: uint256 = staticcall ICounter(counter).number()

    assert number_after == number_before + 1

这样的脚本可以通过以下命令运行:

forge script script/Increment.s.vy  --sig 'run' '<counter address>' --rpc-url $RPC_URL --broadcast  --private-key $PRIVATE_KEY

5. 限制

  • 虽然你可以编写和运行 Vyper 的测试和脚本,但 Vyper 中没有 new 关键字来部署合约。未来将通过新的 cheatcodes 解决此问题。
  • Vyper 不允许使用相同名称但不同参数类型的重载。因此,一些 cheatcode 组合可能需要使用变通方法。(例如 startBroadcast(address sender))startBroadcast(uint256 pk)
  • forge coverage 目前不支持 Vyper 合约。