Factory
A factory is a smart contract that stores a compiled contract, and automatizes deploying the stored contract onto new sub-accounts.
We have a A Generic Factory that deploys the donation contract. This donation contract can be changed for whichever compiled contract you like (e.g. a fungible token or DAO).
Overview​
The factory is a smart contract that:
- Creates sub-accounts of itself and deploys its contract on them (
create_factory_subaccount_and_deploy
). - Can change the stored contract using the
update_stored_contract
method.
- 🦀 Rust
- deploy.rs
- manager.rs
Loading...
Loading...
Quickstart​
Build and Deploy the Factory​
You can automatically compile and deploy the contract in the NEAR testnet by running:
./deploy.sh
Once finished, check the neardev/dev-account
file to find the address in which the contract was deployed:
cat ./neardev/dev-account
# e.g. dev-1659899566943-21539992274727
Deploy the Stored Contract Into a Sub-Account​
create_factory_subaccount_and_deploy
will create a sub-account of the factory and deploy the
stored contract on it.
near call <factory-account> create_factory_subaccount_and_deploy '{ "name": "sub", "beneficiary": "<account-to-be-beneficiary>"}' --deposit 1.24 --accountId <account-id> --gas 300000000000000
This will create the sub.<factory-account>
, which will have a donation
contract deployed on it:
near view sub.<factory-account> get_beneficiary
# expected response is: <account-to-be-beneficiary>
Update the Stored Contract​
update_stored_contract
enables to change the compiled contract that the factory stores.
The method is interesting because it has no declared parameters, and yet it takes an input: the new contract to store as a stream of bytes.
To use it, we need to transform the contract we want to store into its base64
representation, and pass the result as input to the method:
# Use near-cli to update stored contract
export BYTES=`cat ./src/to/new-contract/contract.wasm | base64`
near call <factory-account> update_stored_contract "$BYTES" --base64 --accountId <factory-account> --gas 30000000000000
This works because the arguments of a call can be either a
JSON
object or aString Buffer
Factories - Concepts & Limitations​
Factories are an interesting concept, here we further explain some of their implementation aspects, as well as their limitations.
Automatically Creating Accounts​
NEAR accounts can only create sub-accounts of itself, therefore, the factory
can only create and
deploy contracts on its own sub-accounts.
This means that the factory:
- Can create
sub.factory.testnet
and deploy a contract on it. - Cannot create sub-accounts of the
predecessor
. - Can create new accounts (e.g.
account.testnet
), but cannot deploy contracts on them.
It is important to remember that, while factory.testnet
can create sub.factory.testnet
, it has
no control over it after its creation.
The Update Method​
The update_stored_contracts
has a very short implementation:
#[private]
pub fn update_stored_contract(&mut self) {
self.code = env::input().expect("Error: No input").to_vec();
}
On first sight it looks like the method takes no input parameters, but we can see that its only
line of code reads from env::input()
. What is happening here is that update_stored_contract
bypasses the step of deserializing the input.
You could implement update_stored_contract(&mut self, new_code: Vec<u8>)
,
which takes the compiled code to store as a Vec<u8>
, but that would trigger the contract to:
- Deserialize the
new_code
variable from the input. - Sanitize it, making sure it is correctly built.
When dealing with big streams of input data (as is the compiled wasm
file to be stored), this process
of deserializing/checking the input ends up consuming the whole GAS for the transaction.
At the time of this writing, this example works with the following versions:
- near-cli:
4.0.13
- node:
18.19.1
- rustc:
1.77.0