sns-launch

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SNS DAO Launch

SNS DAO 启动

What This Is

概述

Service Nervous System (SNS) is the DAO framework for decentralizing individual Internet Computer dapps. Like the NNS governs the IC network itself, an SNS governs a specific dapp -- token holders vote on proposals to upgrade code, manage treasury funds, and set parameters. Launching an SNS transfers canister control from developers to a community-owned governance system through a decentralization swap.
Service Nervous System (SNS) 是用于去中心化单个Internet Computer dapp的DAO框架。正如NNS治理IC网络本身一样,SNS治理特定的dapp——通证持有者通过投票表决提案来升级代码、管理国库资金以及设置参数。启动SNS会通过去中心化交换将容器控制权从开发者转移到社区所有的治理系统。

Prerequisites

前提条件

  • An NNS neuron with sufficient stake to submit proposals (mainnet)
  • Dapp canisters already deployed and working on mainnet
  • sns_init.yaml
    configuration file with all parameters defined
  • 拥有足够质押量的NNS神经元以提交提案(主网)
  • dapp容器已部署并在主网正常运行
  • 已定义所有参数的
    sns_init.yaml
    配置文件

Canister IDs

容器ID

CanisterMainnet IDPurpose
NNS Governance
rrkah-fqaaa-aaaaa-aaaaq-cai
Votes on SNS creation proposals
SNS-W (Wasm Modules)
qaa6y-5yaaa-aaaaa-aaafa-cai
Deploys and initializes SNS canisters
NNS Root
r7inp-6aaaa-aaaaa-aaabq-cai
Must be co-controller of dapp before launch
ICP Ledger
ryjl3-tyaaa-aaaaa-aaaba-cai
Handles ICP token transfers during swap
容器主网ID用途
NNS Governance
rrkah-fqaaa-aaaaa-aaaaq-cai
对SNS创建提案进行投票
SNS-W (Wasm Modules)
qaa6y-5yaaa-aaaaa-aaafa-cai
部署并初始化SNS容器
NNS Root
r7inp-6aaaa-aaaaa-aaabq-cai
启动前必须作为dapp的共同控制器
ICP Ledger
ryjl3-tyaaa-aaaaa-aaaba-cai
处理交换过程中的ICP通证转账

SNS Canisters Deployed

部署的SNS容器

When an SNS launch succeeds, SNS-W deploys these canisters on an SNS subnet:
CanisterPurpose
GovernanceProposal submission, voting, neuron management
LedgerSNS token transfers (ICRC-1 standard)
RootSole controller of all dapp canisters post-launch
SwapRuns the decentralization swap (ICP for SNS tokens)
IndexTransaction indexing for the SNS ledger
ArchiveHistorical transaction storage
当SNS启动成功后,SNS-W会在SNS子网部署以下容器:
容器用途
Governance提案提交、投票、神经元管理
LedgerSNS通证转账(遵循ICRC-1标准)
Root启动后成为所有dapp容器的唯一控制器
Swap运行去中心化交换(用ICP兑换SNS通证)
IndexSNS账本的交易索引
Archive历史交易存储

Mistakes That Break Your Build

