## Gear 合约开发案例解析 - Multisig Wallet
<p align="center">
<a href="https://gitpod.io/#https://github.com/gear-dapps/multisig-wallet">
<img src="https://gitpod.io/button/open-in-gitpod.svg" width="240" alt="GEAR">
</a>
</p>
代码仓库: https://github.com/gear-dapps/multisig-wallet
本 PPT 链接: https://hackmd.io/@btwiuse/multisig-wallet
---
<img src="https://hackmd.io/_uploads/B1t4w2OP2.png" width="480" alt="multisig">
---
## 什么是多签钱包
- 合约钱包
- 成员 N
- 阈值 M/N
例如: 要求至少 2/3 的成员确认后,交易才会执行
---
## 多签钱包的用途
- 安全性
- 透明性
- 多人决策
---
与传统账户区别

---
消息的二进制编码 ([SCALE Encoding](https://docs.substrate.io/reference/scale-codec/))
<a href="https://idea.gear-tech.io/programs/0x8e31e75055fc906a81188e0dd954046ca6cf555ae6e575369d66536dd47b0dc6?node=wss%3A%2F%2Frpc-node.gear-tech.io">
<img src="https://hackmd.io/_uploads/By6DM6dv2.png" width=560>
</a>
以模板合约为例: https://github.com/gear-dapps/app
---
寻找 Ping 消息的二进制编码
```
use app_io::PingPong;
use gstd::prelude::Encode;
fn main() {
let ping = PingPong::Ping.encode();
println!("PingPong::Ping: {:?}", ping.clone()); // PingPong::Ping: [0]
println!("PingPong::Ping: 0x{}", hex::encode(ping.clone())); // PingPong::Ping: 0x00
let pong = PingPong::Pong.encode();
println!("PingPong::Pong: {:?}", pong.clone()); // PingPong::Pong: [1]
println!("PingPong::Pong: 0x{}", hex::encode(pong.clone())); // PingPong::Pong: 0x01
}
```
---
对于原生/非自定义类型,如 `String`
```
use gstd::prelude::Encode;
fn main() {
let PING = "PING".encode();
println!("\"PING\": {:?}", PING); // "PING": [16, 80, 73, 78, 71]
println!("\"PING\": 0x{}", hex::encode(PING)); // "PING": 0x1050494e47
}
```
---
当 Rust 源码不可用时, 也可使用 @gear-js/api 结合 meta.txt 生成消息 payload
```
import { getProgramMetadata } from "https://gear-js.deno.dev/api/index.ts";
function meta(p: string) {
let data = Deno.readTextFileSync(p);
return getProgramMetadata(`0x${data}`);
}
let metadata = meta("app.meta.txt");
console.log(metadata.getAllTypes());
/*
{
AppIoPingPong: '{"_enum":["Ping","Pong"]}',
GstdCommonPrimitivesActorId: "[u8;32]"
}
*/
console.log(metadata.getTypeIndexByName("AppIoPingPong"));
// 0
console.log("Ping:", metadata.createType(0, { Ping: null }).toHex());
// Ping: 0x00
console.log("Pong:", metadata.createType(0, { Pong: null }).toHex());
// Pong: 0x01
```
---
编码原生 String 类型
```
import {
ApiPromise,
WsProvider,
} from "https://deno.land/x/polkadot@0.2.41/api/index.ts";
import { GearApi } from "https://gear-js.deno.dev/api/index.ts";
import {
u8aToHex,
} from "https://deno.land/x/polkadot@0.2.41/util/index.ts";
async function initGearApi() {
const PROVIDER = Deno.env.get("PROVIDER") ?? "wss://rpc.vara-network.io";
return await GearApi.create({ providerAddress: PROVIDER });
}
console.log("gearApi is initializing. Please hold on...");
const api = await initGearApi();
console.log("PING", u8aToHex(api.createType("String", "PING").toU8a())); // 0x1050494e47
```
---
## build
```
$ make init
$ make build
```
## download
```
./target/wasm32-unknown-unknown/release/multisig_wallet_state.meta.wasm
./target/wasm32-unknown-unknown/release/multisig_wallet.opt.wasm
./multisig_wallet.meta.txt
```
## deploy
https://idea.gear-tech.io/
---
初始化合约

---
合约初始状态

---
发送消息与合约交互

---
## State
./src/lib.rs
```
#[derive(Default)]
pub struct MultisigWallet {
pub transactions: HashMap<TransactionId, Transaction>,
pub confirmations: HashMap<TransactionId, HashSet<ActorId>>,
pub owners: HashSet<ActorId>,
pub required: u32,
pub transaction_count: U256,
}
```
---
## Metadata
./io/src/lib.rs
```
pub struct ContractMetadata;
impl Metadata for ContractMetadata {
type Init = In<MWInitConfig>;
type Handle = InOut<MWAction, MWEvent>;
type Reply = ();
type Others = ();
type Signal = ();
type State = State;
}
```
---
## Init
./io/src/lib.rs
```
#[derive(Debug, Encode, Decode, TypeInfo)]
pub struct MWInitConfig {
pub owners: Vec<ActorId>,
pub required: u32,
}
```
---
## Handle
./io/src/lib.rs
```
#[derive(Debug, Encode, Decode, TypeInfo)]
pub enum MWAction {
AddOwner(ActorId),
RemoveOwner(ActorId),
ReplaceOwner {
old_owner: ActorId,
new_owner: ActorId,
},
ChangeRequiredConfirmationsCount(u32),
SubmitTransaction {
destination: ActorId,
data: Vec<u8>,
value: u128,
description: Option<String>,
},
ConfirmTransaction(U256),
RevokeConfirmation(U256),
ExecuteTransaction(U256),
}
```
---
./src/lib.rs
```
/// Allows an owner to submit and confirm a transaction.
/// `destination` Transaction target address.
/// `value` Transaction value.
/// `data` Transaction data payload.
/// `description` Transaction description.
/// Returns transaction ID.
fn submit_transaction(
&mut self,
destination: &ActorId,
data: Vec<u8>,
value: u128,
description: Option<String>,
) {
let transaction_id = self.add_transaction(destination, data, value, description);
self.confirm_transaction(&transaction_id);
msg::reply(MWEvent::Submission { transaction_id }, 0).unwrap();
}
```
---
## 课后作业
编译 https://github.com/gear-dapps/multisig-wallet
并在 [Gear IDEA](https://idea.gear-tech.io/) Staging V7 测试网上部署
要求 owner = 3 人, 至少 2 人确认后才能执行交易
并通过你的多签钱包合约向[指定 app 合约](https://idea.gear-tech.io/programs/0x8e31e75055fc906a81188e0dd954046ca6cf555ae6e575369d66536dd47b0dc6?node=wss%3A%2F%2Frpc-node.gear-tech.io)发送至少一条 Ping 消息
最后提交合约访问链接
作业提交/课后答疑频道: https://t.me/Gear_CN
---
- 本 PPT 链接: https://hackmd.io/@btwiuse/multisig-wallet
- 参考文章: https://wiki.gear-tech.io/docs/examples/multisig-wallet/
---
# Questions
{"metaMigratedAt":"2023-06-18T07:48:15.062Z","metaMigratedFrom":"YAML","breaks":true,"slideOptions":"{\"theme\":\"solarized\",\"spotlight\":{\"enabled\":false}}","description":"代码仓库: https://github.com/gear-dapps/multisig-wallet","title":"Gear 合约开发案例解析 - Multisig Wallet","contributors":"[{\"id\":\"94262fbf-81ae-4ed7-933c-561a41bd977a\",\"add\":13263,\"del\":7245}]"}