## 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 的成员确认后,交易才会执行 --- ## 多签钱包的用途 - 安全性 - 透明性 - 多人决策 --- 与传统账户区别 ![](https://hackmd.io/_uploads/rJnwWaOwh.png) --- 消息的二进制编码 ([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/ --- 初始化合约 ![](https://hackmd.io/_uploads/SJLv_AuD3.png) --- 合约初始状态 ![](https://hackmd.io/_uploads/ryRzYR_w3.png) --- 发送消息与合约交互 ![](https://hackmd.io/_uploads/Sk8n_C_vh.png) --- ## 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}]"}
    306 views