导致构建失败的常见错误

  1. Setting
    min_participants
    too high.
    If you require 500 participants but only 200 show up, the entire swap fails and all ICP is refunded. Start conservative -- most successful SNS launches use 100-200 minimum participants.
  2. Forgetting to add NNS Root as co-controller before proposing. The launch process requires NNS Root to take over your canisters. If you submit the proposal without adding it first, the launch will fail at stage 6 when SNS Root tries to become sole controller.
  3. Not testing on SNS testflight first. Going straight to mainnet means discovering configuration issues after your NNS proposal is live. Always deploy a testflight mock SNS on mainnet first to verify governance and upgrade flows.
  4. Token economics that fail NNS review. The NNS community votes on your proposal. Unreasonable tokenomics (excessive developer allocation, zero vesting, absurd swap caps) will get rejected. Study successful SNS launches (OpenChat, Hot or Not, Kinic) for parameter ranges the community accepts.
  5. Not defining fallback controllers. If the swap fails, the dapp needs controllers to return control to. Without
    fallback_controller_principals
    , your dapp could become uncontrollable.
  6. Setting swap duration too short. Users across time zones need time to participate. Less than 24 hours is risky -- 3-7 days is standard.
  7. Forgetting restricted proposal types during swap. Six governance proposal types are blocked while the swap runs:
    ManageNervousSystemParameters
    ,
    TransferSnsTreasuryFunds
    ,
    MintSnsTokens
    ,
    UpgradeSnsControlledCanister
    ,
    RegisterDappCanisters
    ,
    DeregisterDappCanisters
    . Do not plan operations that require these during the swap window.
  8. Developer neurons with zero dissolve delay. Developers can immediately dump tokens post-launch. Set dissolve delays and vesting periods (12-48 months is typical) to signal long-term commitment.
  1. 设置
    min_participants
    过高
    。如果要求500名参与者但实际只有200人参与,整个交换会失败,所有ICP都会被退回。建议保守设置——大多数成功的SNS启动使用100-200的最低参与者数量。
  2. 提交提案前忘记添加NNS Root作为共同控制器。启动流程需要NNS Root接管你的容器。如果提交提案前未添加,当SNS Root尝试成为唯一控制器时,启动会在第6阶段失败。
  3. 未先在SNS Testflight上测试。直接上线主网意味着要在NNS提案生效后才发现配置问题。始终先在主网部署Testflight模拟SNS,以验证治理和升级流程。
  4. 通证经济方案未通过NNS审核。NNS社区会对提案进行投票。不合理的通证经济(如开发者分配过高、无锁仓、交换上限不合理)会被否决。研究成功的SNS启动案例(如OpenChat、Hot or Not、Kinic),参考社区接受的参数范围。
  5. 未定义 fallback 控制器。如果交换失败,dapp需要有控制器来重新获得控制权。如果没有设置
    fallback_controller_principals
    ,你的dapp可能会陷入无法控制的状态。
  6. 交换持续时间设置过短。跨时区用户需要足够时间参与。少于24小时的设置风险很高——标准时长为3-7天。
  7. 交换期间忘记限制提案类型。交换运行期间有6种治理提案类型会被阻止:
    ManageNervousSystemParameters
    TransferSnsTreasuryFunds
    MintSnsTokens
    UpgradeSnsControlledCanister
    RegisterDappCanisters
    DeregisterDappCanisters
    。不要在交换窗口期间计划需要使用这些提案的操作。
  8. 开发者神经元设置解锁延迟为0。开发者可能在启动后立即抛售通证。设置解锁延迟和锁仓期(通常为12-48个月)以表明长期承诺。

Implementation

实施步骤

SNS Configuration File (sns_init.yaml)

SNS配置文件(sns_init.yaml)

This is the single source of truth for all launch parameters. Copy the template from the
dfinity/sns-testing
repo and customize:
yaml
undefined
这是所有启动参数的唯一可信来源。从
dfinity/sns-testing
仓库复制模板并自定义:
yaml
undefined

Note: numeric values are in e8s (1 token = 100_000_000 e8s). Time values are in seconds.

Note: numeric values are in e8s (1 token = 100_000_000 e8s). Time values are in seconds.

=== PROJECT METADATA ===

=== PROJECT METADATA ===

name: MyProject description: > A decentralized application for [purpose]. This proposal requests the NNS to create an SNS for MyProject. logo: logo.png url: https://myproject.com
name: MyProject description: > A decentralized application for [purpose]. This proposal requests the NNS to create an SNS for MyProject. logo: logo.png url: https://myproject.com

=== NNS PROPOSAL TEXT ===

=== NNS PROPOSAL TEXT ===

