1. Sputnik-DAO Factory ContractSputnik-DAO adopts the creational factory design pattern (Factory Pattern) to realize the unified creation and management of the decentralized autonomous organization (DAO) of the platform.
This article will introduce the design and implementation of the Sputnik-DAO platform factory pattern (sputnikdao-factory) in detail.
The source code warehouse of the corresponding contract is located at: https://github.com/near-daos/sputnik-dao-contract/tree/518ad1d97614fff4b945aba75b6c8bd2483187a2

first level title
2. DAPP module function introduction
Open the DAPP page of the Sputnik DAO platform, it can be seen that many decentralized autonomous organizations have created and customized their own DAO instance objects (Sputnikdaov2 contract) on the platform.
As of March 2022, the most active DAO created under this platform is news.sputnik-dao.near, in which 3051 proposals (proposals) are in public voting or the status has been closed.

📄For the convenience of readers, the structure diagram of the contract is provided above for reference.

That is, all DAO instance contracts created based on the Sputnik DAO platform are deployed under the sub-accounts of the NEAR account, for example:
pcp.sputnik-dao.near
test-dao-bro.sputnik-dao.near
blaqkstereo.sputnik-dao.near
octopode-dao.sputnik-dao.near
For the definition of subaccounts in NEAR Protocol, you can get a reference at https://docs.near.org/docs/concepts/account#subaccounts 🔗.
As shown in the figure below, decentralized organizations can openly initiate transactions on the NEAR main network, and create new DAO instances by calling the create() method provided by the sputnikdao-factory contract.

3. Interpretation of sputnikdao-factory contract code
In order to help you better understand the writing method of Rust factory mode contract, this article will deeply interpret the contract code of sputnikdao-factory.
3.1 Creating DAOs
The sputnikdao-factory contract status mainly consists of the following two parts:

factory_manager: The main internal function logic implementation of the contract, providing a series of methods to create/delete/update DAO instances.
daos: adopts a collection data structure, which records the NEAR account addresses of all created DAO instances in the history of the platform.
The sputnikdao-factory contract method create() used to create DAO instances is defined as follows:

The function of lines 3-5 of the code is to complete the user name specified by the function parameter when calling the create() method, so as to obtain the NEAR sub-account address for deploying the DAO contract in the future. Here env::current_account_id() refers to the address of the sputnikdao-factory contract, namely sputnik-dao.near.
Lines 6-11 of the code construct the function parameter of the callback function on_create after the create() method calls factory_manager.create_contract.
Lines 12-19 of the code call the create_contract interface provided by factory_manager in the factory contract to create and deploy a new DAO instance contract for the caller of the create() method. At the same time, for the newly deployed DAO instance contract, the basic configuration information of the contract can be passed in the form of Base64 string through the create_contract parameter args.
The following is a transaction used by a decentralized organization in the NEAR mainnet to create a DAO instance contract on the Sputnik-DAO platform:
FyECaggFxATGaUMrRKkbotRWAPkhjw5SBnZfRHpzSiQ8🔗
This transaction calls the create() method in the sputnikdao-factory contract code, realizes the creation of the multicall.sputnik-dao.near sub-account, and successfully deploys the contract code of the corresponding DAO instance (the specific implementation details will be detailed later expand the description).


The specific content of the args parameter after Base64 decoding is:
This content is exactly the contract configuration information required when executing the contract initialization method new() when deploying the multicall.sputnik-dao.near contract.
The following article will analyze the specific implementation of factory_manager.create_contract in detail:

The parameters of this function are specified as follows:
code_hash: The hash value of the standard DAO instance contract template code provided by the Sputnik-DAO platform.
account_id: The deployment account of the newly created DAO instance contract in the future, such as multicall.sputnik-dao.near, the content of this parameter has been constructed in the upper function create() of create_contract().
new_method: Specifies the contract initialization function in the newly created DAO instance contract, generally new().
args: The configuration information required to execute the DAO instance contract initialization function new(), including the following two aspects:
DAO basic information provided by the decentralized autonomous organization: Config

