Skip to content

Commit 85e5080

Browse files
Rewrite deploy-contract guide's deployer example for the current constructor-based pattern (#2696)
### What Update the deployer guide's contract code, CLI commands, and example link to match the current constructor-based deployer contract instead of the old init-function pattern. ### Why The deployer contract's actual current implementation takes an administrator at construction time and uses `deploy_v2`, so the old `init_fn`/`init_args` example and CLI flags shown here no longer match what a reader would find in the real example or be able to run.
1 parent 8ecc10b commit 85e5080

1 file changed

Lines changed: 48 additions & 59 deletions

File tree

docs/build/guides/conventions/deploy-contract.mdx

Lines changed: 48 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ This guide walks through the process of deploying a smart contract from installe
3030

3131
### Setup environment
3232

33-
The [deployer example](https://github.com/stellar/soroban-examples/tree/v22.0.1/deployer) demonstrates how to deploy contracts using a contract.
33+
The [deployer example](https://github.com/stellar/soroban-examples/tree/main/deployer) demonstrates how to deploy contracts using a contract.
3434

3535
1. Clone the Soroban Examples Repository:
3636

3737
```bash
38-
git clone -b v22.0.1 https://github.com/stellar/soroban-examples
38+
git clone -b main https://github.com/stellar/soroban-examples
3939
```
4040

4141
2. Navigate to the Deployer Example:
@@ -61,9 +61,8 @@ cargo test
6161
You should see the output indicating the tests passed:
6262

6363
```bash
64-
running 2 tests
65-
test test::test_deploy_from_contract ... ok
66-
test test::test_deploy_from_address ... ok
64+
running 1 test
65+
test test::test ... ok
6766
```
6867

6968
### Code overview
@@ -72,90 +71,81 @@ test test::test_deploy_from_address ... ok
7271
#[contract]
7372
pub struct Deployer;
7473

74+
const ADMIN: Symbol = symbol_short!("admin");
75+
7576
#[contractimpl]
7677
impl Deployer {
77-
/// Deploy the contract Wasm and after deployment invoke the init function
78-
/// of the contract with the given arguments.
79-
///
80-
/// This has to be authorized by `deployer` (unless the `Deployer` instance
81-
/// itself is used as deployer). This way the whole operation is atomic
82-
/// and it's not possible to frontrun the contract initialization.
78+
/// Construct the deployer with a provided administrator.
79+
pub fn __constructor(env: Env, admin: Address) {
80+
env.storage().instance().set(&ADMIN, &admin);
81+
}
82+
83+
/// Deploys the contract on behalf of the `Deployer` contract.
8384
///
84-
/// Returns the contract address and result of the init function.
85+
/// This has to be authorized by the `Deployer`s administrator.
8586
pub fn deploy(
8687
env: Env,
87-
deployer: Address,
8888
wasm_hash: BytesN<32>,
8989
salt: BytesN<32>,
90-
init_fn: Symbol,
91-
init_args: Vec<Val>,
92-
) -> (Address, Val) {
93-
// Skip authorization if deployer is the current contract.
94-
if deployer != env.current_contract_address() {
95-
deployer.require_auth();
96-
}
97-
98-
// Deploy the contract using the uploaded Wasm with given hash.
90+
constructor_args: Vec<Val>,
91+
) -> Address {
92+
let admin: Address = env.storage().instance().get(&ADMIN).unwrap();
93+
admin.require_auth();
94+
95+
// Deploy the contract using the uploaded Wasm with given hash on behalf
96+
// of the current contract.
97+
// Note, that not deploying on behalf of the admin provides more
98+
// consistent address space for the deployer contracts - the admin could
99+
// change or it could be a completely separate contract with complex
100+
// authorization rules, but all the contracts will still be deployed
101+
// by the same `Deployer` contract address.
99102
let deployed_address = env
100103
.deployer()
101-
.with_address(deployer, salt)
102-
.deploy(wasm_hash);
103-
104-
// Invoke the init function with the given arguments.
105-
let res: Val = env.invoke_contract(&deployed_address, &init_fn, init_args);
106-
// Return the contract ID of the deployed contract and the result of
107-
// invoking the init result.
108-
(deployed_address, res)
104+
.with_address(env.current_contract_address(), salt)
105+
.deploy_v2(wasm_hash, constructor_args);
106+
107+
deployed_address
109108
}
110109
}
111110
```
112111

113112
## How it works
114113

115-
The deployer contract provides a mechanism to deploy other contracts in a secure and deterministic manner. It ensures that the deployment is authorized by the specified deployer address and supports atomic initialization of the newly deployed contract. This guarantees that the contract is properly initialized immediately after deployment, preventing any potential issues with uninitialized contracts.
114+
The deployer contract provides a mechanism to deploy other contracts in a secure and deterministic manner. It stores an administrator address at construction time and requires that administrator's authorization for every deployment. It also supports atomic initialization of the newly deployed contract via constructor arguments. This guarantees that the contract is properly initialized immediately after deployment, preventing any potential issues with uninitialized contracts.
116115

117116
### Function breakdown
118117

119118
- `env: Env`: The Env object represents the current contract execution environment. It provides methods to interact with the blockchain and other contracts, as well as perform various operations.
120-
- `deployer: Address`: The Address of the entity that is requesting the contract deployment. This address will be used to authorize the deployment.
119+
- `admin: Address`: The Address of the administrator, provided to the `__constructor` function and stored for later use. Only this address can authorize deployments.
121120
- `wasm_hash: BytesN<32>`: The hash of the Wasm bytecode of the contract to be deployed and must already be installed and on the ledger. This hash is used to uniquely identify the contract's code.
122-
- `salt: BytesN<32>`: A unique value used to derive the address of the deployed contract. The same combination of deployer address and salt will always produce the same deployed contract address, ensuring deterministic contract addresses.
123-
- `init_fn: Symbol`: The name of the initialization function to be called on the newly deployed contract.
124-
- `init_args: Vec<Val>`: A vector of arguments, specified as `{type: value}` objects, to be passed to the initialization function of the deployed contract.
121+
- `salt: BytesN<32>`: A unique value used to derive the address of the deployed contract. The same combination of the `Deployer` contract's address and salt will always produce the same deployed contract address, ensuring deterministic contract addresses.
122+
- `constructor_args: Vec<Val>`: A vector of arguments, specified as `{type: value}` objects, to be passed to the constructor of the deployed contract.
125123

126124
### Function steps
127125

128126
```rust
129-
if deployer != env.current_contract_address() {
130-
deployer.require_auth();
131-
}
127+
let admin: Address = env.storage().instance().get(&ADMIN).unwrap();
128+
admin.require_auth();
132129
```
133130

134-
The function checks if the `deployer` address is the same as the current contract’s address. If it is not, it requires authorization from the deployer address to ensure that the deployer has permitted this deployment.
131+
The function loads the administrator address that was stored during construction and requires its authorization, ensuring that only the administrator can trigger a deployment.
135132

136133
```rust
137134
let deployed_address = env
138135
.deployer()
139-
.with_address(deployer, salt)
140-
.deploy(wasm_hash);
136+
.with_address(env.current_contract_address(), salt)
137+
.deploy_v2(wasm_hash, constructor_args);
141138
```
142139

143140
- Uses the `env.deployer()` method to create a deployer object.
144-
- `with_address(deployer, salt)` specifies the deployer address and the salt to derive the new contract’s address.
145-
- `deploy(wasm_hash)` deploys the contract using the provided Wasm bytecode hash and returns the address of the newly deployed contract.
141+
- `with_address(env.current_contract_address(), salt)` specifies that the deployed contract's address is derived from the `Deployer` contract's own address and the given salt.
142+
- `deploy_v2(wasm_hash, constructor_args)` deploys the contract using the provided Wasm bytecode hash, invokes its constructor with `constructor_args`, and returns the address of the newly deployed contract.
146143

147144
```rust
148-
let res: Val = env.invoke_contract(&deployed_address, &init_fn, init_args);
145+
deployed_address
149146
```
150147

151-
- Invokes the specified initialization function (`init_fn`) on the newly deployed contract (`deployed_address`), passing the provided initialization arguments (`init_args`).
152-
- The result of this function call is stored in `res`.
153-
154-
```rust
155-
(deployed_address, res)
156-
```
157-
158-
Returns a tuple containing the address of the newly deployed contract and the result of the initialization function.
148+
Returns the address of the newly deployed contract.
159149

160150
### Build the contracts
161151

@@ -177,7 +167,7 @@ target/wasm32v1-none/release/soroban_deployer_test_contract.wasm
177167

178168
## Run the contract
179169

180-
Before deploying the test contract with the deployer, install the test contract Wasm using the `install` command. The `install` command will print out the hash derived from the Wasm file which should be used by the deployer.
170+
Before deploying the test contract with the deployer, install the test contract Wasm using the `upload` command. The `upload` command will print out the hash derived from the Wasm file which should be used by the deployer.
181171

182172
```sh
183173
stellar contract upload \
@@ -192,18 +182,19 @@ When deploying a smart contract to the network, you must specify an identity tha
192182

193183
The command prints out the hash as hex. It will look something like `6bc6d975ef99c057231481c40f69f4c2a8a89ac56e8826ef0378f8e5c6abc4be`.
194184

195-
We also need to deploy the `Deployer` contract:
185+
We also need to deploy the `Deployer` contract. Since the `Deployer` contract's `__constructor` function requires an administrator address, we provide the `alice` [identity] as the `--admin` argument. Create and provide your own identity, where necessary.
196186

197187
```sh
198188
stellar contract deploy \
199189
--wasm deployer/target/wasm32v1-none/release/soroban_deployer_contract.wasm \
200190
--source-account alice \
201-
--network testnet
191+
--network testnet \
192+
-- --admin alice
202193
```
203194

204195
This will return the deployer address. For example: `CDKYZMA3OXR54YAHOQ5D4EWDFDT3ZNKKRYKQT7MGPWH22ZVD2DX2BJID`.
205196

206-
Then the deployer contract may be invoked with the Wasm hash value above.
197+
Then the deployer contract may be invoked with the Wasm hash value above. The `constructor_args` are passed through to the constructor of the deployed contract, so they are used here to set the initial `value` to `8`.
207198

208199
```sh
209200
stellar contract invoke \
@@ -212,11 +203,9 @@ stellar contract invoke \
212203
--network testnet \
213204
-- \
214205
deploy \
215-
--deployer CDKYZMA3OXR54YAHOQ5D4EWDFDT3ZNKKRYKQT7MGPWH22ZVD2DX2BJID \
216206
--salt 123 \
217207
--wasm_hash 6bc6d975ef99c057231481c40f69f4c2a8a89ac56e8826ef0378f8e5c6abc4be \
218-
--init_fn init \
219-
--init_args '[{"u32":8}]'
208+
--constructor_args '[{"u32":8}]'
220209
```
221210

222211
The deployer contract invocation will return the Contract address (For example: `CCTVFX6BFTQHTGAHA5TY4YZQJRUKRE2RRNUTGVBNKE3PJF5C7CI53APY`) of the newly deployed test contract.

0 commit comments

Comments
 (0)