✈️ Gate 广场【Gate Travel 旅行分享官召集令】
广场家人们注意啦!Gate Travel 已经上线~ 机票+酒店一站式预订,还能用加密货币直接付款 💸
所以说,你的钱包和你的旅行梦终于可以谈恋爱了 😎 💕
现在广场开启 #GateTravel旅行分享官# 活动,邀你来秀旅行灵感 & 使用体验!💡
🌴 参与方式:
1️⃣ 在【广场】带话题 #Gate Travel 旅行分享官# 发帖
2️⃣ 你可以:
你最想用 Gate Travel 去的目的地(私藏小岛 or 网红打卡点都行)
讲讲用 Gate Travel 订票/订酒店的奇妙体验
放放省钱/使用攻略,让大家省到笑出声
或者直接写一篇轻松的 Gate Travel 旅行小故事
📦 奖励安排,走起:
🏆 优秀分享官(1 名):Gate 旅行露营套装
🎖️ 热门分享官(3 名):Gate 旅行速干套装
🎉 幸运参与奖(5 名):Gate 国际米兰旅行小夜灯
*海外用户 旅行露营套装 以 $100 合约体验券,旅行速干套装 以 $50 合约体验券折算,国际米兰旅行小夜灯以 $30合约体验券折算。
📌 优质内容将有机会得到官方账号转发翻牌提升社区曝光!
📌 帖文将综合互动量、内容丰富度和创意评分。禁止小号刷贴,原创分享更容易脱颖而出!
🕒 8月20 18:00 - 8月28日 24:00 UTC+
Sputnik DAO工厂合约解析:创建与更新DAO的核心机制
Sputnik DAO工厂合约解读
1. Sputnik-DAO工厂合约
Sputnik-DAO采用创建型工厂设计模式实现了该平台下去中心化自治组织(DAO)的统一创建与管理。
本文将详细介绍Sputnik-DAO平台工厂模式(sputnikdao-factory)的设计实现。
对应合约的源代码仓库位于:
2. DAPP模块功能介绍
Sputnik DAO平台的DAPP页面已有不少去中心化自治组织创建并定制了属于自己的DAO实例对象(Sputnikdaov2合约)。
截至2022年3月,该平台下创建最活跃的DAO为news.sputnik-dao.near,其中已有3051个提案(proposals)正在公开投票中或状态已结。
通过NEAR Explorer探索可发现,该平台各DAO实例合约由NEAR账户sputnik-dao.near(sputnikdao-factory合约)统一部署。
即所有基于Sputnik DAO平台创建的DAO实例合约分别被部署在该NEAR账户的子账户下,例如:
去中心化组织可在NEAR主网中公开发起交易,通过调用sputnikdao-factory合约所提供的create()方法,创建新的DAO实例。
3. sputnikdao-factory合约代码解读
3.1 创建DAO
sputnikdao-factory合约状态主要由如下两个部分组成:
rust pub struct SputnikDAOFactory { factory_manager: FactoryManager, daos: UnorderedSet, }
factory_manager:合约主要的内部功能逻辑实现,提供了一系列创建/删除/更新DAO实例的方法。
daos:采用集合数据结构,记录了该平台历史上所有已创建DAO实例的NEAR账户地址。
创建DAO实例所使用的sputnikdao-factory合约方法create()定义如下:
rust #[payable] pub fn create(&mut self, name: AccountId, args: Base64VecU8) { let account_id: AccountId = format!('{}.{}', name, env::current_account_id()) .parse() .unwrap(); let callback_args = serde_json::to_vec(&json!({ 'account_id': account_id, 'attached_deposit': U128(env::attached_deposit()), 'predecessor_account_id': env::predecessor_account_id() })) .expect('Failed to serialize'); self.factory_manager.create_contract( self.get_default_code_hash(), account_id, 'new', &args.0, 'on_create', &callback_args, ); }
factory_manager.create_contract的具体实现:
rust pub fn create_contract( &self, code_hash: Base58CryptoHash, account_id: AccountId, new_method: &str, args: &[u8], callback_method: &str, callback_args: &[u8], ) { let code_hash: CryptoHash = code_hash.into(); let attached_deposit = env::attached_deposit(); let factory_account_id = env::current_account_id().as_bytes().to_vec(); let account_id = account_id.as_bytes().to_vec(); unsafe { // Check that such contract exists. assert_eq!( sys::storage_has_key(code_hash.len() as _, code_hash.as_ptr() as _), 1, 'Contract doesn't exist' ); // Load input (wasm code) into register 0. sys::storage_read(code_hash.len() as _, code_hash.as_ptr() as _, 0); // schedule a Promise tx to account_id let promise_id = sys::promise_batch_create(account_id.len() as _, account_id.as_ptr() as _); // create account first. sys::promise_batch_action_create_account(promise_id); // transfer attached deposit. sys::promise_batch_action_transfer(promise_id, &attached_deposit as *const u128 as _); // deploy contract (code is taken from register 0). sys::promise_batch_action_deploy_contract(promise_id, u64::MAX as _, 0); // call new with given arguments. sys::promise_batch_action_function_call( promise_id, new_method.len() as _, new_method.as_ptr() as _, args.len() as _, args.as_ptr() as _, &NO_DEPOSIT as *const u128 as _, CREATE_CALL_GAS.0, ); // attach callback to the factory. let _ = sys::promise_then( promise_id, factory_account_id.len() as _, factory_account_id.as_ptr() as _, callback_method.len() as _, callback_method.as_ptr() as _, callback_args.len() as _, callback_args.as_ptr() as _, &NO_DEPOSIT as *const u128 as _, ON_CREATE_CALL_GAS.0, ); sys::promise_return(promise_id); } }
回调函数on_create的内部代码实现:
rust #[private] pub fn on_create( &mut self, account_id: AccountId, attached_deposit: U128, predecessor_account_id: AccountId, ) -> bool { if near_sdk::is_promise_success() { self.daos.insert(&account_id); true } else { Promise::new(predecessor_account_id).transfer(attached_deposit.0); false } }
3.2 更新DAO
工厂合约所提供的合约接口update():
rust /// Tries to update given account created by this factory to the specified code. pub fn update(&self, account_id: AccountId, code_hash: Base58CryptoHash) { let caller_id = env::predecessor_account_id(); assert!( caller_id == self.get_owner() || caller_id == account_id, 'Must be updated by the factory owner or the DAO itself' ); assert!( self.daos.contains(&account_id), 'Must be contract created by factory' ); self.factory_manager .update_contract(account_id, code_hash, 'update'); }
factory_manager.update_contract()处理细节:
rust /// Forces update on the given contract. /// Contract must support update by factory for this via permission check. pub fn update_contract( &self, account_id: AccountId, code_hash: Base58CryptoHash, method_name: &str, ) { let code_hash: CryptoHash = code_hash.into(); let account_id = account_id.as_bytes().to_vec(); unsafe { // Check that such contract exists. assert!(env::storage_has_key(&code_hash), 'Contract doesn't exist'); // Load the hash from storage. sys::storage_read(code_hash.len() as _, code_hash.as_ptr() as _, 0); // Create a promise toward given account. let promise_id = sys::promise_batch_create(account_id.len() as _, account_id.as_ptr() as _); // Call update method, which should also handle migrations. sys::promise_batch_action_function_call( promise_id, method_name.len() as _, method_name.as_ptr() as _, u64::MAX as _, 0, &NO_DEPOSIT as *const u128 as _, (env::prepaid_gas() - env::used_gas() - GAS_UPDATE_LEFTOVER).0,); sys::promise_return(promise_id); } }
4. Sputnik-DAO Factory合约安全性分析
Sputnik-DAO Factory合约的安全性主要从如下几个方面进行保证:
权限控制:合约开放的view类方法,不应修改合约的状态变量,即方法定义中的第一个参数需设置为&self,而非&mut self。
权限控制:合约开放的特权函数,这些函数只能由合约owner(或DAO合约账户)执行,并在方法中存在相应的assertion。
错误处理:Sputnik-DAO Factory合约对可能发生的异常情况都实现了相应合理的错误处理机制。例如用户使用Factory合约创建新DAO实例合约最后会检查创建所有的步骤时候都已正常完整地执行,否则不应对用户造成损失。