NnsProposal: title: "Proposal to create an SNS for MyProject" url: "https://forum.dfinity.org/t/myproject-sns-proposal/XXXXX" summary: > This proposal creates an SNS DAO to govern MyProject. Token holders will control upgrades, treasury, and parameters.
NnsProposal: title: "Proposal to create an SNS for MyProject" url: "https://forum.dfinity.org/t/myproject-sns-proposal/XXXXX" summary: > This proposal creates an SNS DAO to govern MyProject. Token holders will control upgrades, treasury, and parameters.

=== FALLBACK (if swap fails, these principals regain control) ===

=== FALLBACK (if swap fails, these principals regain control) ===

fallback_controller_principals:
  • YOUR_PRINCIPAL_ID_HERE
fallback_controller_principals:
  • YOUR_PRINCIPAL_ID_HERE

=== CANISTER IDS TO DECENTRALIZE ===

=== CANISTER IDS TO DECENTRALIZE ===

dapp_canisters:
  • BACKEND_CANISTER_ID
  • FRONTEND_CANISTER_ID
dapp_canisters:
  • BACKEND_CANISTER_ID
  • FRONTEND_CANISTER_ID

=== TOKEN CONFIGURATION ===

=== TOKEN CONFIGURATION ===

Token: name: MyToken symbol: MYT transaction_fee: 0.0001 tokens logo: token_logo.png
Token: name: MyToken symbol: MYT transaction_fee: 0.0001 tokens logo: token_logo.png

=== GOVERNANCE PARAMETERS ===

=== GOVERNANCE PARAMETERS ===

Proposals: rejection_fee: 1 token initial_voting_period: 4 days maximum_wait_for_quiet_deadline_extension: 1 day
Neurons: minimum_creation_stake: 1 token
Voting: minimum_dissolve_delay: 1 month MaximumVotingPowerBonuses: DissolveDelay: duration: 8 years bonus: 100% # 2x voting power at max dissolve Age: duration: 4 years bonus: 25% RewardRate: initial: 2.5% final: 2.5% transition_duration: 0 seconds
Proposals: rejection_fee: 1 token initial_voting_period: 4 days maximum_wait_for_quiet_deadline_extension: 1 day
Neurons: minimum_creation_stake: 1 token
Voting: minimum_dissolve_delay: 1 month MaximumVotingPowerBonuses: DissolveDelay: duration: 8 years bonus: 100% # 2x voting power at max dissolve Age: duration: 4 years bonus: 25% RewardRate: initial: 2.5% final: 2.5% transition_duration: 0 seconds

=== TOKEN DISTRIBUTION ===

=== TOKEN DISTRIBUTION ===

Distribution: Neurons: # Developer allocation (with vesting) - principal: DEVELOPER_PRINCIPAL stake: 2_000_000 tokens memo: 0 dissolve_delay: 6 months vesting_period: 24 months
# Seed investors
- principal: INVESTOR_PRINCIPAL
  stake: 500_000 tokens
  memo: 1
  dissolve_delay: 3 months
  vesting_period: 12 months
InitialBalances: treasury: 5_000_000 tokens # Treasury (controlled by DAO) swap: 2_500_000 tokens # Sold during decentralization swap
total: 10_000_000 tokens # Must equal sum of all allocations
Distribution: Neurons: # Developer allocation (with vesting) - principal: DEVELOPER_PRINCIPAL stake: 2_000_000 tokens memo: 0 dissolve_delay: 6 months vesting_period: 24 months
# Seed investors
- principal: INVESTOR_PRINCIPAL
  stake: 500_000 tokens
  memo: 1
  dissolve_delay: 3 months
  vesting_period: 12 months
InitialBalances: treasury: 5_000_000 tokens # Treasury (controlled by DAO) swap: 2_500_000 tokens # Sold during decentralization swap
total: 10_000_000 tokens # Must equal sum of all allocations

=== DECENTRALIZATION SWAP ===

=== DECENTRALIZATION SWAP ===

