8 个版本
0.4.3 | 2024年3月27日 |
---|---|
0.4.2 | 2023年8月29日 |
0.3.6 | 2023年8月14日 |
0.3.5 | 2023年7月28日 |
0.3.3 | 2023年2月17日 |
914 在 魔法豆
113 每月下载量
110KB
3K SLoC
evm-coder
概述
用于在Rust和Solidity代码之间无缝调用转换的库。
通过在Rust中编码Solidity定义,此库还提供了为以太坊开发者生成Solidity接口的功能。
用法
要在Substrate中创建合约,请使用solidity_interface
属性。此属性应应用于代表您的合约的结构体的实现。它提供各种参数,启用继承、编译时接口验证等功能。
还支持使用属性 #[solidity(rename="funcName")]
进行函数重载。
安装
将以下行添加到您的 Cargo.toml
项目文件中。
[dependencies]
evm-coder = "0.3"
示例
考虑这个示例,其中我们创建了一个支持 ERC721 以及额外扩展接口的合约。
首先,我们使用以下 Rust 代码定义我们的合约接口
struct ContractHandle;
#[solidity_interface(
name = MyContract,
is(
ERC721,
CustomContract,
)
)]
impl ContractHandle{}
上面的代码定义了一个名为 MyContract 的合约,它实现了两个接口,即 ERC721 和 CustomContract。
接下来,我们开始实际实现 ERC721 接口
// This docs will be included into the generated `sol` file.
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
#[solidity_interface(
name = ERC721, // Contract name
events(ERC721Events), // Include events
expect_selector = 0x80ac58cd // Expected selector of contract (will be matched at compile time)
)]
impl ContractHandle {
// This docs will be included into the generated `sol` file.
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param owner An address for whom to query the balance
/// @return The number of NFTs owned by `owner`, possibly zero
fn balance_of(&self, owner: Address) -> Result<U256> {
todo!()
}
fn owner_of(&self, token_id: U256) -> Result<Address> {
todo!()
}
#[solidity(rename_selector = "safeTransferFrom")]
fn safe_transfer_from_with_data(&mut self, from: Address, to: Address, token_id: U256, data: Bytes) -> Result<()> {
todo!()
}
fn safe_transfer_from(&mut self, from: Address, to: Address, token_id: U256) -> Result<()> {
todo!()
}
fn transfer_from(&mut self, caller: Caller, from: Address, to: Address, token_id: U256) -> Result<()> {
todo!()
}
fn approve(&mut self, caller: Caller, approved: Address, token_id: U256) -> Result<()> {
todo!()
}
fn set_approval_for_all(&mut self, caller: Caller, operator: Address, approved: bool) -> Result<()> {
todo!()
}
fn get_approved(&self, token_id: U256) -> Result<Address> {
todo!()
}
fn is_approved_for_all(&self, owner: Address, operator: Address) -> Result<bool> {
todo!()
}
}
在这个接口的实现中,我们包括了在相应调用期间会触发的 ERC721Events
事件。为了确保标准接口的无缝实现,solidity_interface
注释中的 expect_selector
指令在编译时检查合约选择器,从而防止错误。
现在,让我们继续创建 ERC721 的事件
#[derive(ToLog)]
pub enum ERC721Events {
// This docs will be included into the generated `sol` file.
/// @dev This emits when ownership of any NFT changes by any mechanism.
Transfer {
#[indexed] // This field will be indexed
from: Address,
#[indexed]
to: Address,
#[indexed]
token_id: U256,
},
Approval {
#[indexed]
owner: Address,
#[indexed]
approved: Address,
#[indexed]
token_id: U256,
},
ApprovalForAll {
#[indexed]
owner: Address,
#[indexed]
operator: Address,
approved: bool,
},
}
让我们创建我们的扩展
#[solidity_interface(name = CustomContract)
impl ContractHandle {
#[solidity(rename_selector = "doSome")]
fn do_some_0(&mut self, caller: Caller, param: bool) -> Result<()> {
todo!()
}
#[solidity(rename_selector = "doSome")]
fn do_some_1(&mut self, caller: Caller, param: u8) -> Result<()> {
todo!()
}
#[solidity(hide)]
fn do_another(&mut self, caller: Caller, param: bool) -> Result<()> {
todo!()
}
fn do_magic(&mut self, caller: Caller, param1: Enum, param2: Struct) -> Result<Option<U256>> {
todo!()
}
}
方法 do_some_0
和 do_some_1
已经用宏 #[solidity(rename_selector = "doSome")]
进行了注释。这使得它们在 solidity 接口中以 单个 重载方法 doSome 的形式呈现。同时,do_another
方法将被包含在 .sol
文件中,但将被注释掉。最后,do_magic
方法使用了自定义类型 -- 我们也可以这样做!
让我们使我们的类型在 solidity 中可用(Option
默认可用)
#[derive(AbiCoder)]
struct Struct {
a: u8,
b: String
}
#[derive(AbiCoder, Default, Clone, Copy)]
#[repr(u8)]
enum Enum {
First,
Second,
#[default]
Third,
}
使用从 AbiCoder
派生的宏,维护您的类型变得非常简单。
最后,我们将指定 sol
文件的生成器
generate_stubgen!(gen_impl, ContractHandleCall<()>, true);
generate_stubgen!(gen_iface, ContractHandleCall<()>, false);
在 scripts 文件夹中包含了一组用于生成接口、sol
模板、json abi
和编译合约的脚本。为此,创建以下 make
文件
MyContract.sol:
PACKAGE=package-name NAME=erc::gen_iface OUTPUT=/path/to/iface/$@ $(PATH_TO_SCRIPTS)/generate_sol.sh
PACKAGE=package-name NAME=erc::gen_impl OUTPUT=/patch/to/stub/$@ $(PATH_TO_SCRIPTS)/generate_sol.sh
MyContract: MyContract.sol
INPUT=/patch/to/stub/$< OUTPUT=/patch/to/compiled/contract/MyContract.raw ./.maintain/scripts/compile_stub.sh
INPUT=/patch/to/stub/$< OUTPUT=/patch/to/abi ./.maintain/scripts/generate_abi.sh
结果,我们得到了以下 sol
接口文件
// SPDX-License-Identifier: OTHER
// This code is automatically generated
pragma solidity >=0.8.0 <0.9.0;
/// @dev common stubs holder
contract Dummy {
}
contract ERC165 is Dummy {
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
struct Struct {
a uint8;
b string;
}
enum Enum {
First,
Second,
Third
}
/// Optional value
struct OptionUint256 {
/// Shows the status of accessibility of value
bool status;
/// Actual value if `status` is true
uint256 value;
}
/// @title A contract that allows you to work with collections.
/// @dev the ERC-165 identifier for this interface is 0x738a0043
contract CustomContract is Dummy, ERC165 {
/// @dev EVM selector for this function is: 0x5465a527,
/// or in textual repr: doSome(bool)
function doSome(bool param) public;
/// @dev EVM selector for this function is: 0x58a93f40,
/// or in textual repr: doSome(uint8)
function doSome(uint8 param) public;
// /// @dev EVM selector for this function is: 0xf41a813e,
// /// or in textual repr: doAnother(bool)
// function doAnother(bool param) public;
/// @dev EVM selector for this function is: 0x8b5c1b1a,
/// or in textual repr: doMagic(uint8,(uint8,string))
function doSome(Enum param1, Struct param2) public returns (OptionUint256);
}
/// @dev inlined interface
contract ERC721Events {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
}
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
contract ERC721 is Dummy, ERC165, ERC721Events {
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param owner An address for whom to query the balance
/// @return The number of NFTs owned by `owner`, possibly zero
/// @dev EVM selector for this function is: 0x70a08231,
/// or in textual repr: balanceOf(address)
function balanceOf(address owner) public view returns (uint256);
/// @dev EVM selector for this function is: 0x6352211e,
/// or in textual repr: ownerOf(uint256)
function ownerOf(uint256 tokenId) public view returns (address);
/// @dev EVM selector for this function is: 0xb88d4fde,
/// or in textual repr: safeTransferFrom(address,address,uint256,bytes)
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public;
/// @dev EVM selector for this function is: 0x42842e0e,
/// or in textual repr: safeTransferFrom(address,address,uint256)
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public;
/// @dev EVM selector for this function is: 0x23b872dd,
/// or in textual repr: transferFrom(address,address,uint256)
function transferFrom(
address from,
address to,
uint256 tokenId
) public;
/// @dev EVM selector for this function is: 0x095ea7b3,
/// or in textual repr: approve(address,uint256)
function approve(address approved, uint256 tokenId) public;
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) public;
/// @dev EVM selector for this function is: 0x081812fc,
/// or in textual repr: getApproved(uint256)
function getApproved(uint256 tokenId) public view returns (address);
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
function isApprovedForAll(address owner, address operator) public view returns (bool);
}
contract MyContract is
Dummy,
ERC165,
ERC721,
CustomContract
{}
许可证
根据您的选择,受Apache 许可证 2.0 版或MIT 许可证的许可。
除非您明确表示,否则您提交给 evm-coder 的任何贡献,根据 Apache-2.0 许可证的定义,将按照上述方式双重许可,不附加任何额外条款或条件。
依赖关系
~5.5MB
~101K SLoC