Soroban Contracts 101 : Deployer
Tags: sorobanathon, soroban, stellar, smartcontract
Published: 2023-03-18 18:29:30

Hi there! Welcome to my ninth post of my series called "Soroban Contracts 101", where I'll be explaining the basics of Soroban contracts, such as data storage, authentication, custom types, and more. All the code that we're gonna explain throughout this series will mostly come from [soroban-contracts-101](https://github.com/yuzurush/soroban-contracts-101) github repository.

In this ninth post of the series, I'll be covering soroban deployer. Deployer is a contract that can deploy and initialize other contracts. Some key benefits of using a deployer contract are:

Abstraction - the deployment logic is separated from main contracts, which can simplify their design
Privileged deployment - only designated deployer accounts/contracts can deploy other contracts, which can be important for security
Standardization - having a common deployer contract interface could allow interoperable deployment of contracts from various sources

We're gonna using 2 contract, `Deployer` contract act as the "contract deployer", and `Contract` contract act as the "test contract" that are going to be deployed by our `Deployer` contract.

##The Contracts Code
###Test Contract Code

```rust
pub struct Contract;

const KEY: Symbol = symbol!("value");

#[contractimpl]
impl Contract {
    pub fn init(env: Env, value: u32) {
        env.storage().set(KEY, value);
    }
    pub fn value(env: Env) -> u32 {
        env.storage().get_unchecked(KEY).unwrap()
    }
}
```
Our Test Contract code defines a simple contract that:

- Stores a value at a constant `KEY` in `init`
- Exposes a `value` function to retrieve the stored value

This is a very basic contract that shows storing and retrieving a value from contract storage.

###Deployer Contract Code

```rust
pub struct Deployer;

#[contractimpl]
impl Deployer {
    /// Deploy the contract using WASM hash and after deployment invoke the init
    /// function of the contract with the given arguments. Returns the
    /// contract ID and result of the init function.
    pub fn deploy(
        env: Env,
        salt: Bytes,
        wasm_hash: BytesN<32>,
        init_fn: Symbol,
        init_args: Vec<RawVal>,
    ) -> (BytesN<32>, RawVal) {
        // Deploy the contract using the installed WASM code with given hash.
        let id = env.deployer().with_current_contract(salt).deploy(wasm_hash);
        // Invoke the init function with the given arguments.
        let res: RawVal = env.invoke_contract(&id, &init_fn, init_args);
        // Return the contract ID of the deployed contract and the result of
        // invoking the init result.
        (id, res)
    }
}
```
Our deployer contract can deploy and initialize other contracts. It:

- Defines a `Deployer` struct
- Implements a `contractimpl` on `Deployer`
- Defines a `deploy` function that:

> - Takes various input parameters (salt, WASM hash, init function name, init arguments)
> - Uses the Env's deployer to deploy a contract from the given WASM hash
> - Calls the `init` function on the deployed contract, passing in the init arguments
> - Returns the contract ID and init result

##The Deployer Contract Test Code

```
// The contract that will be deployed by the deployer contract.
mod contract {
    soroban_sdk::contractimport!(
        file = "../../target/wasm32-unknown-unknown/release/soroban_deployer_test_contract.wasm"
    );
}

#[test]
fn test() {
    let env = Env::default();
    let client = DeployerClient::new(&env, &env.register_contract(None, Deployer));

    // Install the WASM that will be deployed by the deployer contract.
    let wasm_hash = env.install_contract_wasm(contract::WASM);

    // Deploy contract using deployer, and include an init function to call.
    let salt = Bytes::from_array(&env, &[0; 32]);
    let init_fn = symbol!("init");
    let init_fn_args = (5u32,).into_val(&env);
    let (contract_id, init_result) = client.deploy(&salt, &wasm_hash, &init_fn, &init_fn_args);
    assert!(init_result.is_void());

    // Invoke contract to check that it is initialized.
    let client = contract::Client::new(&env, &contract_id);
    let sum = client.value();
    assert_eq!(sum, 5);
}

```
Our test code here exercises deploying and initializing a contract using `Deployer` contract. It:

- Creates an `Env`
- Registers the `Deployer` contract
- Creates a Deployer client
- Installs the WASM for the deployed `Contract` contract
- Calls `deploy` on the Deployer, passing in:

> - Salt
> - TestDeployer WASM hash
> - init function name
> - init argument (5)

- Asserts that the `init` returns () (is void)
- Creates a `contract` client and verifies that it returns the expected value (5), showing it was properly initialized

So this test thoroughly exercises the deployer/deployed contract scheme, and verifies that the deployed contract was initialized as expected.

##Running Contract Tests
Test conducted in deployer directory

```
cd deployer/deployer
cargo test
```
If the tests are successful, you should see an output similar to:

```
running 1 test
test test::test ... ok
```
##Building The Contract
We need to build both `Deployer` contract and `Contract` contract, to build Deployer contract use the following command:
```
cd .../deployer/deployer
cargo build --target wasm32-unknown-unknown --release
```
To build Contract contract use the following command:
```
cd .../deployer/contract
cargo build --target wasm32-unknown-unknown --release
```
Both .wasm files should be found in the ../target directory:
```
../target/wasm32-unknown-unknown/release/soroban_deployer_contract.wasm
../target/wasm32-unknown-unknown/release/soroban_deployer_test_contract.wasm
```

##Initializing The `Contract` contract
Before a contract can be deployed from its WASM code, the WASM must be installed on the blockchain. The WASM installation happens outside of the deployer contract, and only needs to occur once.
After the WASM is installed, the deployer contract can deploy any number of instances of the contract from that single installed WASM. To initialize our `Contract` contract use the following command : 

```
soroban contract install --wasm target/wasm32-unknown-unknown/release/soroban_deployer_test_contract.wasm
```
Returning our WASM hash, in my case :

```
77c33183640b62da4a65c453047d29c846b6aace87ee64a8f532623388b6b06e
```
##Invoking The Contracts
We will use our WASM hash value from previous step,passing it to `deploy` function from `Deployer` contract. The command will be :

```
soroban contract invoke \
    --wasm target/wasm32-unknown-unknown/release/soroban_deployer_contract.wasm \
    --id 1 \
    -- \
    deploy \
    --salt 0000000000000000000000000000000000000000000000000000000000000000 \
    --wasm_hash 77c33183640b62da4a65c453047d29c846b6aace87ee64a8f532623388b6b06e\
    --init_fn init \
    --init_args '[{"u32":10}]'
```
Above command will return contract id of our deployed `Contract` contract, in my case :

```
["ef370e4a3694cde1525f48d7c7666dc71da119aee5b446b0cbd1a274069ab8c8",null]
```
Then we will invoke our `Contract` contract using this following command :

```
soroban contract invoke \
    --id ef370e4a3694cde1525f48d7c7666dc71da119aee5b446b0cbd1a274069ab8c8\
    --fn value
```
You should see the following output:

```
10
```

##Conclusion
That's it! We explored the deployer contract in Soroban. The key point is the WASM hash for the deployed contract need to installed once before deploying it using the `Deployer` contract. Stay tuned for more post in this "Soroban Contracts 101" Series where we will dive deeper into Soroban Contracts and their functionalities.