5. callback_method: Specifies the callback function after the create_contract() method is executed, which is used to maintain and process the information of the new DAO instance contract in this factory contract.
6. callback_args: Function parameters of the callback function.
The execution of this function is mainly divided into the following steps:
Lines 15-22 of the code find and load the DAO instance contract template code (wasm format) provided by the factory contract into the register numbered 0 according to the code_hash.
Lines 23-25 of the code construct a Promise to track the processing results of all the following steps (3-6).
Lines 26-27 of the code create an account for deploying the DAO instance contract.
Code 28-29 transfers NEAR tokens to the newly created account, which originates from the amount attached_deposited by the caller of the original factory contract create() method.
Lines 30-31 of the code read the wasm code from register 0 and deploy the contract.
Lines 32-41 of the code call the initialization function new() of the DAO instance contract code.
After the final DAO instance contract is deployed, the on_create() function will be called back at the end of the factory_manager.create_contract() execution code 32-53 lines.
The following is the internal code implementation of the callback function on_create:
The specific processing logic of this function is:
If there is an error in the above steps (3-6) and cannot be executed normally, the return result of the Promise obtained by calling the near_sdk::is_promise_success() query in the callback function on_create() will be false. At this time, the amount of NEAR tokens attached_deposited by the caller of the original factory contract create() method will be refunded.
If the above steps (3-6) are executed correctly, it means that the new DAO instance contract (Sputnikdaov2) requested by the user has been created normally. At the same time, the factory contract will record and track the sub-account address of the DAO instance contract.
secondary title

text
text
text
The code is located at: sputnikdao-factory2/src/lib.rs # Line136-149
The processing details of factory_manager.update_contract() are as follows: This interface can realize the call of the update() function in the corresponding DAO instance contract.
It is worth mentioning that:
During the analysis of Sputnik-DAO code, BlockSec found a serious security problem in its Factory contract, which will affect all contracts using Sputnik-DAO. After contacting the project party, the Issue was finally confirmed and fixed in time.
💡The security vulnerability is specifically described as:
In the previous version of the code, the public update() method provided by the sputinikdao factory contract lacked the following key assertion check. This results in the method being callable by anyone.
Coincidentally, the DAO instance contract (Sputnikdaov2 contract) allows the upgrade of this contract by Sputnik-DAO Factory through cross-contract calls by default.
The update() method implemented in the DAO instance contract is as follows, the code is located in sputnikdao2/src/upgrade.rs # Line 62
In line 9 of the above code, the value of factory_info.auto_update is set to True by default when the DAO instance contract deploys and calls the new() method for initialization.
The new() method of the DAO instance contract is implemented as follows: the code is located in sputnikdao2/src/lib.rs # Line 83-104
In summary, an ordinary user (not the Factory contract and the DAO contract itself) can upgrade (tamper) the code of any DAO contract through the pub fn update() method provided by the Factory contract, which will give Sputnik-DAO platform and All contract projects that rely on the Sputnik-DAO platform bring great security risks.
🪴 Fortunately, when this problem was discovered, the code of this version had not yet been launched on the NEAR mainnet, so there was no loss.
Due to the rapid response of the project party, the vulnerability has been correctly fixed by adding a reasonable whitelist verification mechanism😊
first level title
text
In addition to the vulnerabilities discovered and fixed above, the security of the Sputnik-DAO Factory contract is mainly guaranteed from the following aspects:
[Access Control] The view method of the contract should not modify the state variables of the contract, that is, the first parameter in the method definition must be set to &self, not &mut self.
None of the following functions modify state variables:
get_owner(&self)
get_number_daos(&self)
get_default_version(&self)
get_default_code_hash(&self)
get_daos(&self, from_index: u64, limit: u64)
get_dao_list(&self)
get_contracts_metadata(&self)
get_code(&self, code_hash: Base58CryptoHash)
[Access Control] Privileged functions opened by the contract, these functions can only be executed by the contract owner (or DAO contract account), and there are corresponding assertions in the method, for example:

Due to the rapid response of the project party, the vulnerability has been correctly fixed by adding a reasonable whitelist verification mechanism😊
See this Fixing Commit: 518ad1d97614fff4b945aba75b6c8bd2483187a2🔗