Smart Contract Deployment

Deploying smart contracts to blockchain networks and managing contract instances.

Advanced⏱️ 60 min📚 Prerequisites: 2

Smart Contract Deployment

Deploying smart contracts involves compiling Rust code to WASM, uploading it to the blockchain, and creating contract instances.

Deployment Process

1. Compile to WASM

BASH
# Install WASM target
rustup target add wasm32-unknown-unknown

# Build contract
cargo build --target wasm32-unknown-unknown --release

# Optimize WASM (reduce size)
wasm-opt -Os target/wasm32-unknown-unknown/release/contract.wasm -o contract_optimized.wasm

2. Contract Code Structure

RUST
// lib.rs
#![no_std]

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct InitMsg {
    pub name: String,
    pub symbol: String,
    pub decimals: u8,
    pub initial_supply: u64,
}

#[derive(Serialize, Deserialize)]
pub struct ExecuteMsg {
    pub transfer: TransferMsg,
}

#[derive(Serialize, Deserialize)]
pub struct TransferMsg {
    pub to: String,
    pub amount: u64,
}

#[no_mangle]
pub extern "C" fn instantiate(env_ptr: u32, msg_ptr: u32) -> u32 {
    // Initialize contract
    0
}

#[no_mangle]
pub extern "C" fn execute(env_ptr: u32, msg_ptr: u32) -> u32 {
    // Execute contract function
    0
}

#[no_mangle]
pub extern "C" fn query(env_ptr: u32, msg_ptr: u32) -> u32 {
    // Query contract state
    0
}

3. Deployment Configuration

RUST
// deployment.rs
struct DeploymentConfig {
    network: String,
    chain_id: String,
    gas_limit: u64,
    gas_price: u64,
    admin: Option<String>, // Optional admin for upgrades
}

impl DeploymentConfig {
    fn new(network: String) -> Self {
        DeploymentConfig {
            network,
            chain_id: String::from("testnet"),
            gas_limit: 1_000_000,
            gas_price: 1,
            admin: None,
        }
    }
}

Contract Instantiation

RUST
struct ContractInstance {
    address: String,
    code_id: u64,
    creator: String,
    admin: Option<String>,
    label: String,
}

fn instantiate_contract(
    code_id: u64,
    init_msg: InitMsg,
    label: String,
    admin: Option<String>,
) -> Result<ContractInstance, String> {
    // Upload contract code
    // Instantiate contract
    // Return contract address
    Ok(ContractInstance {
        address: String::from("contract_address"),
        code_id,
        creator: String::from("deployer"),
        admin,
        label,
    })
}

Deployment Strategies

1. Direct Deployment

RUST
fn deploy_direct(
    wasm_code: Vec<u8>,
    init_msg: InitMsg,
) -> Result<ContractInstance, String> {
    // Upload code
    let code_id = upload_code(wasm_code)?;
    
    // Instantiate
    instantiate_contract(code_id, init_msg, String::from("MyContract"), None)
}

2. Factory Pattern

RUST
struct ContractFactory;

impl ContractFactory {
    fn deploy_token(
        name: String,
        symbol: String,
        initial_supply: u64,
    ) -> Result<ContractInstance, String> {
        let init_msg = InitMsg {
            name,
            symbol,
            decimals: 18,
            initial_supply,
        };
        
        deploy_direct(load_token_wasm(), init_msg)
    }
}

Contract Verification

RUST
fn verify_contract(
    address: &str,
    expected_code_hash: &str,
) -> Result<bool, String> {
    // Fetch contract code
    let code_hash = get_contract_code_hash(address)?;
    
    // Compare with expected
    Ok(code_hash == expected_code_hash)
}

Migration and Upgrades

RUST
struct UpgradeConfig {
    contract_address: String,
    new_code_id: u64,
    migrate_msg: Option<String>,
    admin: String,
}

fn upgrade_contract(config: UpgradeConfig) -> Result<(), String> {
    // Verify admin
    verify_admin(&config.contract_address, &config.admin)?;
    
    // Migrate state if needed
    if let Some(msg) = config.migrate_msg {
        migrate_state(&config.contract_address, msg)?;
    }
    
    // Update code
    update_contract_code(
        &config.contract_address,
        config.new_code_id,
    )?;
    
    Ok(())
}

