✈️ 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實例合約最後會檢查創建所有的步驟時候都已正常完整地執行,否則不應對用戶造成損失。