ERC-20
一个 ERC-20 token 合约会跟踪 fungible 代币:任何代币与任何其他代币完全相等;没有代币具有与其相关的特殊权利或行为。 这使得 ERC-20 代币可用于诸如*交换媒介货币*、投票权、*质押*等用途。
OpenZeppelin Contracts 为 Arbitrum Stylus 提供了许多与 ERC20 相关的合约。
在 API reference
中,你可以找到有关其属性和用法的详细信息。
构建 ERC-20 代币合约
使用 Contracts,我们可以轻松创建自己的 ERC-20 代币合约,该合约将用于跟踪 Gold(GLD),这是一种假设游戏中的内部货币。
这是我们的 GLD 代币的样子。
use openzeppelin_stylus::{
token::erc20::{
self,
extensions::{Erc20Metadata, IErc20Metadata},
Erc20, IErc20,
},
};
#[entrypoint]
#[storage]
struct GLDToken {
erc20: Erc20,
metadata: Erc20Metadata,
}
#[public]
#[implements(IErc20<Error = erc20::Error>, IErc20Metadata, IErc165)]
impl GLDToken {
#[constructor]
fn constructor(&mut self, name: String, symbol: String) {
self.metadata.constructor(name, symbol);
}
// ...
}
#[public]
impl IErc20 for GLDToken {
type Error = erc20::Error;
fn total_supply(&self) -> U256 {
self.erc20.total_supply()
}
fn balance_of(&self, account: Address) -> U256 {
self.erc20.balance_of(account)
}
fn transfer(
&mut self,
to: Address,
value: U256,
) -> Result<bool, Self::Error> {
self.erc20.transfer(to, value)
}
fn allowance(&self, owner: Address, spender: Address) -> U256 {
self.erc20.allowance(owner, spender)
}
fn approve(
&mut self,
spender: Address,
value: U256,
) -> Result<bool, Self::Error> {
self.erc20.approve(spender, value)
}
fn transfer_from(
&mut self,
from: Address,
to: Address,
value: U256,
) -> Result<bool, Self::Error> {
self.erc20.transfer_from(from, to, value)
}
}
#[public]
impl IErc20Metadata for GLDToken {
fn name(&self) -> String {
self.metadata.name()
}
fn symbol(&self) -> String {
self.metadata.symbol()
}
fn decimals(&self) -> U8 {
self.metadata.decimals()
}
}
#[public]
impl IErc165 for GLDToken {
fn supports_interface(&self, interface_id: B32) -> bool {
self.erc20.supports_interface(interface_id)
|| self.metadata.supports_interface(interface_id)
}
}
关于 decimals
的说明
通常,你可能希望能够将你的代币分成任意金额:例如,如果你拥有 5 GLD
,你可能希望将 1.5 GLD
发送给朋友,并保留 3.5 GLD
给自己。
不幸的是,Solidity 和 EVM 不支持此行为:只能使用整数(完整)数字,这会带来问题。
你可以发送 1
或 2
个代币,但不能发送 1.5
个。
为了解决这个问题,ERC20 提供了一个 decimals
字段,用于指定代币有多少个小数位。
为了能够转移 1.5 GLD
,decimals
必须至少为 1
,因为该数字具有一位小数。
如何实现这一点?
实际上非常简单:代币合约可以使用更大的整数值,因此余额为 50
将代表 5 GLD
,转移 15
将对应于发送 1.5 GLD
,依此类推。
重要的是要理解 decimals
仅用于显示目的。
合约内的所有算术运算仍然以整数执行,并且必须由不同的用户界面(钱包、交易所等)根据 decimals
调整显示的值。
GLD
中未指定每个帐户的总代币供应量和余额:你需要除以 10 ** decimals
才能获得实际的 GLD
金额。
你可能想要使用 18
的 decimals
值,就像 Ether 和大多数正在使用的 ERC-20 代币合约一样,除非你有特殊原因不这样做。
当铸造代币或转移它们时,你实际上将发送数字 GLD * (10 ** decimals)
。
默认情况下,ERC20 使用 18 作为 decimals 的值。
|
要使用不同的值,你需要覆盖合约中的 decimals()
函数。例如,要使用 16
个小数位,你可以这样做:
fn decimals(&self) -> U8 {
U8::from(16)
}
因此,如果你想使用具有 18
个小数位的代币合约发送 5
个代币,则实际调用的方法将是:
token.transfer(recipient, 5 * uint!(10_U256).pow(uint!(18_U256)));
扩展
此外,还有多个自定义扩展,包括:
-
ERC-20 Burnable:销毁自有代币。
-
ERC-20 Capped:在铸造代币时强制执行总供应量的上限。
-
ERC-20 Metadata:扩展的 ERC20 接口,包括 name、symbol 和 decimals 函数。
-
ERC-20 Pausable:暂停代币转移的能力。
-
ERC-20 Permit:无 Gas 批准代币(标准化为
EIP-2612
)。 -
ERC-4626:代币化的 vault,管理由资产(另一个 ERC-20)支持的股份(表示为 ERC-20)。
-
ERC-20 Flash-Mint:通过临时代币的铸造和销毁,为闪电贷提供代币级别的支持(标准化为
EIP-3156
)。 -
ERC-20 Wrapper:包装器,用于创建由另一个 ERC-20 支持的 ERC-20,具有存款和取款方法。