Swap: minimum_participants: 100 minimum_direct_participation_icp: 50_000 tokens maximum_direct_participation_icp: 500_000 tokens minimum_participant_icp: 1 token maximum_participant_icp: 25_000 tokens duration: 7 days neurons_fund_participation: true
VestingSchedule: events: 5 # Neurons unlock in 5 stages interval: 3 months
confirmation_text: > I confirm that I am not a resident of a restricted jurisdiction and I understand the risks of participating in this token swap.
restricted_countries: - US - CN
undefined
Swap: minimum_participants: 100 minimum_direct_participation_icp: 50_000 tokens maximum_direct_participation_icp: 500_000 tokens minimum_participant_icp: 1 token maximum_participant_icp: 25_000 tokens duration: 7 days neurons_fund_participation: true
VestingSchedule: events: 5 # Neurons unlock in 5 stages interval: 3 months
confirmation_text: > I confirm that I am not a resident of a restricted jurisdiction and I understand the risks of participating in this token swap.
restricted_countries: - US - CN
undefined

Launch Process (11 Stages)

启动流程(11个阶段)

Stage 1:  Developer defines parameters in sns_init.yaml
Stage 2:  Developer adds NNS Root as co-controller of dapp canisters
Stage 3:  Developer submits NNS proposal using `dfx sns propose`
Stage 4:  NNS community votes on the proposal
Stage 5:  (If adopted) SNS-W deploys uninitialized SNS canisters
Stage 6:  SNS Root becomes sole controller of dapp canisters
Stage 7:  SNS-W initializes canisters in pre-decentralization-swap mode
Stage 8:  24-hour minimum wait before swap opens
Stage 9:  Decentralization swap opens (users send ICP, receive SNS neurons)
Stage 10: Swap closes (time expires or maximum ICP reached)
Stage 11: Finalization (exchange rate set, neurons created, normal mode)
Stage 1:  Developer defines parameters in sns_init.yaml
Stage 2:  Developer adds NNS Root as co-controller of dapp canisters
Stage 3:  Developer submits NNS proposal using `dfx sns propose`
Stage 4:  NNS community votes on the proposal
Stage 5:  (If adopted) SNS-W deploys uninitialized SNS canisters
Stage 6:  SNS Root becomes sole controller of dapp canisters
Stage 7:  SNS-W initializes canisters in pre-decentralization-swap mode
Stage 8:  24-hour minimum wait before swap opens
Stage 9:  Decentralization swap opens (users send ICP, receive SNS neurons)
Stage 10: Swap closes (time expires or maximum ICP reached)
Stage 11: Finalization (exchange rate set, neurons created, normal mode)

Motoko

Motoko

Prepare your canister for SNS control. The key requirement is that your canister accepts upgrade proposals from SNS governance:
motoko
import Principal "mo:core/Principal";
import Runtime "mo:core/Runtime";

persistent actor {
  // SNS Root will be set as sole controller after launch.
  // Your canister code does not need to change -- SNS governance
  // controls upgrades via the standard canister management API.

  // If your canister has admin functions, transition them to
  // accept SNS governance proposals instead of direct principal checks:

  var snsGovernanceId : ?Principal = null;

  // ⚠ SECURITY: This setter MUST be access-controlled. Without a check, any caller
  // can front-run you and set themselves as governance, permanently locking you out.
  // Replace DEPLOYER_PRINCIPAL with your actual principal or use an admin list.
  public shared ({ caller }) func setSnsGovernance(id : Principal) : async () {
    // Only the deployer (or canister controllers) should call this.
    assert (Principal.isController(caller));

    switch (snsGovernanceId) {
      case (null) { snsGovernanceId := ?id };
      case (?_) { Runtime.trap("SNS governance already set") };
    };
  };

  func requireGovernance(caller : Principal) {
    switch (snsGovernanceId) {
      case (?gov) {
        if (caller != gov) { Runtime.trap("Only SNS governance can call this") };
      };
      case (null) { Runtime.trap("SNS governance not configured") };
    };
  };

  // Admin functions become governance-gated:
  public shared ({ caller }) func updateConfig(newFee : Nat) : async () {
    requireGovernance(caller);
    // ... apply config change
  };
};
为你的容器准备好接受SNS控制。核心要求是你的容器接受来自SNS治理的升级提案:
motoko
import Principal "mo:core/Principal";
import Runtime "mo:core/Runtime";

