6 个版本 (2 个稳定版本)
2.0.0 | 2024 年 5 月 7 日 |
---|---|
1.0.0 | 2024 年 5 月 6 日 |
0.3.0 | 2024 年 3 月 26 日 |
0.2.2 | 2024 年 2 月 29 日 |
0.2.1 | 2023 年 11 月 10 日 |
#709 in 魔法豆
每月下载量 1,016 次
在 wrapped-azero 中使用
57KB
957 行
PSP22 兑换令牌
PSP22 是一个用于在基于 Substrate 框架的区块链上运行的 WebAssembly 智能合约的可兑换令牌标准。它与以太坊的 ERC-20 相当。PSP22 标准的定义可以在 这里 找到。
此存储库包含 PSP22 令牌在 ink! 智能合约编程语言(基于 Rust 的 EDSL)中的简单、最小实现。
如何使用此存储库
[!IMPORTANT] 此版本的 psp22 crate 与 ink! 5 兼容。为了与 ink! 4 兼容,请使用 1.x.x 版本。
要将此存储库作为依赖项使用,请在其项目的 Cargo.toml
中添加以下行:
psp22 = { version = "2.0", default-features = false, features = ["ink-as-dependency"] }
# ...
[features]
# ...
std = [
# ...
"psp22/std",
]
此存储库的内容可以按以下方式使用:
1. 即用型合约
文件 lib.rs
包含一个即用型的基本 PSP22 令牌合约实现(扩展了 PSP22Metadata)。要使用它,请检出此存储库并使用 cargo-contract
编译其内容。
cargo contract build --release
2. 使用 traits 的跨合约调用
PSP22 特性包含了 PSP22 标准中定义的所有方法。该特性可以与 ink! 的
contract_ref
宏一起使用,以方便跨合约调用。
在你的合约中,如果你想要调用实现 PSP22 标准的其他合约,你只需做以下操作
use ink::contract_ref;
use ink::prelude::vec;
use psp22::PSP22;
let mut token: contract_ref!(PSP22) = psp22_contract_address.into();
// Now `token` has all PSP22 methods
let balance = token.balance_of(some_account);
token.transfer(recipient, balance, vec![]); // returns Result<(), PSP22Error>
相同的方法也可以用于其他特性(如 PSP22Metadata
、PSP22Burnable
、PSP22Mintable
),这些特性在这个 crate 中定义。有关详细信息,请参阅 traits.rs
的内容。
3. 使用 PSP22Data
自定义实现 PSP22 逻辑
PSP22Data
类可用于扩展您的合约以包含 PSP22 令牌逻辑。换句话说,您可以轻松构建同时实现 PSP22 接口和一些项目业务逻辑定义的其他功能的合约。
PSP22Data
类的方法直接对应于由 PSP22 令牌标准定义的查询和操作。要使您的合约成为 PSP22 令牌,您需要
- 在您的合约存储中放置一个
PSP22Data
实例,并用一些初始令牌供应量对其进行初始化。 - 添加一个
impl PSP22 for [struct_name]
块,并使用PSP22Data
方法实现 PSP22 特性消息。每个修改令牌数据库状态的方法都会返回一个包含该操作生成的所有事件的Result<Vec<PSP22Event>, PSP22Error>
。请确保正确处理错误并发出结果事件(请参阅emit_events
函数)。 - 可选地实现
PSP22Metadata
特性,以便您的令牌与生态系统工具良好兼容。 - 可选地添加带有
tests!
宏的单元测试(见下文)
lib.rs
中的合约包含一个示例实现,遵循上述所有步骤。您可以随意复制粘贴其中的一部分。
4. 单元测试
此 crate 包含一组 PSP22 令牌的单元测试。您可以使用辅助宏 tests!
将其轻松添加到合约的单元测试中。该宏应在主合约模块(带有 #[ink::contract]
注解的模块)内部调用
#[ink::contract]
mod mycontract {
...
#[ink(storage)]
pub struct MyContract { ... }
...
#[cfg(test)]
mod tests {
use super::MyContract;
psp22::tests!(MyContract, (|total_supply| MyContract::new(..., total_supply, ...)));
}
}
如上述代码片段所示,psp22::tests!
宏接受两个参数。第一个参数应该是实现 PSP22
特性的结构体的名称(通常是您的合约存储结构体)。第二个参数应该是给定总供应量的令牌构造函数。换句话说,第二个参数应该是一个表达式,它接受单个 u128
参数,并返回一个初始化为具有该数量作为总供应量的 PSP22 结构体(所有令牌最初分配给调用者的账户)。
5. 可燃烧和可铸造扩展
PSP22Data
类还包含 burn
和 mint
方法,这些方法可以用来实现 PSP22Burnable
和 PSP22Mintable
扩展,使您的代币可燃烧和/或可铸造。以下是一个示例实现,遵循基本特质的相同模式。
impl PSP22Burnable for Token {
#[ink(message)]
fn burn(&mut self, value: u128) -> Result<(), PSP22Error> {
let events = self.data.burn(self.env().caller(), value)?;
self.emit_events(events);
Ok(())
}
}
请注意,PSP22Data
的 burn
和 mint
方法不强制执行任何形式的访问控制。让任何人随时都可以铸造和燃烧代币可能不是一个好主意。在实现可燃烧和可铸造扩展时,请确保它们的用法符合您项目的业务逻辑。例如
#[ink(storage)]
pub struct Token {
data: PSP22Data,
name: Option<String>,
symbol: Option<String>,
decimals: u8,
owner: AccountId, // creator of the token
}
impl Token {
#[ink(constructor)]
pub fn new(
supply: u128,
name: Option<String>,
symbol: Option<String>,
decimals: u8,
) -> Self {
Self {
data: PSP22Data::new(supply, Self::env().caller()),
name,
symbol,
decimals,
owner: Self::env().caller(),
}
}
// ...
}
impl PSP22Burnable for Token {
#[ink(message)]
fn burn(&mut self, value: u128) -> Result<(), PSP22Error> {
if self.env().caller() != self.owner {
return PSP22Error::Custom(String::from("Only owner can burn"));
}
let events = self.data.burn(self.env().caller(), value)?;
self.emit_events(events);
Ok(())
}
}
依赖项
~5.5–9MB
~167K SLoC