Skip to content

Governance Canister Build

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_backend via include_bytes!()
  • Requires special build process: Docker build → Candid generation with didc → Embedding
  • Location: src/sons_governance/
  • Declarations: src/declarations/sons_governance/
  1. Not in dfx.json: Dynamic canisters cannot use dfx generate
  2. Embedded WASM: Backend embeds governance WASM using include_bytes!("../governance_wasm/sons_governance.wasm.gz")
  3. Candid Generation: Must use didc tool instead of dfx generate
  4. Build Order: Governance must be built before backend (backend depends on governance WASM)
  1. Docker must be installed and running
  2. didc tool (installed automatically by build script, or manually)

Step 1: Build Governance Canister with Docker

Section titled “Step 1: Build Governance Canister with Docker”

The Docker build process handles the complete workflow:

Terminal window
# From project root
./Deploy/docker-build.sh

What happens inside Docker:

  1. Build governance WASM:

    Terminal window
    cargo build --target wasm32-unknown-unknown --release -p sons_governance
  2. 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 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 backend embedding:

    Terminal window
    mkdir -p src/backend/governance_wasm
    cp sons_governance_final.wasm src/backend/governance_wasm/sons_governance.wasm
    gzip -9 -f -k src/backend/governance_wasm/sons_governance.wasm
  4. 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

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:

Terminal window
# Ensure directory exists
mkdir -p src/declarations/sons_governance
# Generate JavaScript bindings
didc bind src/sons_governance/sons_governance.did -t js > \
src/declarations/sons_governance/sons_governance.did.js
# Generate TypeScript definitions
didc bind src/sons_governance/sons_governance.did -t ts > \
src/declarations/sons_governance/sons_governance.did.d.ts
# Copy .did file
cp src/sons_governance/sons_governance.did \
src/declarations/sons_governance/sons_governance.did

Manual Generation (if needed):

If you need to regenerate declarations manually:

Terminal window
# Install didc (one-time, if not already installed)
# macOS:
curl -fsSL https://github.com/dfinity/candid/releases/latest/download/didc-macos -o /tmp/didc
chmod +x /tmp/didc
# Linux:
curl -fsSL https://github.com/dfinity/candid/releases/latest/download/didc-linux64 -o /tmp/didc
chmod +x /tmp/didc
# Generate declarations
cd /path/to/dev-ohshii-launcher
mkdir -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.did

After build, verify all files exist:

Terminal window
# Check WASM files
ls -lh Deploy/wasm/sons_governance_docker.wasm*
ls -lh src/backend/governance_wasm/sons_governance.wasm.gz
# Check declarations
ls -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)

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

The Dockerfile handles the correct build order automatically:

# STEP 1: Build sons_governance FIRST
RUN cargo build --target wasm32-unknown-unknown --release -p sons_governance
# Optimize governance WASM
RUN 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 embedding
RUN 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_governance

ALWAYS regenerate when you:

  1. Modify the .did file (src/sons_governance/sons_governance.did)
  2. Add/remove public methods in lib.rs
  3. Change struct definitions in types.rs that are exposed via Candid
  4. Add/modify types exported by the canister
  5. Update Rust code that affects the WASM binary

Regeneration process:

Terminal window
# Full rebuild (recommended)
./Deploy/docker-build.sh
# Or manual steps:
# 1. Build WASM
cargo 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.ts

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

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';
Terminal window
npx tsc --noEmit --allowJs --checkJs --jsx preserve \
src/frontend/src/pages/OnsVotingPage.jsx
Terminal window
# Check raw WASM hash
shasum -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 hash
shasum -a 256 src/backend/governance_wasm/sons_governance.wasm.gz

In your component:

import { idlFactory as sonsGovernanceIDL } from '@declarations/sons_governance';

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:

  1. Run ./Deploy/docker-build.sh to generate declarations
  2. Verify index.js and index.d.ts exist in src/declarations/sons_governance/
  3. Check index.js exports the idlFactory

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:

Terminal window
/tmp/didc bind src/sons_governance/sons_governance.did -t ts > \
src/declarations/sons_governance/sons_governance.did.d.ts

Error: “/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:

Terminal window
./Deploy/docker-build.sh # Builds governance first, then backend

Error: “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:

  1. Verify file exists: ls -lh src/backend/governance_wasm/sons_governance.wasm.gz
  2. Rebuild governance: ./Deploy/docker-build.sh
  3. Check Dockerfile copied file correctly
  • dfx generate only works for canisters defined in dfx.json
  • Dynamic canisters (created at runtime) are not in dfx.json
  • didc is the standalone tool used internally by dfx generate
  • Allows manual generation for any .did file
  1. Always rebuild after .did changes - Don’t manually edit generated files
  2. Commit generated files - These are part of the codebase (needed for TypeScript)
  3. Update canister-types.js - Maintain the type bridge for new types
  4. Test compilation - Run npx tsc --noEmit after regeneration
  5. Keep .did in sync - Ensure .did matches the Rust structs exactly
  6. Use Docker build - Ensures reproducible builds and correct build order
  7. Verify hashes - Check WASM hashes match expected values after build
Terminal window
./Deploy/docker-build.sh

This 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
Terminal window
# 1. Build governance WASM
cargo build --target wasm32-unknown-unknown --release -p sons_governance
# 2. Optimize WASM
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
# 3. Copy and gzip for backend
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 -k src/backend/governance_wasm/sons_governance.wasm
# 4. Generate declarations
mkdir -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.did
  • Docker Build Guide: Deploy/DOCKER_DEPLOY_GUIDE.md
  • Candid declarations: regenerate the launcher canisters’ declarations with dfx generate --network ic; for sons_governance (a dynamic canister not in dfx.json) use didc bind against src/sons_governance/sons_governance.did.
  • Frontend type strategy: see docs/FRONTEND.md (the canister-types.js / utils/canister.js bridges and the JSDoc @ts-check policy).
  • Main README: README.md (Governance Tier Purchase Limit System section)