persistent actor {
  // SNS Root will be set as sole controller after launch.
  // Your canister code does not need to change -- SNS governance
  // controls upgrades via the standard canister management API.

  // If your canister has admin functions, transition them to
  // accept SNS governance proposals instead of direct principal checks:

  var snsGovernanceId : ?Principal = null;

  // ⚠ SECURITY: This setter MUST be access-controlled. Without a check, any caller
  // can front-run you and set themselves as governance, permanently locking you out.
  // Replace DEPLOYER_PRINCIPAL with your actual principal or use an admin list.
  public shared ({ caller }) func setSnsGovernance(id : Principal) : async () {
    // Only the deployer (or canister controllers) should call this.
    assert (Principal.isController(caller));

    switch (snsGovernanceId) {
      case (null) { snsGovernanceId := ?id };
      case (?_) { Runtime.trap("SNS governance already set") };
    };
  };

  func requireGovernance(caller : Principal) {
    switch (snsGovernanceId) {
      case (?gov) {
        if (caller != gov) { Runtime.trap("Only SNS governance can call this") };
      };
      case (null) { Runtime.trap("SNS governance not configured") };
    };
  };

  // Admin functions become governance-gated:
  public shared ({ caller }) func updateConfig(newFee : Nat) : async () {
    requireGovernance(caller);
    // ... apply config change
  };
};

Rust

Rust

rust
use candid::{CandidType, Deserialize, Principal};
use ic_cdk::{init, post_upgrade, query, update};
use std::cell::RefCell;

#[derive(CandidType, Deserialize, Clone)]
struct Config {
    sns_governance: Option<Principal>,
}

thread_local! {
    // ⚠ STATE LOSS: RefCell<T> in thread_local! is HEAP storage — it is wiped on every
    // canister upgrade. In production, use ic-stable-structures (StableCell or StableBTreeMap)
    // to persist this across upgrades. At minimum, implement #[pre_upgrade]/#[post_upgrade]
    // hooks to serialize/deserialize this data. Without that, an upgrade erases your
    // governance config and locks out SNS control.
    static CONFIG: RefCell<Config> = RefCell::new(Config {
        sns_governance: None,
    });
}

fn require_governance(caller: Principal) {
    CONFIG.with(|c| {
        let config = c.borrow();
        match config.sns_governance {
            Some(gov) if gov == caller => (),
            Some(_) => ic_cdk::trap("Only SNS governance can call this"),
            None => ic_cdk::trap("SNS governance not configured"),
        }
    });
}

// ⚠ SECURITY: This setter MUST be access-controlled. Without a check, any caller
// can front-run you and set themselves as governance, permanently locking you out.
#[update]
fn set_sns_governance(id: Principal) {
    // Only canister controllers should call this.
    if !ic_cdk::api::is_controller(&ic_cdk::api::msg_caller()) {
        ic_cdk::trap("Only canister controllers can set governance");
    }
    CONFIG.with(|c| {
        let mut config = c.borrow_mut();
        if config.sns_governance.is_some() {
            ic_cdk::trap("SNS governance already set");
        }
        config.sns_governance = Some(id);
    });
}

#[update]
fn update_config(new_fee: u64) {
    let caller = ic_cdk::api::msg_caller();
    require_governance(caller);
    // ... apply config change
}
Cargo.toml dependencies:
toml
[package]
name = "sns_dapp_backend"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
candid = "0.10"
ic-cdk = "0.19"
serde = { version = "1", features = ["derive"] }
rust
use candid::{CandidType, Deserialize, Principal};
use ic_cdk::{init, post_upgrade, query, update};
use std::cell::RefCell;

