Governance Canister Build
Overview
Section titled “Overview”The sons_governance canister is a dynamic canister that is created at runtime for each LGE campaign. Unlike other canisters in this project, it is NOT defined in dfx.json because it’s deployed dynamically when campaigns are created.
Key Characteristics:
- Created dynamically for each LGE campaign with governance vesting
- Embedded in
ohshii_launcher_backendviainclude_bytes!() - Requires special build process: Docker build → Candid generation with
didc→ Embedding - Location:
src/sons_governance/ - Declarations:
src/declarations/sons_governance/
Why Special Build Process?
Section titled “Why Special Build Process?”- Not in dfx.json: Dynamic canisters cannot use
dfx generate - Embedded WASM: Backend embeds governance WASM using
include_bytes!("../governance_wasm/sons_governance.wasm.gz") - Candid Generation: Must use
didctool instead ofdfx generate - Build Order: Governance must be built before backend (backend depends on governance WASM)
Complete Build Process
Section titled “Complete Build Process”Prerequisites
Section titled “Prerequisites”- Docker must be installed and running
- didc tool (installed automatically by build script, or manually)
Step-by-Step Build Process
Section titled “Step-by-Step Build Process”Step 1: Build Governance Canister with Docker
Section titled “Step 1: Build Governance Canister with Docker”The Docker build process handles the complete workflow:
# From project root./Deploy/docker-build.shWhat happens inside Docker:
-
Build governance WASM:
Terminal window cargo build --target wasm32-unknown-unknown --release -p sons_governance -
Optimize WASM (shrink + add Candid metadata):
Terminal window ic-wasm target/wasm32-unknown-unknown/release/sons_governance.wasm \-o target/wasm32-unknown-unknown/release/sons_governance_optimized.wasm shrinkic-wasm target/wasm32-unknown-unknown/release/sons_governance_optimized.wasm \-o target/wasm32-unknown-unknown/release/sons_governance_final.wasm \metadata candid:service -f src/sons_governance/sons_governance.did -v public -
Copy and gzip for backend embedding:
Terminal window mkdir -p src/backend/governance_wasmcp sons_governance_final.wasm src/backend/governance_wasm/sons_governance.wasmgzip -9 -f -k src/backend/governance_wasm/sons_governance.wasm -
Extract WASM files:
- Raw WASM:
Deploy/wasm/sons_governance_docker.wasm - Gzipped WASM:
Deploy/wasm/sons_governance_docker.wasm.gz - Embedded WASM:
src/backend/governance_wasm/sons_governance.wasm.gz
- Raw WASM:
Step 2: Generate TypeScript/JavaScript Declarations with didc
Section titled “Step 2: Generate TypeScript/JavaScript Declarations with didc”After building the WASM, generate frontend declarations:
Automatic (via docker-build.sh):
The build script automatically generates declarations:
# Ensure directory existsmkdir -p src/declarations/sons_governance
# Generate JavaScript bindingsdidc bind src/sons_governance/sons_governance.did -t js > \ src/declarations/sons_governance/sons_governance.did.js
# Generate TypeScript definitionsdidc bind src/sons_governance/sons_governance.did -t ts > \ src/declarations/sons_governance/sons_governance.did.d.ts
# Copy .did filecp src/sons_governance/sons_governance.did \ src/declarations/sons_governance/sons_governance.didManual Generation (if needed):
If you need to regenerate declarations manually:
# Install didc (one-time, if not already installed)# macOS:curl -fsSL https://github.com/dfinity/candid/releases/latest/download/didc-macos -o /tmp/didcchmod +x /tmp/didc
# Linux:curl -fsSL https://github.com/dfinity/candid/releases/latest/download/didc-linux64 -o /tmp/didcchmod +x /tmp/didc
# Generate declarationscd /path/to/dev-ohshii-launchermkdir -p src/declarations/sons_governance
/tmp/didc bind src/sons_governance/sons_governance.did -t js > \ src/declarations/sons_governance/sons_governance.did.js
/tmp/didc bind src/sons_governance/sons_governance.did -t ts > \ src/declarations/sons_governance/sons_governance.did.d.ts
cp src/sons_governance/sons_governance.did \ src/declarations/sons_governance/sons_governance.didStep 3: Verify Generated Files
Section titled “Step 3: Verify Generated Files”After build, verify all files exist:
# Check WASM filesls -lh Deploy/wasm/sons_governance_docker.wasm*ls -lh src/backend/governance_wasm/sons_governance.wasm.gz
# Check declarationsls -lh src/declarations/sons_governance/Expected files:
WASM files:
- ✅
Deploy/wasm/sons_governance_docker.wasm- Raw WASM (for deployment) - ✅
Deploy/wasm/sons_governance_docker.wasm.gz- Gzipped WASM (for DAO proposals) - ✅
src/backend/governance_wasm/sons_governance.wasm.gz- Embedded WASM (used by backend)
Declarations:
- ✅
src/declarations/sons_governance/sons_governance.did- Candid interface (copied) - ✅
src/declarations/sons_governance/sons_governance.did.js- JavaScript IDL factory (generated by didc) - ✅
src/declarations/sons_governance/sons_governance.did.d.ts- TypeScript type definitions (generated by didc) - ✅
src/declarations/sons_governance/index.js- Actor factory (manually maintained) - ✅
src/declarations/sons_governance/index.d.ts- TypeScript index (manually maintained)
Build Order Dependencies
Section titled “Build Order Dependencies”The governance canister must be built before the backend canister:
1. Build sons_governance WASM ↓2. Optimize governance WASM (ic-wasm shrink + Candid metadata) ↓3. Gzip governance WASM for embedding ↓4. Copy to src/backend/governance_wasm/sons_governance.wasm.gz ↓5. Build ohshii_launcher_backend (includes governance WASM via include_bytes!) ↓6. Build other canisters (dao_storage, pool_manager, ohshii_governance)Why this order?
- Backend uses
include_bytes!("../governance_wasm/sons_governance.wasm.gz")at compile time - Rust’s
include_bytes!()macro reads the file during compilation - If governance WASM doesn’t exist, backend build fails
Dockerfile Build Process
Section titled “Dockerfile Build Process”The Dockerfile handles the correct build order automatically:
# STEP 1: Build sons_governance FIRSTRUN cargo build --target wasm32-unknown-unknown --release -p sons_governance
# Optimize governance WASMRUN ic-wasm target/wasm32-unknown-unknown/release/sons_governance.wasm \ -o target/wasm32-unknown-unknown/release/sons_governance_optimized.wasm shrink && \ ic-wasm target/wasm32-unknown-unknown/release/sons_governance_optimized.wasm \ -o target/wasm32-unknown-unknown/release/sons_governance_final.wasm \ metadata candid:service -f src/sons_governance/sons_governance.did -v public
# Copy and gzip for backend embeddingRUN mkdir -p src/backend/governance_wasm && \ cp target/wasm32-unknown-unknown/release/sons_governance_final.wasm \ src/backend/governance_wasm/sons_governance.wasm && \ gzip -9 -f -k src/backend/governance_wasm/sons_governance.wasm
# STEP 2: Build backend (now includes fresh governance WASM)RUN cargo build --target wasm32-unknown-unknown --release \ -p ohshii_launcher_backend \ -p dao_storage \ -p pool_manager \ -p ohshii_governanceWhen to Regenerate
Section titled “When to Regenerate”ALWAYS regenerate when you:
- Modify the
.didfile (src/sons_governance/sons_governance.did) - Add/remove public methods in
lib.rs - Change struct definitions in
types.rsthat are exposed via Candid - Add/modify types exported by the canister
- Update Rust code that affects the WASM binary
Regeneration process:
# Full rebuild (recommended)./Deploy/docker-build.sh
# Or manual steps:# 1. Build WASMcargo build --target wasm32-unknown-unknown --release -p sons_governance
# 2. Regenerate declarations/tmp/didc bind src/sons_governance/sons_governance.did -t js > \ src/declarations/sons_governance/sons_governance.did.js/tmp/didc bind src/sons_governance/sons_governance.did -t ts > \ src/declarations/sons_governance/sons_governance.did.d.tsBackend Embedding
Section titled “Backend Embedding”The backend canister embeds the governance WASM at compile time:
Location: src/backend/src/lib.rs
const GOVERNANCE_WASM: &[u8] = include_bytes!("../governance_wasm/sons_governance.wasm.gz");Usage:
- When creating an LGE with governance vesting, backend deploys a new governance canister
- The embedded WASM is used for deployment
- Each LGE campaign gets its own governance canister instance
TypeScript Integration
Section titled “TypeScript Integration”After generating declarations, update canister-types.js if new types were added:
File: src/frontend/src/types/canister-types.js
// ============================================================================// ICO GOVERNANCE TYPES (for governance and vesting)// ============================================================================
/** @typedef {import('@declarations/sons_governance/sons_governance.did.d.ts').NewType} NewType */Usage in components:
/** @typedef {import('../types/canister-types').GovernanceVotingPowerResult} GovernanceVotingPowerResult */
import { idlFactory as sonsGovernanceIDL } from '@declarations/sons_governance';Verification
Section titled “Verification”1. Check TypeScript Compilation
Section titled “1. Check TypeScript Compilation”npx tsc --noEmit --allowJs --checkJs --jsx preserve \ src/frontend/src/pages/OnsVotingPage.jsx2. Verify WASM Hash
Section titled “2. Verify WASM Hash”# Check raw WASM hashshasum -a 256 Deploy/wasm/sons_governance_docker.wasm
# Check gzipped WASM hash (for DAO proposals)shasum -a 256 Deploy/wasm/sons_governance_docker.wasm.gz
# Check embedded WASM hashshasum -a 256 src/backend/governance_wasm/sons_governance.wasm.gz3. Verify Import Works
Section titled “3. Verify Import Works”In your component:
import { idlFactory as sonsGovernanceIDL } from '@declarations/sons_governance';Common Issues
Section titled “Common Issues”Error: “Cannot find module ‘@declarations/sons_governance’”
Section titled “Error: “Cannot find module ‘@declarations/sons_governance’””Cause: Declarations not generated or index.js missing
Solution:
- Run
./Deploy/docker-build.shto generate declarations - Verify
index.jsandindex.d.tsexist insrc/declarations/sons_governance/ - Check
index.jsexports theidlFactory
Error: “Property X does not exist on type Y”
Section titled “Error: “Property X does not exist on type Y””Cause: Declarations out of sync with .did file
Solution: Regenerate declarations after any .did file changes:
/tmp/didc bind src/sons_governance/sons_governance.did -t ts > \ src/declarations/sons_governance/sons_governance.did.d.tsError: “/tmp/didc: no such file or directory”
Section titled “Error: “/tmp/didc: no such file or directory””Cause: didc not installed
Solution: Install didc (see Step 2 above) or use ./Deploy/docker-build.sh which installs it automatically
Error: Backend build fails with “file not found: governance_wasm/sons_governance.wasm.gz”
Section titled “Error: Backend build fails with “file not found: governance_wasm/sons_governance.wasm.gz””Cause: Governance WASM not built before backend
Solution: Always build governance first:
./Deploy/docker-build.sh # Builds governance first, then backendError: “include_bytes! failed to read file”
Section titled “Error: “include_bytes! failed to read file””Cause: Governance WASM.gz file missing or wrong path
Solution:
- Verify file exists:
ls -lh src/backend/governance_wasm/sons_governance.wasm.gz - Rebuild governance:
./Deploy/docker-build.sh - Check Dockerfile copied file correctly
Why Not dfx generate?
Section titled “Why Not dfx generate?”dfx generateonly works for canisters defined indfx.json- Dynamic canisters (created at runtime) are not in
dfx.json didcis the standalone tool used internally bydfx generate- Allows manual generation for any
.didfile
Best Practices
Section titled “Best Practices”- Always rebuild after .did changes - Don’t manually edit generated files
- Commit generated files - These are part of the codebase (needed for TypeScript)
- Update canister-types.js - Maintain the type bridge for new types
- Test compilation - Run
npx tsc --noEmitafter regeneration - Keep .did in sync - Ensure .did matches the Rust structs exactly
- Use Docker build - Ensures reproducible builds and correct build order
- Verify hashes - Check WASM hashes match expected values after build
Quick Reference
Section titled “Quick Reference”One-Liner Build Command
Section titled “One-Liner Build Command”./Deploy/docker-build.shThis single command:
- ✅ Builds governance WASM with Docker
- ✅ Optimizes WASM (shrink + Candid metadata)
- ✅ Creates gzipped version for embedding
- ✅ Generates TypeScript/JavaScript declarations
- ✅ Calculates SHA-256 hashes
- ✅ Saves hashes to
Deploy/WASM_HASHES.txt
Manual Build (if Docker unavailable)
Section titled “Manual Build (if Docker unavailable)”# 1. Build governance WASMcargo build --target wasm32-unknown-unknown --release -p sons_governance
# 2. Optimize WASMic-wasm target/wasm32-unknown-unknown/release/sons_governance.wasm \ -o target/wasm32-unknown-unknown/release/sons_governance_optimized.wasm shrink
ic-wasm target/wasm32-unknown-unknown/release/sons_governance_optimized.wasm \ -o target/wasm32-unknown-unknown/release/sons_governance_final.wasm \ metadata candid:service -f src/sons_governance/sons_governance.did -v public
# 3. Copy and gzip for backendmkdir -p src/backend/governance_wasmcp target/wasm32-unknown-unknown/release/sons_governance_final.wasm \ src/backend/governance_wasm/sons_governance.wasmgzip -9 -k src/backend/governance_wasm/sons_governance.wasm
# 4. Generate declarationsmkdir -p src/declarations/sons_governance/tmp/didc bind src/sons_governance/sons_governance.did -t js > \ src/declarations/sons_governance/sons_governance.did.js/tmp/didc bind src/sons_governance/sons_governance.did -t ts > \ src/declarations/sons_governance/sons_governance.did.d.tscp src/sons_governance/sons_governance.did \ src/declarations/sons_governance/sons_governance.didRelated Documentation
Section titled “Related Documentation”- Docker Build Guide:
Deploy/DOCKER_DEPLOY_GUIDE.md - Candid declarations: regenerate the launcher canisters’ declarations with
dfx generate --network ic; forsons_governance(a dynamic canister not indfx.json) usedidc bindagainstsrc/sons_governance/sons_governance.did. - Frontend type strategy: see
docs/FRONTEND.md(thecanister-types.js/utils/canister.jsbridges and the JSDoc@ts-checkpolicy). - Main README:
README.md(Governance Tier Purchase Limit System section)