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 日

#83 in #solidity

每月 27 次下载
evm-coder 中使用

MIT/Apache

120KB
3.5K 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_0do_some_1被宏#[solidity(rename_selector = ""doSome"")]标注。这允许它们在Solidity接口中以一个单一的 overloaded 方法名为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 License, Version 2.0MIT许可证

除非您明确声明,否则您故意提交给evm-coder的任何贡献,根据Apache-2.0许可证的定义,应按上述方式双许可,不附加任何额外条款或条件。

依赖项

~3MB
~48K SLoC