#[derive(CandidType, Deserialize, Clone)]
struct Config {
    sns_governance: Option<Principal>,
}

thread_local! {
    // ⚠ STATE LOSS: RefCell<T> in thread_local! is HEAP storage — it is wiped on every
    // canister upgrade. In production, use ic-stable-structures (StableCell or StableBTreeMap)
    // to persist this across upgrades. At minimum, implement #[pre_upgrade]/#[post_upgrade]
    // hooks to serialize/deserialize this data. Without that, an upgrade erases your
    // governance config and locks out SNS control.
    static CONFIG: RefCell<Config> = RefCell::new(Config {
        sns_governance: None,
    });
}

fn require_governance(caller: Principal) {
    CONFIG.with(|c| {
        let config = c.borrow();
        match config.sns_governance {
            Some(gov) if gov == caller => (),
            Some(_) => ic_cdk::trap("Only SNS governance can call this"),
            None => ic_cdk::trap("SNS governance not configured"),
        }
    });
}

// ⚠ SECURITY: This setter MUST be access-controlled. Without a check, any caller
// can front-run you and set themselves as governance, permanently locking you out.
#[update]
fn set_sns_governance(id: Principal) {
    // Only canister controllers should call this.
    if !ic_cdk::api::is_controller(&ic_cdk::api::msg_caller()) {
        ic_cdk::trap("Only canister controllers can set governance");
    }
    CONFIG.with(|c| {
        let mut config = c.borrow_mut();
        if config.sns_governance.is_some() {
            ic_cdk::trap("SNS governance already set");
        }
        config.sns_governance = Some(id);
    });
}

#[update]
fn update_config(new_fee: u64) {
    let caller = ic_cdk::api::msg_caller();
    require_governance(caller);
    // ... apply config change
}
Cargo.toml依赖:
toml
[package]
name = "sns_dapp_backend"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
candid = "0.10"
ic-cdk = "0.19"
serde = { version = "1", features = ["derive"] }

Deploy & Test

部署与测试

Local Testing with sns-testing

使用sns-testing进行本地测试

bash
undefined
bash
undefined

Clone the SNS testing repository

Clone the SNS testing repository

WARNING: starting a fresh network wipes all local canister data. Only use for fresh setup.

WARNING: starting a fresh network wipes all local canister data. Only use for fresh setup.

icp network start -d
icp network start -d

Deploy NNS canisters locally (includes governance, ledger, SNS-W)

Deploy NNS canisters locally (includes governance, ledger, SNS-W)

Note: Use the sns-testing repo's setup scripts for NNS + SNS-W canister installation.

Note: Use the sns-testing repo's setup scripts for NNS + SNS-W canister installation.

See https://github.com/dfinity/sns-testing for current instructions.

See https://github.com/dfinity/sns-testing for current instructions.

Deploy your dapp canisters

Deploy your dapp canisters

icp deploy my_backend icp deploy my_frontend
icp deploy my_backend icp deploy my_frontend

Deploy a testflight SNS locally using your config

Deploy a testflight SNS locally using your config

Use the sns-testing repo tooling to deploy a local testflight SNS.

Use the sns-testing repo tooling to deploy a local testflight SNS.

See sns-testing README for the current testflight workflow.

See sns-testing README for the current testflight workflow.

undefined
undefined

Mainnet Testflight (Mock SNS)

主网Testflight(模拟SNS)

bash
undefined
bash
undefined

Deploy a mock SNS on mainnet to test governance flows

Deploy a mock SNS on mainnet to test governance flows

This does NOT do a real swap -- it creates a mock SNS you control

This does NOT do a real swap -- it creates a mock SNS you control

Use the sns-testing repo tooling for mainnet testflight deployment.

Use the sns-testing repo tooling for mainnet testflight deployment.

See https://github.com/dfinity/sns-testing for the current testflight workflow.

See https://github.com/dfinity/sns-testing for the current testflight workflow.

Test submitting proposals, voting, and upgrading via SNS governance

