{"id":30703185,"url":"https://github.com/dfinity/sns-testing","last_synced_at":"2025-09-02T16:56:58.657Z","repository":{"id":163158979,"uuid":"638542861","full_name":"dfinity/sns-testing","owner":"dfinity","description":"Testing SNS in local testing environment","archived":false,"fork":false,"pushed_at":"2025-06-10T16:00:58.000Z","size":581,"stargazers_count":10,"open_issues_count":9,"forks_count":16,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-06-10T16:34:24.668Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dfinity.png","metadata":{"files":{"readme":"README.md","changelog":"change_subnet_params.sh","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-05-09T15:16:56.000Z","updated_at":"2025-04-04T04:01:30.000Z","dependencies_parsed_at":null,"dependency_job_id":"0b7fceb6-9781-486f-8ed0-359a2ab7a258","html_url":"https://github.com/dfinity/sns-testing","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/dfinity/sns-testing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fsns-testing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fsns-testing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fsns-testing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fsns-testing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dfinity","download_url":"https://codeload.github.com/dfinity/sns-testing/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fsns-testing/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273317765,"owners_count":25084037,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-02T02:00:09.530Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-02T16:56:53.536Z","updated_at":"2025-09-02T16:56:58.642Z","avatar_url":"https://github.com/dfinity.png","language":"Shell","readme":"# Testing SNS in local testing environment\n\n\u003e This repository currently does not accept external contributions in the form of pull requests. Please submit your suggestions and bug reports by [opening a ticket](https://github.com/dfinity/sns-testing/issues).\n\n\n## Purpose\n\nThe main purpose of `sns-testing` is to enable developers of Internet Computer (IC) dapps to test Service Nervous System (SNS) decentralization. However, this solution may also be applicable in other scenarios, e.g.:\n\n* Testing tools such as `dfx`, `quill`, `sns`.\n* Testing UX-related aspects before releasing the [NNS frontend dapp](https://nns.ic0.app/).\n\n## How to use these instructions\n\nAssuming you are a developer who wants to test SNS-decentralization of their dapp, you need to establish your own dapp's deployment process before using this solution. Usually, a testing deployment is done in a shell script that interacts with a local replica via [DFX](https://internetcomputer.org/docs/current/references/cli-reference/dfx-parent).\n\nYou might need to slightly adjust your deployment script to work with sns-testing. In particular, please avoid running `dfx start` or `dfx replica` inside your deployment script (`sns-testing` will take care of starting a replica instance for you).\n\nIf you do not yet have a dapp that is ready for decentralization, you may still run `sns-testing` with the built-in test dapp.\n\n## Special instructions for Apple silicon users\n\n\u003ca name=\"apple-silicon\"\u003e\u003c/a\u003e\n\n_[Skip to the next section](#docker) if you are using an x86-compatible system, e.g., Linux, Windows, or Intel-based Mac._\n\nThe `sns-testing` solution is based on Docker; however, there are subtle issues while running Docker on new [Apple silicon](https://support.apple.com/en-us/HT211814) systems (e.g., Apple M1, Apple M2). Therefore, Apple silicon users are advised to run the commands provided by this repository _directly_. This requires additional preparation:\n\n0. Make sure you have Homebrew installed.\n   * Instructions: https://brew.sh/\n   * Use Homebrew to install (or upgrade to the latest available versions) `bash`, `coreutils` (needed for tools e.g., `sha256sum`), `jq`, and `yq`:\n     ```bash\n     brew install bash coreutils jq yq\n     ```\n\n   You also need rosetta that you can install by running:\n   ```bash\n   softwareupdate --install-rosetta\n   ```\n\n   Also make sure you have Rust installed including the `wasm32-unknown-unknown` target.\n   * Instructions: https://www.rust-lang.org/tools/install\n   * Add `wasm32-unknown-unknown` into your active toolchain by running:\n   ```bash\n   rustup target add wasm32-unknown-unknown\n   ```\n\n1. Ensure the newly installed tools are added to your `PATH`:\n   ```bash\n   echo 'export PATH=\"$PATH:/opt/homebrew/bin/:/usr/local/opt/coreutils/libexec/gnubin\"' \u003e\u003e \"${HOME}/.bashrc\"\n   ```\n   Above, we rely on `.bashrc`, as the main commands from this repository are to be executed via Bash.\n2. Clone this repository: \n   ```bash\n   git clone git@github.com:dfinity/sns-testing.git\n   cd sns-testing\n   ```\n3. Run the installation script:\n   ```bash\n   bash install.sh\n   ```\n4. Start a local replica (this will keep running in the current console; press ⌘+C to stop):\n   ```bash\n   DX_NET_JSON=\"${HOME}/.config/dfx/networks.json\"\n   mkdir -p \"$(dirname \"${DX_NET_JSON}\")\"\n   cp \"$DX_NET_JSON\" \"${DX_NET_JSON}.tmp\" 2\u003e/dev/null  # save original config if present\n   echo '{\n      \"local\": {\n         \"bind\": \"0.0.0.0:8080\",\n         \"type\": \"ephemeral\",\n         \"replica\": {\n            \"subnet_type\": \"system\",\n            \"port\": 8000\n         }\n      }\n   }' \u003e \"${DX_NET_JSON}\"\n   ./bin/dfx start --clean; \\\n   mv \"${DX_NET_JSON}.tmp\" \"$DX_NET_JSON\" 2\u003e/dev/null  # restore original config if it was present\n   ```\n\n   While running these instructions for the first time, you may need to hit the \"Allow\" button to authorize the system to execute the binaries shipped with sns-testing, e.g., `./bin/dfx`.\n\n   This should print the dashboard URL:\n\n    ```\n    Replica API running on 0.0.0.0:8000\n    ```\n\n5. Open another Bash console:\n   ```bash\n   bash\n   ```\n   and run the setup script:\n   ```bash\n   ./setup_locally.sh  # from Bash\n   ```\n   After this step, you can also access the [NNS frontend dapp](http://qsgjb-riaaa-aaaaa-aaaga-cai.localhost:8080/) from the browser.\n\n\n6. To validate the testing environment, run the test dapp shipped with this repository through the entire SNS lifecycle:\n   ```bash\n   ./run_basic_scenario.sh  # from Bash\n   ```\n   If the basic scenario finished successfully, you should see the message\n    `Basic scenario has successfully finished.` on the last line of the output.\n\n   Observe the newly created SNS instance via the [NNS frontend dapp](http://qsgjb-riaaa-aaaaa-aaaga-cai.localhost:8080/). When you try to login for the first time, you will need to register a new Internet Identity for testing.\n\n   \u003e If you have successfully executed the above commands, you are now ready to [test your own dapp's SNS decentralization](#lifecycle).\n\n7. Clean-up (after you are done testing):\n\n    \u003e Note that performing the clean-up will delete some files in the sns-testing repository and your DFX wallets\n    \u003e for the local network (not affecting mainnet).\n    \u003e Make sure to back up all files you move into the sns-testing repository.\n\n    ```bash\n    ./cleanup.sh  # from Bash\n    ```\n\n    It should now be possible to repeat the scenario starting from step 4.\n\n## Bootstrapping a testing environment via Docker\n\n\u003ca name=\"docker\"\u003e\u003c/a\u003e\n\n_This section explains the simplest way to set up a local environment for testing SNS decentralization. However, this solution is based on Docker and is currently [not supported on Apple silicon systems](#apple-silicon). Please proceed if you are using Linux, Windows, or Intel-based Mac._\n\nAfter getting familiar with the basic scenario, you may replace the test canister with your own one, and use this repo as a skeleton for creating a custom testing environment.\n\n\n1. If your dapp is ready for testing, clone it into the current directory and cd into it.\n\n2. Make sure you have the latest verion of the sns-testing Docker container by running the command:\n   ```bash\n   docker pull ghcr.io/dfinity/sns-testing:main\n   ```\n\n3. Start a local replica instance inside a Docker container:\n    ```bash\n   SNS_TESTING_INSTANCE=$(\n       docker run -p 8000:8000 -p 8080:8080 -v \"`pwd`\":/dapp -d ghcr.io/dfinity/sns-testing:main dfx start --clean\n   )\n   while ! docker logs $SNS_TESTING_INSTANCE 2\u003e\u00261 | grep -m 1 'Replica API running'\n   do\n       echo \"Awaiting local replica ...\"\n       sleep 3\n   done\n    ```\n    This should print the dashboard URL:\n\n    ```\n    Awaiting local replica ...\n    Replica API running on 0.0.0.0:8080\n    ```\n\n4. Run setup:\n    ```bash\n    docker exec -it $SNS_TESTING_INSTANCE bash setup_locally.sh\n    ```\n    After this step, you can also access the [NNS frontend dapp](http://qsgjb-riaaa-aaaaa-aaaga-cai.localhost:8080/)\n    from the browser on your host machine.\n\n5. Run the basic scenario:\n    ```bash\n    docker exec $SNS_TESTING_INSTANCE bash run_basic_scenario.sh\n    ```\n    If the basic scenario finished successfully, you should see the message\n    `Basic scenario has successfully finished.` on the last line of the output.\n\n    Observe the newly created SNS instance via the [NNS frontend dapp](http://qsgjb-riaaa-aaaaa-aaaga-cai.localhost:8080/). When you try to login for the first time, you will need to register a new Internet Identity for testing.\n\n6. If you have successfully executed the above commands, enter a Bash shell inside your `sns-testing` Docker instance by running\n   ```bash\n   docker exec -it $SNS_TESTING_INSTANCE bash\n   ```\n   Note: The instruction for testing your own dapp's SNS decentralization assume that all commands are executed from _this_ bash session (inside Docker). You should still have access to your dapp's files, as the repo was mounted at `/dapp` inside the container.\n\n   \u003e You are now ready to [test your own dapp's SNS decentralization](#lifecycle).\n\n7. Clean-up (after you are done testing):\n    ```bash\n    docker kill $SNS_TESTING_INSTANCE\n    ```\n    It should now be possible to repeat the scenario starting from step 1.\n\nThe above run-book could be easily automated and integrated into your CI/CD pipeline.\n\n## Troubleshooting\n\n-  If either of the ports 8000 or 8080 are occupied, then `docker run -p 8000:8000 -p 8080:8080 ...` and `./bin/dfx start --clean` are expected to fail.\n   In that case, you should run `docker ps` (if you have Docker installed on your system) and `lsof -i :8000` or `lsof -i :8080`\n   to determine the service listening on the port 8000 or 8080, correspondingly, and then close the service.\n\n## SNS lifecycle\n\n\u003ca name=\"lifecycle\"\u003e\u003c/a\u003e\n\n_This section assumes that you have successfully deployed a local environment for testing SNS decentralization and validated your setup by creating an SNS instance for the test dapp (shipped with `sns-testing`)._\n\nWe now explain how to test your own dapp's SNS decentralization.\n\nYour SNS configuration file should only specify a single initial SNS developer neuron\ncontrolled by your DFX principal for the following instructions to work without\nany additional steps required (otherwise, you'd need to _manually vote_ on SNS proposals\ncreated during these steps with your initial SNS developer neurons).\n\n0. Run the following script to ensure the local file system is in the right state:\n\n   ```bash\n   ./cleanup.sh  # from Bash\n   ```\n\n1. Deploy your dapp onto the local replica instance as per usual. You can find\n   your dapp repo under the path `/dapp` in the Docker container. This step\n   requires your dapp repo to have a deployment script that interacts with the\n   replica via the 8080 port.\n\n   If you don't yet have a solution to deploy your custom dapp, you can still\n   proceed with these instructions by deploying the test dapp provided with this\n   repo:\n\n   ```bash\n   ./deploy_test_canister.sh  # from Bash\n   ```\n\n   This will deploy a test canister (see Section\n   [Test canister](https://github.com/dfinity/sns-testing#test-canister)\n   for further details) which can be thought of as a placeholder\n   for your dapp.\n\n2. Give control of your dapp canisters to NNS:\n   ```bash\n   ./let_nns_control_dapp.sh  # from Bash\n   ```` \n   This automatically creates an SNS\n   configuration file named `sns_init.yaml`, unless such a file already exists\n   (e.g. you hand-crafted one yourself). The auto-generated file assumes that\n   you are using the test dapp.\n\n3. Submit an NNS proposal to create an SNS that will control your dapp canister(s):\n   ```bash\n   ./propose_sns.sh  # from Bash\n   ``` \n   The proposal will pass right away, because it is made by a neuron that has an\n   overwhelming amount of voting power (this is part of the testing environment).\n\n   After a few minutes, you should see a new SNS instance in the [Launchpad]\n   section of the NNS dapp.\n\n   [Launchpad]: http://qsgjb-riaaa-aaaaa-aaaga-cai.localhost:8080/launchpad\n\n4. Optional: Upgrade your dapp canister(s) via SNS proposal.\n\n   If you are going through these instructions using the test dapp, this step \n   can be done as follows:\n\n   ```bash\n   ./upgrade_test_canister.sh \"Swap is taking place.\"  # from Bash\n   ```\n\n   This submits an SNS proposal to upgrade the test dapp. If you are using the\n   auto-generated `sns_init.yaml` file, the proposing neuron will have all of\n   the voting power in the SNS. Thus, the proposal will be adopted and executed\n   right away. Otherwise, you you might to vote with additional initial neurons\n   to pass the upgrade proposal.\n\n   If you are using your own dapp rather than the test dapp, look at how\n   `upgrade_test_canister.sh` works. In short, it ends up calling `quill sns\n   make-upgrade-canister-proposal`. That command takes a fair number of\n   arguments. Therefore, it is helpful to look at how the script(s) here invoke\n   that command as a guide to how you can invoke the command to propose an\n   upgrade to your own dapp.\n   \n5. Once the swap starts, you can simulate multiple users' participation:\n\n   ```bash\n   ./participate_sns_swap.sh \u003cnum-participants\u003e \u003cicp-per-participant\u003e  # from Bash\n   ``` \n   \n   You can run `./participate_sns_swap.sh` multiple times (with different\n   arguments). For example, if you run \n   ```\n   ./participate_sns_swap.sh 2 7\n   ./participate_sns_swap.sh 4 3\n   ```\n\n   The first above command creates two user identities (called\n   `participant-000` and `participant-001`), each contributing 7 ICPs to the\n   swap. The second command then _reuses_ the first two participants, each\n   of which now contributes 3 _more_ ICPs; it then creates two _new_\n   participants, each contributing 3 ICPs as well. So, the overall contributions\n   will be:\n\n   Identity Name   | Swap Contribution, ICP\n   --------------- | ----------------------\n   participant-000 | 10\n   participant-001 | 10\n   participant-002 | 3\n   participant-003 | 3\n\n   You can also participate in the swap using the [NNS Dapp][nns-dapp]\n   (another feature of the test environment). There, you can conjure some ICP\n   for yourself using the \"Get ICP\" button (another feature of the test\n   environment), and use the ICP to participate in the ongoing swap.\n\n   [nns-dapp]: http://qsgjb-riaaa-aaaaa-aaaga-cai.localhost:8080\n\n   You can make the swap complete immediately by making the total participation\n   amount equal to the target ICP amount.\n\n6. Optional: Submit (another) proposal to upgrade one of your dapp canisters.\n   Unlike before, the proposal will probably not pass right away, because now,\n   the voting power is spread among multiple neurons.\n\n   If using the test dapp, this step can be performed like so:\n\n   ```bash\n   ./upgrade_test_canister.sh \"First upgrade after the initial token swap.\"  # from Bash\n   ```\n\n   This is similar to step 4, but we use a different message argument so that\n   there will be a visible change to the dapp.\n\n   * To make the upgrade proposal pass, tell the participants created by \n      `participate_sns_swap.sh` to vote on the proposal, like so:\n\n      ```bash\n      ./vote_on_sns_proposal.sh \u003cnum-participants\u003e \u003cproposal-id\u003e y  # from Bash\n      ```\n\n   * If you would rather test the proposal rejection scenario, simply replace\n      `y` in the above command with `n`.\n\n   **Casting sufficient votes.** In either of the above cases, make sure that `\u003cnum-participants\u003e` is sufficient\n   for the proposal to be decided by majority; i.e., we need _strictly more_ than\n   50%.\n   \n   For example, if you called `./participate_sns_swap.sh 100 \u003cicp-per-participant\u003e`, then\n   `./vote_on_sns_proposal.sh 51 \u003cproposal-id\u003e y` will cast enough yes-votes for\n   the proposal to be adopted, and `./vote_on_sns_proposal.sh 51 \u003cproposal-id\u003e n`\n   will cast enough no-votes for the proposal to be rejected, whereas, e.g.,\n   `./vote_on_sns_proposal.sh 50 \u003cproposal-id\u003e n` will still keep the proposal\n   open.\n\n   **Observing voting errors.** It is expected to get the error\n   \"Neuron not eligible to vote on proposal.\" for some neurons because\n   each participant gets a basket of neurons with various dissolve delays\n   and only neurons with dissolve delay at least\n   `neuron_minimum_dissolve_delay_to_vote_seconds` (according to the SNS configuration\n   file from step 1.) are eligible to vote. It is also expected to get the error\n   \"Neuron already voted on proposal.\" for some neurons because\n   they are followers of other neurons and our simple voting script\n   does not take this into account.\n  \nCongratulations! You have now seen SNS in action in a test environment. We\nrecommend that you experiment with different configurations until you find one\nthat works best for your project.\n\n## Check out SNS state\n\nYou can inspect the SNS state by running the following scripts:\n\n- `get_last_sns_proposal.sh`: displays the SNS proposal that was added most recently;\n- `get_sns_proposal.sh \u003cproposal-id\u003e`: displays the SNS proposal with given `\u003cproposal-id\u003e`;\n- `get_sns_canisters.sh`: shows the canister IDs of the SNS canisters and the registered dapp canisters;\n- `get_sns_neurons.sh`: displays all SNS neurons of your DFX identity;\n- `get_all_sns_neurons.sh`: displays all SNS neurons in the SNS governance canister;\n- `get_sns_swap_state.sh`: displays the SNS swap's state.\n\n## Test canister\n\nThe test canister is available in the directory `test`. Its purpose is\n* to provide a simple dapp that can be deployed\n  and handed over control of to an SNS in the basic scenario and\n* to show how generic SNS functions can be implemented and secured to be\n  only called by the SNS governance canister.\n\nInternally, the test canister keeps an integer counter and a greeting message.\nFurthermore, the test canister exposes public methods to get the value of the counter,\na greeting text starting with the greeting message, and a pair of functions (called\n`validate` and `execute` that can be used as generic SNS functions, which means that these methods can be called as a result of an SNS proposal ---\nsee Section 7 of this\n[tutorial](https://internetcomputer.org/docs/current/developer-docs/integrations/sns/get-sns/testflight)\nfor further details). \nCallers to the `execute` function are restricted to a canister ID that can be set\nin the initial arguments when deploying or upgrading the test canister.\nBy specifying the SNS governance canister ID as the allowed canister ID to call the `execute`\nfunction you make sure that this method can _only_ be invoked as a result of an adopted SNS proposal.\nNote that the `validate` function must be safe to call with any arguments because\nevery submitted SNS proposal for executing generic SNS functions triggers\nthe execution of the `validate` function (also if the SNS proposal is eventually rejected)\nand every SNS neuron can make such an SNS proposal.\nHence, callers of the `validate` function are not restricted in the test canister\n(you might still want to restrict callers of the `validate` function in your own canister, e.g.,\nto prevent users from using up your canister's cycles by calling the `validate` function)\nand the `validate` function is implemented to be safe to call by anyone with any arguments.\n\nYou should specify the SNS governance canister ID as the allowed canister ID\nto call the `execute` function.\n\n1. You can deploy the test canister from this repository by running the script\n   `./deploy_test_canister.sh`.\n\nYou should run the following steps after `deploy_sns.sh \u003cconfig-path\u003e`\nand before `open_sns_swap.sh` according to\nthe [SNS lifecycle](https://github.com/dfinity/sns-testing#sns-lifecycle) section.\n\n2. You can then register the test canister with the SNS by running the script\n   `./register_dapp.sh \u003ccanister-id\u003e`.\n   \n   Here, `\u003ccanister-id\u003e` is the principal of the canister that you want to decentralize.\n\n3. Upgrade the test canister by running the script `upgrade_test_canister.sh`.\n   The upgrade also stores the SNS governance canister ID in the canister's\n   memory to implement access control restrictions\n   to the `execute` method of the test canister\n   (only the SNS governance canister is allowed to call this method).\n4. To test generic functions of the test canister, you need to register them\n   first. An example SNS proposal to register generic functions (the functions\n   `validate` and `execute` of the test canister)\n   is submitted by running the script `./register_generic_functions_test.sh`.\n5. Now you can submit SNS proposals to execute the generic functions by running\n   `./execute_generic_functions_test.sh \u003cgreeting\u003e` where `\u003cgreeting\u003e`\n   is the new greeting message to be set in the test canister. Note that\n   `\u003cgreeting\u003e` must be non-empty (otherwise the proposal validation\n   should fail).\n6. Once the SNS proposal is executed, you should see the new greeting\n   message when invoking `dfx canister call test greet \"Martin\"`.\n   You should also see that the counter obtained via\n   `dfx canister call test get` got incremented after executing\n   the SNS proposal (the counter gets incremented upon every call to\n   the `execute` function).\n\n## Check out canister status\n\nYou can inspect the status of a canister `\u003cname\u003e` (with a corresponding entry\nin the `dfx.json` file) by running the script `./get_canister_status.sh \u003cname\u003e`.\nNote that your DFX identity must be a controller of the canister `\u003cname\u003e`\nfor that script to succeed. In particular, you can also invoke the script\nto check if your DFX identity is a controller of the canister `\u003cname\u003e`.\n\n## Hacking\n\nYou can find further instructions for expert users in the file\n[HACKING.md](https://github.com/dfinity/sns-testing/blob/main/HACKING.md).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdfinity%2Fsns-testing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdfinity%2Fsns-testing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdfinity%2Fsns-testing/lists"}