🎉 亲爱的广场小伙伴们,福利不停,精彩不断!目前广场上这些热门发帖赢奖活动火热进行中,发帖越多,奖励越多,快来 GET 你的专属好礼吧!🚀
1️⃣ #TokenOfLove# |广场音乐节打 CALL
为偶像打 CALL,Gate 广场送你直达 Token of Love!泫雅、SUECO、DJ KAKA、CLICK#15 —— 你最想 pick 谁?现在在广场带上 歌手名字 + TokenOfLove 标签发帖应援,就有机会赢取 20 张音乐节门票。
详情 👉 https://www.gate.com/post/status/13214134
2️⃣ #GateTravel旅行分享官# |晒旅程赢好礼
广场家人们注意啦,Gate Travel 已经上线。带话题发帖,分享你的 Gate Travel 旅行体验、心愿清单、使用攻略或趣味见闻,就有机会获得旅行露营套装、速干套装、国际米兰旅行小夜灯等好礼!
详情 👉 https://www.gate.com/post/status/13172887
3️⃣ #内容挖矿# |发帖还能赚钱
广场长期活动进行中,最高可享 10% 手续费返佣!发布优质内容,如行情解析、交易观点等,吸引更多用户点赞和评论。若用户在互动后 180 分钟内完成现货或合约交易,你将获得最高 10% 的手续费返佣!
详情 👉 https://www.gate.
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实例合约最后会检查创建所有的步骤时候都已正常完整地执行,否则不应对用户造成损失。