## Gear 合约开发案例解析 - Fungible Token
<p align="center">
<a href="https://gitpod.io/#https://github.com/gear-dapps/fungible-token">
<img src="https://gitpod.io/button/open-in-gitpod.svg" width="240" alt="GEAR">
</a>
</p>
https://github.com/gear-dapps/fungible-token
<!--
- https://github.com/gear-dapps/sharded-fungible-token
-->
---

---
## ERC20 标准
https://eips.ethereum.org/EIPS/eip-20
- name: `My Token`
- symbol: `HEX`
- decimals: `10`
- totalSupply: 10000000000
- balanceOf(address _owner)
- allowance(address _owner, address _spender)
- transferFrom(address _from, address _to, uint256 _value)
- approve(address _spender, uint256 _value)
---
<p align="center">
<a href="https://gitpod.io/#https://github.com/gear-dapps/fungible-token">
<img src="https://gitpod.io/button/open-in-gitpod.svg" width="240" alt="GEAR">
</a>
</p>
---
## build
```
$ make init
$ make build
```
## download
```
./target/wasm32-unknown-unknown/release/ft_state.meta.wasm
./target/wasm32-unknown-unknown/release/fungible_token.opt.wasm
./fungible_token.meta.txt
```
## deploy
https://idea.gear-tech.io/
---
[](https://idea.gear-tech.io/programs/0x5217fc6ef47a7779033835148f76a4da73ae53eca50626471a95ac17f2113efd?node=wss%3A%2F%2Frpc-node.gear-tech.io)
---
[](https://idea.gear-tech.io/send/message/0x5217fc6ef47a7779033835148f76a4da73ae53eca50626471a95ac17f2113efd?node=wss%3A%2F%2Frpc-node.gear-tech.io)
---
[](https://idea.gear-tech.io/state/full/0x5217fc6ef47a7779033835148f76a4da73ae53eca50626471a95ac17f2113efd?node=wss%3A%2F%2Frpc-node.gear-tech.io)
---
## State
./src/contract.rs
```
static mut FUNGIBLE_TOKEN: Option<FungibleToken> = None;
struct FungibleToken {
/// Name of the token.
name: String,
/// Symbol of the token.
symbol: String,
/// Total supply of the token.
total_supply: u128,
/// Map to hold balances of token holders.
balances: HashMap<ActorId, u128>,
/// Map to hold allowance information of token holders.
allowances: HashMap<ActorId, HashMap<ActorId, u128>>,
/// Token's decimals.
pub decimals: u8,
}
```
---
## Metadata
./io/src/lib.rs
```
pub struct FungibleTokenMetadata;
impl Metadata for FungibleTokenMetadata {
type Init = In<InitConfig>;
type Handle = InOut<FTAction, FTEvent>;
...
}
```
---
## Init
./io/src/lib.rs
```
pub struct InitConfig {
pub name: String,
pub symbol: String,
pub decimals: u8,
}
```
---
./src/lib.rs
```
#[no_mangle]
extern "C" fn init() {
let config: InitConfig = msg::load().expect("Unable to decode InitConfig");
let ft = FungibleToken {
name: config.name,
symbol: config.symbol,
decimals: config.decimals,
..Default::default()
};
unsafe { FUNGIBLE_TOKEN = Some(ft) };
}
```
---
## Handle
./io/src/lib.rs
```
pub enum FTAction {
Mint(u128),
Burn(u128),
Transfer {
from: ActorId,
to: ActorId,
amount: u128,
},
Approve {
to: ActorId,
amount: u128,
},
TotalSupply,
BalanceOf(ActorId),
}
```
---
./io/src/lib.rs
```
pub enum FTEvent {
Transfer {
from: ActorId,
to: ActorId,
amount: u128,
},
Approve {
from: ActorId,
to: ActorId,
amount: u128,
},
TotalSupply(u128),
Balance(u128),
}
```
---
./src/contract.rs
```
#[no_mangle]
extern "C" fn handle() {
let action: FTAction = msg::load().expect("Could not load Action");
let ft: &mut FungibleToken = unsafe { FUNGIBLE_TOKEN.get_or_insert(Default::default()) };
match action {
FTAction::Mint(amount) => {
ft.mint(amount);
}
FTAction::Burn(amount) => {
ft.burn(amount);
}
FTAction::Transfer { from, to, amount } => {
ft.transfer(&from, &to, amount);
}
FTAction::Approve { to, amount } => {
ft.approve(&to, amount);
}
FTAction::TotalSupply => {
msg::reply(FTEvent::TotalSupply(ft.total_supply), 0).unwrap();
}
FTAction::BalanceOf(account) => {
let balance = ft.balances.get(&account).unwrap_or(&0);
msg::reply(FTEvent::Balance(*balance), 0).unwrap();
}
}
}
```
---
## Contract
./src/contract.rs
```
impl FungibleToken {
/// Executed on receiving `fungible-token-messages::MintInput`.
fn mint(&mut self, amount: u128) {
...
}
/// Executed on receiving `fungible-token-messages::BurnInput`.
fn burn(&mut self, amount: u128) {
...
}
/// Executed on receiving `fungible-token-messages::TransferInput` or `fungible-token-messages::TransferFromInput`.
/// Transfers `amount` tokens from `sender` account to `recipient` account.
fn transfer(&mut self, from: &ActorId, to: &ActorId, amount: u128) {
...
}
/// Executed on receiving `fungible-token-messages::ApproveInput`.
fn approve(&mut self, to: &ActorId, amount: u128) {
...
}
fn can_transfer(&mut self, from: &ActorId, amount: u128) -> bool {
...
}
}
```
---
## 课后作业
Fork https://github.com/gear-dapps/fungible-token
实现:
```
- 限制任意账户调用 mint 方法
- 为 `FungibleToken` 添加 admin 字段
- 在合约 init 函数中设置 admin = msg::source()
- 在 `FungibleToken` 的 init 方法中确保用户为 admin,否则 panic
```
在 [Gear IDEA](https://idea.gear-tech.io/) 上部署你的 Fork
提交 Fork 仓库地址及合约访问链接
作业提交频道: https://t.me/Gear_CN
{"metaMigratedAt":"2023-06-18T04:38:31.032Z","metaMigratedFrom":"YAML","title":"Untitled","breaks":false,"slideOptions":"{\"theme\":\"moon\",\"spotlight\":{\"enabled\":false}}","contributors":"[{\"id\":\"94262fbf-81ae-4ed7-933c-561a41bd977a\",\"add\":10250,\"del\":5951}]"}