Dfx nns install sns Error installing Index WASM

Maybe I’m naive but, generally speaking, is it possible to propose a Sns locally when running a replica and governance spinned with dfx nns install? Or maybe that’s actually not supported at all?

I resolved my previous issue by transferring cycles, but now I am hitting the next error.

2024-07-10 18:59:07.736840 UTC: [Canister rrkah-fqaaa-aaaaa-aaaaq-cai] [Governance] Execution of proposal: 10 failed. Reason: GovernanceError { error_type: External, error_message: “Error in deploy_new_sns response: SnsWasmError { message: "Error installing Index WASM: Failed to install WASM on canister avqkn-guaaa-aaaaa-qaaea-cai: error code 5: Canister avqkn-guaaa-aaaaa-qaaea-cai trapped explicitly: failed to decode call arguments: Custom(Fail to decode argument 0 from table0 to record { ledger_id : principal }\n\nCaused by:\n Subtyping error: Type mismatch at external/crate_index__candid-0.10.6/src/de.rs:994)" } refund result: Ok(\n (),\n)” } (Proposal title: Some(“NNS Proposal to create an SNS named ‘CYCLES-TRANSFER-STATION’”))

So, not sure if this can be resolved or I should actually just give up developing this on the IC?

The root cause of this problem is dfx nns install being buggy.

  1. When using dfx extension install nns --version 0.4.1, the Icrc Index canister uploaded in the deployed SNS-W canister is an outdated WASM - i.e., not the “ng” canister - which does not match the init parameters used when the SNS proposal is executed to spin the Index canister of the SNS project.

  2. dfx extension install nns --version 0.4.2 is better in the sense that it uploads an Index “ng” canister WASM, but that stuff is still buggy and ultimately leads to the same problem. I don’t know exactly what the issue is, but everything seems to point out that the WASM contained in the extension is too old or the Candid files used to spin the Index canister are too recent. One or the other, it’s buggy and inconsistent.

For local testing purposes, the SNS-W is actually deployed locally without many guards on who can execute its functions. Therefore, the solution to the issue is to manually upload a valid WASM version of the Index canister.

At this point, you might ask yourself: “Yeah, that’s cool, but how do you know which version?”

So, here’s the thing: after 5 hours of debugging yesterday, writing hacks, and being totally upset by the developer experience, I finally had luck. I just reused the same commit hash that I am using in Oisy and Juno to deploy the Index canister, and it worked out. I could have cried.

Anyway, so here’s my download script:

#!/bin/bash

DIR=.

IC_COMMIT="43f31c0a1b0d9f9ecbc4e2e5f142c56c7d9b0c7b"

curl -sSL https://download.dfinity.systems/ic/$IC_COMMIT/canisters/ic-icrc1-index-ng.wasm.gz -o "$DIR"/ic-icrc1-index-ng.wasm.gz

curl -sSL https://raw.githubusercontent.com/dfinity/ic/$IC_COMMIT/rs/rosetta-api/icrc1/index-ng/index-ng.did -o "$DIR"/ic-icrc1-index-ng.did

And that’s the script I use to manually upload the WASM to SNS-W:

#!/usr/bin/env node

import { hexStringToUint8Array } from '@dfinity/utils';
import { createHash } from 'crypto';
import { copyFileSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { getSnsWasmActor } from './actor.utils.mjs';

const snsWasmCanisterId = 'qaa6y-5yaaa-aaaaa-aaafa-cai';

copyFileSync(
	join(process.cwd(), 'node_modules/@dfinity/nns/dist/candid/sns_wasm.idl.js'),
	join(process.cwd(), 'node_modules/@dfinity/nns/dist/candid/sns_wasm.idl.mjs')
);

const loadGWasm = async () => {
	const buffer = await readFile(`${process.cwd()}/ic-icrc1-index-ng.wasm.gz`);

	return {
		wasm: [...new Uint8Array(buffer)],
		hash: createHash('sha256').update(buffer).digest('hex')
	};
};

const deployIndexCanister = async () => {
	// The canister of that SNS-Wasm on mainnet is qaa6y-5yaaa-aaaaa-aaafa-cai
	const actor = await getSnsWasmActor(snsWasmCanisterId);

	const { wasm, hash } = await loadGWasm();

	console.log(`Upload ${hash}`);

	await actor.add_wasm({
		hash: hexStringToUint8Array(hash),
		wasm: [
			{
				wasm,
				proposal_id: [],
				canister_type: 6 // #!/usr/bin/env node

import { hexStringToUint8Array } from '@dfinity/utils';
import { createHash } from 'crypto';
import { copyFileSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { getSnsWasmActor } from './actor.utils.mjs';

const snsWasmCanisterId = 'qaa6y-5yaaa-aaaaa-aaafa-cai';

copyFileSync(
	join(process.cwd(), 'node_modules/@dfinity/nns/dist/candid/sns_wasm.idl.js'),
	join(process.cwd(), 'node_modules/@dfinity/nns/dist/candid/sns_wasm.idl.mjs')
);

const loadGWasm = async () => {
	const buffer = await readFile(`${process.cwd()}/ic-icrc1-index-ng.wasm.gz`);

	return {
		wasm: [...new Uint8Array(buffer)],
		hash: createHash('sha256').update(buffer).digest('hex')
	};
};

const deployIndexCanister = async () => {
	// The canister of that SNS-Wasm on mainnet is qaa6y-5yaaa-aaaaa-aaafa-cai
	const actor = await getSnsWasmActor(snsWasmCanisterId);

	const { wasm, hash } = await loadGWasm();

	console.log(`Upload ${hash}`);

	await actor.add_wasm({
		hash: hexStringToUint8Array(hash),
		wasm: [
			{
				wasm,
				proposal_id: [],
				canister_type: 6 // Yolo it's 6 for Index
			}
		]
	});

	// dfx canister call qaa6y-5yaaa-aaaaa-aaafa-cai get_latest_sns_version_pretty
	console.log(await actor.get_latest_sns_version_pretty(null));
};

await deployIndexCanister();

			}
		]
	});

	// dfx canister call qaa6y-5yaaa-aaaaa-aaafa-cai get_latest_sns_version_pretty
	console.log(await actor.get_latest_sns_version_pretty(null));
};

await deployIndexCanister();

Just copying the above code won’t probably work. Check out the repo for everything: https://github.com/peterpeterparker/proposals.network

See also: dfx-extensions/e2e/launch-simple-sns.sh at c88b9e52045174bbdb5cc879e4a08370e975fb5d · dfinity/dfx-extensions · GitHub