Best Practices

  • Optimize WASM: Reduce contract size
  • Verify contracts: Publish source code
  • Test thoroughly: Test on testnet first
  • Set admin: For upgradeable contracts
  • Monitor deployment: Track gas usage
  • Document: Document contract interface

Common Deployment Frameworks

  • CosmWasm: For Cosmos-based chains
  • ink!: For Polkadot/Substrate
  • NEAR SDK: For NEAR Protocol
  • Solana Program: For Solana

Deployment Checklist

  • Code compiled to WASM
  • WASM optimized
  • Tests passing
  • Gas estimation done
  • Testnet deployment successful
  • Contract verified
  • Documentation complete
  • Mainnet deployment ready

Code Examples

Deployment Process

Complete contract deployment workflow

RUST
struct ContractDeployment {
    code_id: u64,
    contract_address: String,
    deployer: String,
}

fn deploy_contract(
    wasm_code: Vec<u8>,
    init_msg: String,
    deployer: String,
) -> Result<ContractDeployment, String> {
    // Step 1: Upload code
    println!("Uploading contract code...");
    let code_id = upload_code(wasm_code)?;
    println!("Code uploaded with ID: {}", code_id);
    
    // Step 2: Instantiate
    println!("Instantiating contract...");
    let contract_address = instantiate_contract(code_id, init_msg)?;
    println!("Contract deployed at: {}", contract_address);
    
    Ok(ContractDeployment {
        code_id,
        contract_address,
        deployer,
    })
}

fn upload_code(wasm_code: Vec<u8>) -> Result<u64, String> {
    // Simulate code upload
    Ok(1)
}

fn instantiate_contract(code_id: u64, init_msg: String) -> Result<String, String> {
    // Simulate instantiation
    Ok(String::from("contract123"))
}

fn main() {
    let wasm_code = vec![0x00, 0x61, 0x73, 0x6d];
    let init_msg = String::from("{\"name\": \"Token\"}");
    
    match deploy_contract(wasm_code, init_msg, String::from("deployer")) {
        Ok(deployment) => {
            println!("Deployment successful!");
            println!("Code ID: {}", deployment.code_id);
            println!("Address: {}", deployment.contract_address);
        }
        Err(e) => println!("Deployment failed: {}", e),
    }
}

Explanation:

Contract deployment involves uploading WASM code and instantiating a contract instance. The process returns a code ID and contract address.

Contract Factory

Factory pattern for deploying multiple contract instances

RUST
struct TokenFactory {
    token_code_id: u64,
}

impl TokenFactory {
    fn new(token_code_id: u64) -> Self {
        TokenFactory { token_code_id }
    }
    
    fn deploy_token(
        &self,
        name: String,
        symbol: String,
        initial_supply: u64,
    ) -> Result<String, String> {
        let init_msg = format!(
            "{{\"name\": \"{}\", \"symbol\": \"{}\", \"initial_supply\": {}}}",
            name, symbol, initial_supply
        );
        
        instantiate_contract(self.token_code_id, init_msg)
    }
}

fn instantiate_contract(code_id: u64, init_msg: String) -> Result<String, String> {
    Ok(format!("token_{}", code_id))
}

fn main() {
    let factory = TokenFactory::new(1);
    
    let token1 = factory.deploy_token(
        String::from("TokenA"),
        String::from("TKA"),
        1000000,
    ).unwrap();
    
    let token2 = factory.deploy_token(
        String::from("TokenB"),
        String::from("TKB"),
        2000000,
    ).unwrap();
    
    println!("Deployed tokens: {}, {}", token1, token2);
}

Explanation:

A factory pattern allows deploying multiple contract instances from the same code. This is useful for creating multiple token contracts or other contract types.

Exercises

Deploy Contract

Create a function to deploy a contract!

Medium

Starter Code:

RUST
fn deploy_contract(wasm_code: Vec<u8>) -> Result<String, String> {
    // Upload code and instantiate
    Ok(String::from("contract_address"))
}

fn main() {
    let code = vec![0x00, 0x61, 0x73, 0x6d];
    match deploy_contract(code) {
        Ok(address) => println!("Deployed at: {}", address),
        Err(e) => println!("Error: {}", e),
    }
}