21 个不稳定版本

0.25.7 2024年5月15日
0.25.5 2024年2月22日
0.25.4 2023年11月24日
0.25.0 2023年7月13日
0.15.3 2021年8月31日

#5 in #expectation

Download history 8/week @ 2024-04-15 263/week @ 2024-04-22 147/week @ 2024-04-29 18/week @ 2024-05-06 219/week @ 2024-05-13 52/week @ 2024-05-20 13/week @ 2024-05-27 39/week @ 2024-06-03 109/week @ 2024-06-10 47/week @ 2024-06-17 27/week @ 2024-06-24 64/week @ 2024-07-01 18/week @ 2024-07-08 28/week @ 2024-07-15 33/week @ 2024-07-22 504/week @ 2024-07-29

617 个月下载量

MIT/Apache

410KB
8K SLoC

这个 crate 允许使用有限数量的支持 RPC 调用来模拟以太坊节点,使您能够模拟以太坊合约。

使用 Mock::deploy 函数创建一个新的部署。

使用 Contract::expect_transactionContract::expect_call 配置合约的行为。

最后,通过调用 Contract::instance 创建一个 ethcontract 的 实例,然后在测试中使用该实例。

示例

让我们模拟 solidity 示例中的投票合约

首先,我们创建一个模拟节点并部署一个新的模拟合约

let mock = Mock::new(/* chain_id = */ 1337);
let contract = mock.deploy(abi);

然后我们设置方法调用的期望

// We'll need to know method signatures and types.
let vote: Signature<(U256,), ()> = [1, 33, 185, 63].into();
let winning_proposal: Signature<(), U256> = [96, 159, 241, 189].into();

// We expect some transactions calling the `vote` method.
contract
    .expect_transaction(vote);

// We also expect calls to `winning_proposal` that will return
// a value of `1`.
contract
    .expect_call(winning_proposal)
    .returns(1.into());

最后,我们创建一个动态实例并像往常一样使用它

let instance = contract.instance();

instance
    .method(vote, (1.into(),))?
    .from(account)
    .send()
    .await?;

let winning_proposal_index = instance
    .view_method(winning_proposal, ())?
    .call()
    .await?;
assert_eq!(winning_proposal_index, 1.into());

描述期望

模拟合约具有与 mockall crate 相似的外观。

对于测试期间期望调用的每个合约方法,调用 Contract::expect_transactionContract::expect_call 并使用诸如 returnstimesin_sequence 等函数设置创建的 期望。为了提高灵活性,您可以为同一方法附加多个期望。

有关更多信息示例,请参阅 Expectation

与模拟合约交互

在合约行为编程完成后,您可以调用 Contract::instance 来创建 ethcontract 的 Instance

您也可以通过 web3 直接获取合约地址并发送 RPC 调用。

具体来说,模拟节点支持 eth_calleth_sendRawTransactioneth_getTransactionReceipt

目前,模拟节点不能自行签名交易,因此不支持 eth_sendTransaction。此外,通过 eth_sendRawTransaction 部署合约目前也不可行。

模拟生成的合约

总的来说,生成的合约与动态合约类似:它们通过 Mock::deploy 部署,并通过 Contract::expect_callContract::expect_transaction 进行配置。

您可以使用 raw_contract 函数获取生成的合约的 ABI。

生成的 方法签名 通过 signatures 函数可用。

最后,可以使用 at 方法创建类型安全的实例。

以下是一个模拟 ERC20 兼容合约的示例。

首先,我们创建一个模拟节点并部署一个新的模拟合约

ethcontract::contract!("ERC20.json");

let mock = Mock::new(/* chain_id = */ 1337);
let contract = mock.deploy(ERC20::raw_contract().abi.clone());

然后我们使用生成的方法签名设置预期

contract
    .expect_transaction(ERC20::signatures().transfer())
    .once()
    .returns(true);

最后,我们使用模拟合约的地址与模拟节点交互

let instance = ERC20::at(&mock.web3(), contract.address());
instance
    .transfer(recipient, 100.into())
    .from(account)
    .send()
    .await?;

模拟 gas 和 gas 估算

模拟节点允许您自定义 eth_gasPrice RPC 调用返回的值。使用 Mock::update_gas_price 设置新的 gas 价格。

目前不支持使用 eth_estimateGas 估算 gas 消耗。目前,对 eth_estimateGas 的调用始终返回 1

依赖关系

~15–22MB
~296K SLoC