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
RUSTstruct 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
RUSTfn 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
RUSTstruct 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
RUSTfn 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
RUSTstruct 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!
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),
}
}