Skip to content

Commit 02395b5

Browse files
authored
How to Deploy Contracts from Contracts (#2730)
Learn how to implement the factory pattern on NEAR to programmatically deploy smart contracts from within other smart contracts.
1 parent 9e24d7f commit 02395b5

File tree

1 file changed

+157
-77
lines changed

1 file changed

+157
-77
lines changed

docs/tutorials/examples/factory.md

Lines changed: 157 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,63 @@
11
---
22
id: factory
3-
title: Factory
4-
description: "A factory is a smart contract that stores a compiled contract, and automatizes deploying the stored contract onto new sub-accounts."
3+
title: How to Deploy Contracts from Contracts
4+
description: "Learn how to implement the factory pattern on NEAR to programmatically deploy smart contracts from within other smart contracts."
55
---
66

77
import Tabs from '@theme/Tabs';
88
import TabItem from '@theme/TabItem';
99
import {CodeTabs, Language, Github} from "@site/src/components/codetabs"
1010

11-
A factory is a smart contract that stores a compiled contract, and automatizes deploying the stored contract onto new sub-accounts.
11+
# How to Deploy Contracts from Contracts
1212

13-
We have a [**A Generic Factory**](https://github.com/near-examples/factory-rust) that deploys the [donation contract](./donation.md). This donation contract can be changed for whichever compiled contract you like (e.g. a fungible token or DAO).
13+
The factory pattern is a powerful design pattern that allows one smart contract to deploy and manage other smart contracts programmatically. This tutorial will teach you how to build a factory contract that can store compiled contract code and deploy it to new sub-accounts automatically.
14+
15+
## What is a Factory Contract?
16+
17+
A factory contract is a smart contract that acts as a template deployer. Instead of manually deploying each instance of a contract, the factory automates this process by:
18+
19+
- Storing compiled contract bytecode
20+
- Creating new sub-accounts
21+
- Deploying the stored contract to those sub-accounts
22+
- Managing and updating the stored contract code
23+
24+
This pattern is particularly useful when you need to deploy many instances of the same contract type, such as creating multiple DAOs, token contracts, or any standardized smart contract.
25+
26+
## Why Use the Factory Pattern?
27+
28+
**Cost Efficiency**: Deploy once, reuse many times without re-uploading contract code.
29+
30+
**Standardization**: Ensure all deployed contracts follow the same tested pattern.
31+
32+
**Automation**: Programmatically create contracts without manual intervention.
33+
34+
**Upgradability**: Update the stored contract template for future deployments.
35+
36+
**Access Control**: Implement permissions for who can deploy new instances.
1437

1538
---
1639

17-
## Overview {#generic-factory}
40+
## Understanding NEAR Account Limitations
41+
42+
Before implementing a factory, it's crucial to understand NEAR's account creation rules:
43+
44+
### What Factories Can Do
45+
- Create sub-accounts of themselves (e.g., `factory.near` can create `instance1.factory.near`)
46+
- Deploy contracts to their own sub-accounts
47+
- Manage the stored contract bytecode
48+
49+
### What Factories Cannot Do
50+
- Create sub-accounts for other accounts
51+
- Deploy contracts to accounts they don't own
52+
- Control sub-accounts after creation (they become independent)
53+
54+
This means your factory at `factory.testnet` can create `dao1.factory.testnet` and `dao2.factory.testnet`, but cannot create `dao1.alice.testnet`.
55+
56+
---
1857

19-
The factory is a smart contract that:
58+
## Building Your Factory Contract
2059

21-
1. Creates sub-accounts of itself and deploys its contract on them (`create_factory_subaccount_and_deploy`).
22-
2. Can change the stored contract using the `update_stored_contract` method.
60+
Let's examine the core components of a factory contract:
2361

2462
<CodeTabs>
2563
<Language value="rust" language="rust">
@@ -32,123 +70,165 @@ The factory is a smart contract that:
3270
</Language>
3371
</CodeTabs>
3472

35-
---
73+
### Core Factory Methods
3674

37-
## Quickstart
75+
The factory implements two essential methods:
3876

39-
1. Make sure you have installed [rust](https://www.rust-lang.org/).
40-
2. Install the [`NEAR CLI`](/tools/near-cli#installation)
77+
**`create_factory_subaccount_and_deploy`**: Creates a new sub-account and deploys the stored contract to it.
4178

42-
<hr className="subsection" />
79+
**`update_stored_contract`**: Updates the contract bytecode that will be deployed to future instances.
4380

44-
### Build and Deploy the Factory
81+
---
4582

46-
You can automatically compile and deploy the contract in the NEAR testnet by running:
83+
## Implementing Contract Deployment
4784

48-
```bash
49-
./deploy.sh
50-
```
85+
When you call `create_factory_subaccount_and_deploy`, the factory:
5186

52-
Once finished, check the `neardev/dev-account` file to find the address in which the contract was deployed:
87+
1. **Creates the sub-account** using NEAR's account creation APIs
88+
2. **Transfers the required deposit** for account creation and storage
89+
3. **Deploys the stored contract** bytecode to the new account
90+
4. **Initializes the contract** with the provided parameters
5391

5492
```bash
55-
cat ./neardev/dev-account
56-
# e.g. dev-1659899566943-21539992274727
93+
near call <factory-account> create_factory_subaccount_and_deploy '{ "name": "sub", "beneficiary": "<account-to-be-beneficiary>"}' --deposit 1.24 --accountId <account-id> --gas 300000000000000
5794
```
5895

59-
<hr className="subsection" />
96+
The deposit covers:
97+
- Account creation costs (~0.1 NEAR)
98+
- Storage costs for the contract code
99+
- Initial balance for the new contract
60100

61-
### Deploy the Stored Contract Into a Sub-Account
101+
---
62102

63-
`create_factory_subaccount_and_deploy` will create a sub-account of the factory and deploy the
64-
stored contract on it.
103+
## Managing Contract Updates
65104

66-
```bash
67-
near call <factory-account> create_factory_subaccount_and_deploy '{ "name": "sub", "beneficiary": "<account-to-be-beneficiary>"}' --deposit 1.24 --accountId <account-id> --gas 300000000000000
105+
One of the factory pattern's key advantages is the ability to update the stored contract for future deployments:
106+
107+
### The Update Method Implementation
108+
109+
```rust
110+
#[private]
111+
pub fn update_stored_contract(&mut self) {
112+
self.code = env::input().expect("Error: No input").to_vec();
113+
}
68114
```
69115

70-
This will create the `sub.<factory-account>`, which will have a `donation` contract deployed on it:
116+
This method uses a clever optimization: instead of deserializing the input parameters (which would consume excessive gas for large files), it reads the raw input directly using `env::input()`.
117+
118+
### Why This Optimization Matters
119+
120+
Standard parameter deserialization would:
121+
1. Parse the entire WASM file from JSON
122+
2. Validate the input format
123+
3. Convert it to the appropriate data type
124+
125+
For large contract files, this process consumes the entire gas limit. The direct input approach bypasses this overhead.
126+
127+
### Updating Your Stored Contract
71128

72129
```bash
73-
near view sub.<factory-account> get_beneficiary
74-
# expected response is: <account-to-be-beneficiary>
130+
# Convert your contract to base64
131+
export BYTES=`cat ./path/to/new-contract.wasm | base64`
132+
133+
# Update the factory's stored contract
134+
near call <factory-account> update_stored_contract "$BYTES" --base64 --accountId <factory-account> --gas 30000000000000
75135
```
76136

77-
<hr className="subsection" />
137+
---
138+
139+
## Testing Your Factory
78140

79-
### Update the Stored Contract
141+
### 1. Deploy the Factory
80142

81-
`update_stored_contract` enables to change the compiled contract that the factory stores.
143+
```bash
144+
./deploy.sh
145+
```
82146

83-
The method is interesting because it has no declared parameters, and yet it takes
84-
an input: the new contract to store as a stream of bytes.
147+
Check the deployment:
148+
```bash
149+
cat ./neardev/dev-account
150+
# Returns: dev-1659899566943-21539992274727
151+
```
85152

86-
To use it, we need to transform the contract we want to store into its `base64`
87-
representation, and pass the result as input to the method:
153+
### 2. Create Your First Instance
88154

89155
```bash
90-
# Use near-cli to update stored contract
91-
export BYTES=`cat ./src/to/new-contract/contract.wasm | base64`
92-
near call <factory-account> update_stored_contract "$BYTES" --base64 --accountId <factory-account> --gas 30000000000000
156+
near call <factory-account> create_factory_subaccount_and_deploy '{ "name": "test-instance", "beneficiary": "alice.testnet"}' --deposit 1.24 --accountId <your-account> --gas 300000000000000
93157
```
94158

95-
> This works because the arguments of a call can be either a `JSON` object or a `String Buffer`
159+
### 3. Verify the Deployment
96160

97-
---
161+
```bash
162+
near view test-instance.<factory-account> get_beneficiary
163+
# Expected: alice.testnet
164+
```
98165

99-
## Factories - Concepts & Limitations
166+
---
100167

101-
Factories are an interesting concept, here we further explain some of their implementation aspects,
102-
as well as their limitations.
168+
## Best Practices and Considerations
103169

104-
<hr className="subsection" />
170+
### Gas Management
171+
- Contract deployment requires significant gas (200-300 TGas)
172+
- Always specify sufficient gas limits
173+
- Test gas requirements with smaller contracts first
105174

106-
### Automatically Creating Accounts
175+
### Storage Costs
176+
- Factor in storage costs for both factory and deployed contracts
177+
- Require sufficient deposit to cover all costs
178+
- Consider implementing deposit refund mechanisms
107179

108-
NEAR accounts can only create sub-accounts of itself, therefore, the `factory` can only create and
109-
deploy contracts on its own sub-accounts.
180+
### Security Considerations
181+
- Implement access controls for who can deploy contracts
182+
- Validate initialization parameters before deployment
183+
- Consider implementing factory ownership and permissions
110184

111-
This means that the factory:
185+
### Contract Versioning
186+
- Implement version tracking for stored contracts
187+
- Consider allowing multiple contract versions
188+
- Document breaking changes between versions
112189

113-
1. **Can** create `sub.factory.testnet` and deploy a contract on it.
114-
2. **Cannot** create sub-accounts of the `predecessor`.
115-
3. **Can** create new accounts (e.g. `account.testnet`), but **cannot** deploy contracts on them.
190+
---
116191

117-
It is important to remember that, while `factory.testnet` can create `sub.factory.testnet`, it has
118-
no control over it after its creation.
192+
## Alternative Approaches
119193

120-
<hr className="subsection" />
194+
### Direct Account Creation
195+
Instead of using a factory, you could create accounts directly, but this requires:
196+
- Manual contract deployment for each instance
197+
- Separate storage costs for each deployment
198+
- More complex coordination between deployments
121199

122-
### The Update Method
200+
### Proxy Pattern
201+
For upgradeable contracts, consider the proxy pattern where:
202+
- A proxy contract delegates calls to an implementation contract
203+
- Updates change the implementation address
204+
- All instances can be upgraded simultaneously
123205

124-
The `update_stored_contracts` has a very short implementation:
206+
### When to Use Factories
207+
Factories work best when:
208+
- You need many instances of similar contracts
209+
- Instances should be independent after creation
210+
- You want to standardize deployment parameters
211+
- Cost optimization is important for multiple deployments
125212

126-
```rust
127-
#[private]
128-
pub fn update_stored_contract(&mut self) {
129-
self.code = env::input().expect("Error: No input").to_vec();
130-
}
131-
```
213+
---
132214

133-
On first sight it looks like the method takes no input parameters, but we can see that its only
134-
line of code reads from `env::input()`. What is happening here is that `update_stored_contract`
135-
**bypasses** the step of **deserializing the input**.
215+
## Common Pitfalls to Avoid
136216

137-
You could implement `update_stored_contract(&mut self, new_code: Vec<u8>)`,
138-
which takes the compiled code to store as a `Vec<u8>`, but that would trigger the contract to:
217+
**Insufficient Deposits**: Always calculate the minimum required deposit including account creation, storage, and initialization costs.
139218

140-
1. Deserialize the `new_code` variable from the input.
141-
2. Sanitize it, making sure it is correctly built.
219+
**Gas Limit Errors**: Contract deployment is gas-intensive. Start with higher limits and optimize down.
142220

143-
When dealing with big streams of input data (as is the compiled `wasm` file to be stored), this process
144-
of deserializing/checking the input ends up **consuming the whole GAS** for the transaction.
221+
**Access Control**: Implement proper permissions to prevent unauthorized contract deployments.
145222

146-
:::note Versioning for this article
223+
**Storage Management**: Monitor storage costs as they accumulate with each stored contract and deployment.
147224

148-
At the time of this writing, this example works with the following versions:
225+
**Sub-account Naming**: Plan your naming convention carefully as sub-accounts cannot be renamed after creation.
149226

227+
:::note Development Environment
228+
This tutorial works with:
150229
- near-cli: `4.0.13`
151-
- node: `18.19.1`
230+
- node: `18.19.1`
152231
- rustc: `1.77.0`
153-
154232
:::
233+
234+
The factory pattern provides a powerful way to scale smart contract deployment on NEAR. By understanding the account limitations, gas considerations, and implementation details, you can build efficient factory contracts that automate and standardize your deployment process.

0 commit comments

Comments
 (0)