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账户的子账户下,例如:

  • pcp.sputnik-dao.near
  • test-dao-bro.sputnik-dao.near
  • blaqkstereo.sputnik-dao.near
  • octopode-dao.sputnik-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实例合约最后会检查创建所有的步骤时候都已正常完整地执行,否则不应对用户造成损失。

SYS-7.96%
此页面可能包含第三方内容,仅供参考(非陈述/保证),不应被视为 Gate 认可其观点表述,也不得被视为财务或专业建议。详见声明
  • 赞赏
  • 7
  • 转发
  • 分享
评论
0/400
链游脱坑专家vip
· 07-26 22:26
无聊的工厂模式有啥好说的
回复0
空投猎人007vip
· 07-26 03:54
这个工厂还能不能打一点
回复0
DeFiVeteranvip
· 07-24 02:49
叫我dao老兵
回复0
孤独锚定师vip
· 07-24 02:46
又看了但没看懂 浪费我三秒钟
回复0
精神财富收割机vip
· 07-24 02:32
好家伙 工厂模式这么复杂的吗
回复0
烤猫铁粉vip
· 07-24 02:22
来个人翻译下,看懵了
回复0
交易,随时随地
qrCode
扫码下载 Gate App
社群列表
简体中文
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)