# スプートニクDAO工場契約の解釈## 1. スプートニク-DAO工場契約Sputnik-DAOは、創造的なファクトリーデザインパターンを採用して、プラットフォーム下の分散型自治組織(DAO)の統一的な作成と管理を実現しました。本文では、Sputnik-DAOプラットフォームのファクトリーモード(sputnikdao-factory)の設計実装について詳しく説明します。対応する契約のソースコードリポジトリは次の場所にあります:! [](https://img-cdn.gateio.im/social/moments-22ee206b84145d4d511a0b0fec691dce)## 2. DAPPモジュールの機能の紹介Sputnik DAOプラットフォームのDAPPページには、多くの分散型自律組織が作成し、自身のDAOインスタンスオブジェクト(Sputnikdaov2契約)をカスタマイズしています。2022年3月現在、このプラットフォームで最も活発なDAOはnews.sputnik-dao.nearであり、3051件の提案(proposals)が公開投票中または状態が終了しています。NEAR Explorerを通じて発見されたように、このプラットフォームの各DAOインスタンス契約は、NEARアカウントsputnik-dao.near(および契約)によって統一的にデプロイされています。すべての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インスタンスを作成します。! [](https://img-cdn.gateio.im/social/moments-78921e045e487159a6dd01b0f3466573)## 3. スプートニクダオ-ファクトリー契約コードの解釈### 3.1 DAOを作成するスプートニクダオ-工場の契約状態は、以下の二つの部分から成り立っている。さび公開構造体 SputnikDAOFactory { factory_manager:ファクトリーマネージャー、 daos: UnorderedSet<accountid>,}- factory_manager:契約の主要な内部機能ロジックの実装、DAOインスタンスを作成/削除/更新するための一連のメソッドを提供します。- daos:集合データ構造を採用し、このプラットフォームで歴史的に作成されたすべてのDAOインスタンスのNEARアカウントアドレスを記録しています。DAOインスタンスを作成するために使用されるsputnikdao-factoryコントラクトのメソッドcreate()は次のように定義されています:さび#[payable]pub fn create(&mut self, name: AccountId, args: Base64VecU8) { account_id: AccountId = format!('{} です。 {}', 名前, 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::p redecessor_account_id() })) .expect('シリアライズに失敗しました'); self.factory_manager.create_contract( self.get_default_code_hash()、 account_id、 'new'、 &args.0 です。 'on_create'、 &callback_args、 );}! [](https://img-cdn.gateio.im/social/moments-db234efe334b609e12be80398e4e6fa5)factory_manager.create_contract の実装:錆パブFN create_contract( &self や code_hash:Base58CryptoHash、 account_id: AccountId、 new_method: &str, 引数: &[u8], callback_method: &str, callback_args: &[u8],) { code_hashしましょう:CryptoHash = code_hash.into(); 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(); 安全でない{ // このような契約が存在するか確認します。 assert_eq!( sys::storage_has_key(code_hash.len() を _ として、code_hash.as_ptr() を _ として ), 1, '契約は存在しません' ); // レジスタ0に入力(のwasmコード)をロードします。 sys::storage_read(code_hash.len() を _ として、code_hash.as_ptr() を _ として、 0); // アカウントIDにPromise txをスケジュールする let promise_id = sys::p romise_batch_create(account_id.len() を _ として、account_id.as_ptr() を _ として ); // まずアカウントを作成します。 sys::p romise_batch_action_create_account(promise_id); // 添付された入金を転送します。 sys::p romise_batch_action_transfer(promise_id, &attached_deposit as *const u128 as _); // コントラクトをデプロイ (コードは登録から取得されています 0). sys::p romise_batch_action_deploy_contract(promise_id, u64::MAX as _, 0); 指定された引数で new を呼び出します。 sys::p romise_batch_action_function_call( promise_id、 new_method.len() として _, new_method.as_ptr() を _ として、 args.len() を _ として、 args.as_ptr() を _ として指定します。 &NO_DEPOSIT を *const u128 を _ として、 CREATE_CALL_GAS.0、 ); // コールバックをファクトリーにアタッチします。 let _ = sys::p romise_then( promise_id、 factory_account_id.len() as _, factory_account_id.as_ptr() を _ として、 callback_method.len() as _, callback_method.as_ptr() を _ として、 callback_args.len() as _, callback_args.as_ptr() を _ として、 &NO_DEPOSIT を *const u128 を _ として、 ON_CREATE_CALL_GAS.0、 ); sys::p romise_return(promise_id); }}! [](https://img-cdn.gateio.im/social/moments-373080ddb66a4cb83e0722b387056be0)コールバック関数の内部コード実装on_create:錆#[private]パブFN on_create( &mut self、 account_id: AccountId、 attached_deposit:U128、 predecessor_account_id: AccountId、) -> ブール { if near_sdk::is_promise_success() { self.daos.insert(&account_id); 真 } else { 約束::new(predecessor_account_id).transfer(attached_deposit.0); 偽 }}! [](https://img-cdn.gateio.im/social/moments-8b462e3ac0fd55e990921d21d66e3d42)### 3.2 アップデート DAO工場契約が提供する契約インターフェースupdate():錆/// このファクトリーによって作成された指定されたコードに対して、与えられたアカウントを更新しようとします。pub fn update( self, account_id: AccountId, code_hash: Base58CryptoHash) { let caller_id = env::p redecessor_account_id(); アサート!( caller_id == self.get_owner() || caller_id == account_id、 '工場のオーナーまたはDAO自体によって更新される必要があります' ); アサート!( self.daos.contains(&account_id)、 'ファクトリーによって作成された契約である必要があります' ); self.factory_manager .update_contract(account_id、code_hash、「更新」);}! [](https://img-cdn.gateio.im/social/moments-a8d69d504693c5c14767aed9244a090a)factory_manager.update_contract() 処理の詳細:錆/// 指定された契約の更新を強制します。/// コントラクトは、このパーミッションチェックのためにファクトリーによる更新をサポートする必要があります。パブFN update_contract( &self や account_id: AccountId、 code_hash:Base58CryptoHash、 method_name: &str,) { code_hashしましょう:CryptoHash = code_hash.into(); let account_id = account_id.as_bytes().to_vec(); 安全でない{ // そのような契約が存在することを確認します。 assert!(env::storage_has_key(&code_hash), '契約が存在しません'); // ストレージからハッシュをロードします。 sys::storage_read(code_hash.len() を _ として、code_hash.as_ptr() を _ として、 0); // 指定されたアカウントに対するプロミスを作成します。 let promise_id = sys::p romise_batch_create(account_id.len() を _ として、account_id.as_ptr() を _ として ); // 更新メソッドを呼び出します。これにはマイグレーションも処理される必要があります。 sys::p romise_batch_action_function_call( promise_id、 method_name.len() として _, method_name.as_ptr() を _ として、 u64::MAX を _ として、 0, &NO_DEPOSIT を *const u128 を _ として、 (env::p repaid_gas() - 環境変数::used_gas() - GAS_UPDATE_ LEFTOVER).0,); sys::p romise_return(promise_id); }}! [](https://img-cdn.gateio.im/social/moments-729b65b1335931b4645ff5a9981d125b)## 4. スプートニク-DAOファクトリー契約セキュリティ分析Sputnik-DAO Factory契約の安全性は主に以下のいくつかの側面から保証されています:- 権限管理: コントラクトのオープンなviewタイプのメソッドは、コントラクトの状態変数を変更すべきではなく、メソッド定義の最初のパラメータは&selfに設定する必要があります。- 権限管理: コントラクトが開放している特権関数、これらの関数はコントラクトのowner(またはDAOコントラクトアカウント)によってのみ実行され、メソッド内に相応のassertionが存在します。- エラーハンドリング: Sputnik-DAO Factory契約は、発生する可能性のある例外状況に対して合理的なエラーハンドリングメカニズムを実装しています。例えば、ユーザーがFactory契約を使用して新しいDAOインスタンス契約を作成する際、すべての手順が正常に完了しているかどうかを最終的に確認します。そうでない場合、ユーザーに損失を与えるべきではありません。! [](https://img-cdn.gateio.im/social/moments-074c1aa0b7f9adfa5780a29c73654fc0)! [](https://img-cdn.gateio.im/social/moments-aebf6315f45abba98b7345259ffe8b5d)! [](https://img-cdn.gateio.im/social/moments-a3a84d139b8950498848a5cafbdfea83)! [](https://img-cdn.gateio.im/social/moments-46f1a89001b11b13b1a8d1b484d4877d)</accountid>
Sputnik DAOファクトリー契約解析:DAOのコアメカニズムの作成と更新
スプートニクDAO工場契約の解釈
1. スプートニク-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(および契約)によって統一的にデプロイされています。
すべてのSputnik DAOプラットフォームに基づいて作成されたDAOインスタンス契約は、それぞれこのNEARアカウントのサブアカウントにデプロイされます。例えば:
分散型組織は、NEARメインネットで公開に取引を開始でき、sputnikdao-factoryコントラクトが提供するcreate()メソッドを呼び出すことで、新しいDAOインスタンスを作成します。
!
3. スプートニクダオ-ファクトリー契約コードの解釈
3.1 DAOを作成する
スプートニクダオ-工場の契約状態は、以下の二つの部分から成り立っている。
さび 公開構造体 SputnikDAOFactory { factory_manager:ファクトリーマネージャー、 daos: UnorderedSet, }
factory_manager:契約の主要な内部機能ロジックの実装、DAOインスタンスを作成/削除/更新するための一連のメソッドを提供します。
daos:集合データ構造を採用し、このプラットフォームで歴史的に作成されたすべてのDAOインスタンスのNEARアカウントアドレスを記録しています。
DAOインスタンスを作成するために使用されるsputnikdao-factoryコントラクトのメソッドcreate()は次のように定義されています:
さび #[payable] pub fn create(&mut self, name: AccountId, args: Base64VecU8) { account_id: AccountId = format!('{} です。 {}', 名前, 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::p redecessor_account_id() })) .expect('シリアライズに失敗しました'); self.factory_manager.create_contract( self.get_default_code_hash()、 account_id、 'new'、 &args.0 です。 'on_create'、 &callback_args、 ); }
!
factory_manager.create_contract の実装:
錆 パブFN create_contract( &self や code_hash:Base58CryptoHash、 account_id: AccountId、 new_method: &str, 引数: &[u8], callback_method: &str, callback_args: &[u8], ) { code_hashしましょう:CryptoHash = code_hash.into(); 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(); 安全でない{ // このような契約が存在するか確認します。 assert_eq!( sys::storage_has_key(code_hash.len() を _ として、code_hash.as_ptr() を _ として ), 1, '契約は存在しません' ); // レジスタ0に入力(のwasmコード)をロードします。 sys::storage_read(code_hash.len() を _ として、code_hash.as_ptr() を _ として、 0); // アカウントIDにPromise txをスケジュールする let promise_id = sys::p romise_batch_create(account_id.len() を _ として、account_id.as_ptr() を _ として ); // まずアカウントを作成します。 sys::p romise_batch_action_create_account(promise_id); // 添付された入金を転送します。 sys::p romise_batch_action_transfer(promise_id, &attached_deposit as *const u128 as _); // コントラクトをデプロイ (コードは登録から取得されています 0). sys::p romise_batch_action_deploy_contract(promise_id, u64::MAX as _, 0); 指定された引数で new を呼び出します。 sys::p romise_batch_action_function_call( promise_id、 new_method.len() として _, new_method.as_ptr() を _ として、 args.len() を _ として、 args.as_ptr() を _ として指定します。 &NO_DEPOSIT を *const u128 を _ として、 CREATE_CALL_GAS.0、 ); // コールバックをファクトリーにアタッチします。 let _ = sys::p romise_then( promise_id、 factory_account_id.len() as _, factory_account_id.as_ptr() を _ として、 callback_method.len() as _, callback_method.as_ptr() を _ として、 callback_args.len() as _, callback_args.as_ptr() を _ として、 &NO_DEPOSIT を *const u128 を _ として、 ON_CREATE_CALL_GAS.0、 ); sys::p romise_return(promise_id); } }
!
コールバック関数の内部コード実装on_create:
錆 #[private] パブFN on_create( &mut self、 account_id: AccountId、 attached_deposit:U128、 predecessor_account_id: AccountId、 ) -> ブール { if near_sdk::is_promise_success() { self.daos.insert(&account_id); 真 } else { 約束::new(predecessor_account_id).transfer(attached_deposit.0); 偽 } }
!
3.2 アップデート DAO
工場契約が提供する契約インターフェースupdate():
錆 /// このファクトリーによって作成された指定されたコードに対して、与えられたアカウントを更新しようとします。 pub fn update( self, account_id: AccountId, code_hash: Base58CryptoHash) { let caller_id = env::p redecessor_account_id(); アサート!( caller_id == self.get_owner() || caller_id == account_id、 '工場のオーナーまたはDAO自体によって更新される必要があります' ); アサート!( self.daos.contains(&account_id)、 'ファクトリーによって作成された契約である必要があります' ); self.factory_manager .update_contract(account_id、code_hash、「更新」); }
!
factory_manager.update_contract() 処理の詳細:
錆 /// 指定された契約の更新を強制します。 /// コントラクトは、このパーミッションチェックのためにファクトリーによる更新をサポートする必要があります。 パブFN update_contract( &self や account_id: AccountId、 code_hash:Base58CryptoHash、 method_name: &str, ) { code_hashしましょう:CryptoHash = code_hash.into(); let account_id = account_id.as_bytes().to_vec(); 安全でない{ // そのような契約が存在することを確認します。 assert!(env::storage_has_key(&code_hash), '契約が存在しません'); // ストレージからハッシュをロードします。 sys::storage_read(code_hash.len() を _ として、code_hash.as_ptr() を _ として、 0); // 指定されたアカウントに対するプロミスを作成します。 let promise_id = sys::p romise_batch_create(account_id.len() を _ として、account_id.as_ptr() を _ として ); // 更新メソッドを呼び出します。これにはマイグレーションも処理される必要があります。 sys::p romise_batch_action_function_call( promise_id、 method_name.len() として , method_name.as_ptr() を _ として、 u64::MAX を _ として、 0, &NO_DEPOSIT を *const u128 を _ として、 (env::p repaid_gas() - 環境変数::used_gas() - GAS_UPDATE LEFTOVER).0,); sys::p romise_return(promise_id); } }
!
4. スプートニク-DAOファクトリー契約セキュリティ分析
Sputnik-DAO Factory契約の安全性は主に以下のいくつかの側面から保証されています:
権限管理: コントラクトのオープンなviewタイプのメソッドは、コントラクトの状態変数を変更すべきではなく、メソッド定義の最初のパラメータは&selfに設定する必要があります。
権限管理: コントラクトが開放している特権関数、これらの関数はコントラクトのowner(またはDAOコントラクトアカウント)によってのみ実行され、メソッド内に相応のassertionが存在します。
エラーハンドリング: Sputnik-DAO Factory契約は、発生する可能性のある例外状況に対して合理的なエラーハンドリングメカニズムを実装しています。例えば、ユーザーがFactory契約を使用して新しいDAOインスタンス契約を作成する際、すべての手順が正常に完了しているかどうかを最終的に確認します。そうでない場合、ユーザーに損失を与えるべきではありません。
!
!
!
!