Test submitting proposals, voting, and upgrading via SNS governance

undefined
undefined

Mainnet Launch (Real)

主网正式启动

bash
undefined
bash
undefined

Step 1: Add NNS Root as co-controller of each dapp canister

Step 1: Add NNS Root as co-controller of each dapp canister

Requires dfx sns extension:
dfx extension install sns

Requires dfx sns extension:
dfx extension install sns

dfx sns prepare-canisters add-nns-root BACKEND_CANISTER_ID --network ic dfx sns prepare-canisters add-nns-root FRONTEND_CANISTER_ID --network ic
dfx sns prepare-canisters add-nns-root BACKEND_CANISTER_ID --network ic dfx sns prepare-canisters add-nns-root FRONTEND_CANISTER_ID --network ic

Step 2: Validate your config locally before submitting

Step 2: Validate your config locally before submitting

dfx sns init-config-file validate
dfx sns init-config-file validate

Or review the rendered proposal by inspecting the yaml output carefully.

Or review the rendered proposal by inspecting the yaml output carefully.

You can also test the full flow on a local replica first (see Local Testing above).

You can also test the full flow on a local replica first (see Local Testing above).

Step 3: Submit the proposal (THIS IS IRREVERSIBLE — double-check your config)

Step 3: Submit the proposal (THIS IS IRREVERSIBLE — double-check your config)

dfx sns propose --network ic --neuron $NEURON_ID sns_init.yaml
undefined
dfx sns propose --network ic --neuron $NEURON_ID sns_init.yaml
undefined

Verify It Works

验证功能

After local testflight deployment:

本地Testflight部署后:

bash
undefined
bash
undefined

List deployed SNS canisters

List deployed SNS canisters

icp canister id sns_governance icp canister id sns_ledger icp canister id sns_root icp canister id sns_swap
icp canister id sns_governance icp canister id sns_ledger icp canister id sns_root icp canister id sns_swap

Verify SNS governance is operational

Verify SNS governance is operational

icp canister call sns_governance get_nervous_system_parameters '()'
icp canister call sns_governance get_nervous_system_parameters '()'

Expected: returns the governance parameters you configured

Expected: returns the governance parameters you configured

Verify token distribution

Verify token distribution

icp canister call sns_ledger icrc1_total_supply '()'
icp canister call sns_ledger icrc1_total_supply '()'

Expected: matches your total token supply

Expected: matches your total token supply

Verify dapp canister controllers changed

Verify dapp canister controllers changed

icp canister status BACKEND_CANISTER_ID
icp canister status BACKEND_CANISTER_ID

Expected: controller is the SNS Root canister, NOT your principal

Expected: controller is the SNS Root canister, NOT your principal

Test an SNS proposal (upgrade your canister via governance)

Test an SNS proposal (upgrade your canister via governance)

icp canister call sns_governance manage_neuron '(record { ... })'
icp canister call sns_governance manage_neuron '(record { ... })'

Expected: proposal created, can be voted on

Expected: proposal created, can be voted on

undefined
undefined

After mainnet launch:

主网启动后:

bash
undefined
bash
undefined

Check swap status

Check swap status

icp canister call SNS_SWAP_ID get_state '()' -e ic
icp canister call SNS_SWAP_ID get_state '()' -e ic

Expected: shows swap status, participation count, ICP raised

Expected: shows swap status, participation count, ICP raised

Check SNS governance

Check SNS governance

icp canister call SNS_GOVERNANCE_ID get_nervous_system_parameters '()' -e ic
icp canister call SNS_GOVERNANCE_ID get_nervous_system_parameters '()' -e ic

Expected: returns your configured parameters

Expected: returns your configured parameters

Verify dapp controller is SNS Root

Verify dapp controller is SNS Root

icp canister status BACKEND_CANISTER_ID -e ic
icp canister status BACKEND_CANISTER_ID -e ic

Expected: single controller = SNS Root canister ID

Expected: single controller = SNS Root canister ID

undefined
undefined