diff --git a/scripts/test-vectors/index-20220613.ts b/scripts/test-vectors/20220613-index.ts similarity index 100% rename from scripts/test-vectors/index-20220613.ts rename to scripts/test-vectors/20220613-index.ts diff --git a/scripts/test-vectors/index-20220617.ts b/scripts/test-vectors/20220617-index.ts similarity index 100% rename from scripts/test-vectors/index-20220617.ts rename to scripts/test-vectors/20220617-index.ts diff --git a/scripts/test-vectors/index-20220817.ts b/scripts/test-vectors/20220817-index.ts similarity index 91% rename from scripts/test-vectors/index-20220817.ts rename to scripts/test-vectors/20220817-index.ts index a264caeb6..5c4039bf2 100644 --- a/scripts/test-vectors/index-20220817.ts +++ b/scripts/test-vectors/20220817-index.ts @@ -2,7 +2,7 @@ // https://github.com/Zondax/ledger-icp/issues/166 // We'll keep each script for reproducibility. import { IDL } from "@dfinity/candid"; -import { toLeaveCommunityFundRequest } from "../../packages/nns/src/canisters/governance/request.converters"; +import { toLeaveCommunityFundRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; import { ManageNeuronFn } from "./governance.idl"; import { createBlob, writeToJson } from "./utils"; diff --git a/scripts/test-vectors/20220923-list-neurons.ts b/scripts/test-vectors/20220923-list-neurons.ts new file mode 100644 index 000000000..ad9e03b7d --- /dev/null +++ b/scripts/test-vectors/20220923-list-neurons.ts @@ -0,0 +1,42 @@ +// Date represents the date of the issue with the test vectors sent to Zondax: +// https://github.com/Zondax/ledger-icp/issues/166 +// We'll keep each script for reproducibility. +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { fromListNeurons } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ListNeuronsFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createListNeurons = (neuronIds?: NeuronId[]) => { + const rawRequestBody = fromListNeurons(neuronIds); + + return { + blob_candid: createBlob({ + arg: IDL.encode(ListNeuronsFn.argTypes, [rawRequestBody]), + methodName: "list_neurons", + }), + name: "List Neurons", + candid_request: rawRequestBody, + output: ["0 | Transaction type : List Own Neurons"], + }; +}; + +const main = () => { + try { + const vectors = [createListNeurons()]; + + writeToJson({ + data: vectors, + fileName: "test-vectors.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20220923-spawn-neuron.ts b/scripts/test-vectors/20220923-spawn-neuron.ts new file mode 100644 index 000000000..ddec324f2 --- /dev/null +++ b/scripts/test-vectors/20220923-spawn-neuron.ts @@ -0,0 +1,114 @@ +// Date represents the date of the issue with the test vectors sent to Zondax: +// https://github.com/Zondax/ledger-icp/issues/166 +// We'll keep each script for reproducibility. +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toSpawnNeuronRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { Principal } from "@dfinity/principal"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, splitPrincipal, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); +const mockPrincipal = Principal.fromText( + "xlmdg-vkosz-ceopx-7wtgu-g3xmd-koiyc-awqaq-7modz-zf6r6-364rh-oqe", +); + +const createCandidSpawnNeuron = ({ + neuronId, + percentageToSpawn, + newController, + nonce, +}: { + neuronId: NeuronId; + percentageToSpawn?: number; + newController?: Principal; + nonce?: bigint; +}) => { + const rawRequestBody = toSpawnNeuronRequest({ + neuronId, + percentageToSpawn, + newController, + nonce, + }); + + const outputs = [ + "Transaction type : Spawn Neuron", + `Neuron ID : ${neuronId}`, + ]; + + if (percentageToSpawn !== undefined) { + outputs.push(`Percentage to spawn : ${percentageToSpawn}`); + } + + const controllerMessages = + newController === undefined + ? ["Controller : Self"] + : splitPrincipal(newController).map( + (data, i, elements) => + `Controller [${i + 1}/${elements.length}] : ${data}`, + ); + + outputs.push(...controllerMessages); + + if (nonce !== undefined) { + outputs.push(`Nonce : ${nonce}`); + } + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Spawn Neuron", + candid_request: rawRequestBody, + output: outputs.map((data, index) => `${index + 1} | ${data}`), + }; +}; + +const main = () => { + try { + const vectors = [ + createCandidSpawnNeuron({ + neuronId: mockNeuronId, + }), + createCandidSpawnNeuron({ + neuronId: mockNeuronId2, + }), + createCandidSpawnNeuron({ + neuronId: mockNeuronId2, + percentageToSpawn: 50, + }), + createCandidSpawnNeuron({ + neuronId: mockNeuronId2, + newController: mockPrincipal, + }), + createCandidSpawnNeuron({ + neuronId: mockNeuronId2, + percentageToSpawn: 50, + newController: mockPrincipal, + }), + createCandidSpawnNeuron({ + neuronId: mockNeuronId, + percentageToSpawn: 50, + nonce: BigInt(12345), + }), + createCandidSpawnNeuron({ + neuronId: mockNeuronId2, + percentageToSpawn: 30, + newController: mockPrincipal, + nonce: BigInt(12345), + }), + ]; + + writeToJson({ + data: vectors, + fileName: "test-vectors-20220817.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20221005-auto-stake-maturity.ts b/scripts/test-vectors/20221005-auto-stake-maturity.ts new file mode 100644 index 000000000..c15b09492 --- /dev/null +++ b/scripts/test-vectors/20221005-auto-stake-maturity.ts @@ -0,0 +1,71 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toAutoStakeMaturityRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createCandidAutoStakeMaturity = ({ + neuronId, + autoStake: autoStake, +}: { + neuronId: NeuronId; + autoStake: boolean; +}) => { + const rawRequestBody = toAutoStakeMaturityRequest({ + neuronId, + autoStake, + }); + + const outputs = [ + "Transaction type : Set Auto Stake Maturity", + `Neuron ID : ${neuronId}`, + `Auto stake : ${autoStake}`, + ]; + + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Auto Stake Maturity", + candid_request: rawRequestBody, + output: outputs.map((data, index) => `${index + 1} | ${data}`), + }; +}; + +const main = () => { + try { + const vectors = [ + createCandidAutoStakeMaturity({ + neuronId: mockNeuronId, + autoStake: true, + }), + createCandidAutoStakeMaturity({ + neuronId: mockNeuronId2, + autoStake: true, + }), + createCandidAutoStakeMaturity({ + neuronId: mockNeuronId2, + autoStake: false, + }), + createCandidAutoStakeMaturity({ + neuronId: mockNeuronId, + autoStake: false, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "auto-stake-maturity.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20221005-stake-maturity.ts b/scripts/test-vectors/20221005-stake-maturity.ts new file mode 100644 index 000000000..943e6ebe9 --- /dev/null +++ b/scripts/test-vectors/20221005-stake-maturity.ts @@ -0,0 +1,77 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toStakeMaturityRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createCandidStakeMaturity = ({ + neuronId, + percentageToStake, +}: { + neuronId: NeuronId; + percentageToStake?: number; +}) => { + const rawRequestBody = toStakeMaturityRequest({ + neuronId, + percentageToStake, + }); + + const outputs = [ + "Transaction type : Stake Maturity Neuron", + `Neuron ID : ${neuronId}`, + `Percentage to stake : ${percentageToStake}`, + ]; + + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Stake Maturity", + candid_request: rawRequestBody, + output: outputs.map((data, index) => `${index + 1} | ${data}`), + }; +}; + +const main = () => { + try { + const vectors = [ + createCandidStakeMaturity({ + neuronId: mockNeuronId, + }), + createCandidStakeMaturity({ + neuronId: mockNeuronId2, + }), + createCandidStakeMaturity({ + neuronId: mockNeuronId2, + percentageToStake: 50, + }), + createCandidStakeMaturity({ + neuronId: mockNeuronId, + percentageToStake: 50, + }), + createCandidStakeMaturity({ + neuronId: mockNeuronId, + percentageToStake: 15, + }), + createCandidStakeMaturity({ + neuronId: mockNeuronId2, + percentageToStake: 30, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "stake-maturity.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20221202-list-neurons.ts b/scripts/test-vectors/20221202-list-neurons.ts new file mode 100644 index 000000000..f0053b7b9 --- /dev/null +++ b/scripts/test-vectors/20221202-list-neurons.ts @@ -0,0 +1,49 @@ +// Date represents the date of the issue with the test vectors sent to Zondax: +// https://github.com/Zondax/ledger-icp/issues/166 +// We'll keep each script for reproducibility. +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { fromListNeurons } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ListNeuronsFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createListNeurons = (neuronIds?: NeuronId[]) => { + const rawRequestBody = fromListNeurons(neuronIds); + + const test = { + blob_candid: createBlob({ + arg: IDL.encode(ListNeuronsFn.argTypes, [rawRequestBody]), + methodName: "list_neurons", + }), + name: "List Neurons", + candid_request: rawRequestBody, + output: ["0 | Transaction type : List Own Neurons"], + }; + neuronIds?.forEach((id, idx) => { + test.output.push(`${idx + 1} | Neuron ID ${idx + 1} : ${id}`); + }); + return test; +}; + +const main = () => { + try { + const vectors = [ + createListNeurons(), + createListNeurons([mockNeuronId, mockNeuronId2]), + ]; + + writeToJson({ + data: vectors, + fileName: "list-neurons.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20221206-increase-dd.ts b/scripts/test-vectors/20221206-increase-dd.ts new file mode 100644 index 000000000..449e41b27 --- /dev/null +++ b/scripts/test-vectors/20221206-increase-dd.ts @@ -0,0 +1,51 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toIncreaseDissolveDelayRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createIncreaseDissolveDelayVector = ( + neuronId: NeuronId, + additionalDissolveDelaySeconds: number, +) => { + const params = { + neuronId, + additionalDissolveDelaySeconds, + }; + const rawRequestBody = toIncreaseDissolveDelayRequest(params); + + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Increase Dissolve Delay", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createIncreaseDissolveDelayVector(mockNeuronId, 3600), + createIncreaseDissolveDelayVector(mockNeuronId, 3600 * 24), + createIncreaseDissolveDelayVector(mockNeuronId2, 3600 * 24 * 7), + createIncreaseDissolveDelayVector(mockNeuronId2, 3600 * 24 * 7 * 4), + createIncreaseDissolveDelayVector(mockNeuronId2, 3600), + ]; + + writeToJson({ + data: vectors, + fileName: "increase-dissolve-delay.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20221206-start-dissolving.ts b/scripts/test-vectors/20221206-start-dissolving.ts new file mode 100644 index 000000000..2fd3358e2 --- /dev/null +++ b/scripts/test-vectors/20221206-start-dissolving.ts @@ -0,0 +1,40 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toStartDissolvingRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createStartDissolvingVector = (neuronId: NeuronId) => { + const rawRequestBody = toStartDissolvingRequest(neuronId); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Start Dissolving", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createStartDissolvingVector(mockNeuronId), + createStartDissolvingVector(mockNeuronId2), + ]; + + writeToJson({ + data: vectors, + fileName: "start-dissolving.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20221206-stop-dissolving.ts b/scripts/test-vectors/20221206-stop-dissolving.ts new file mode 100644 index 000000000..14769a07a --- /dev/null +++ b/scripts/test-vectors/20221206-stop-dissolving.ts @@ -0,0 +1,40 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toStopDissolvingRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createStopDissolvingVector = (neuronId: NeuronId) => { + const rawRequestBody = toStopDissolvingRequest(neuronId); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Stop Dissolving", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createStopDissolvingVector(mockNeuronId), + createStopDissolvingVector(mockNeuronId2), + ]; + + writeToJson({ + data: vectors, + fileName: "stop-dissolving.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230110-sns-add-hotkey.ts b/scripts/test-vectors/20230110-sns-add-hotkey.ts new file mode 100644 index 000000000..67b305064 --- /dev/null +++ b/scripts/test-vectors/20230110-sns-add-hotkey.ts @@ -0,0 +1,166 @@ +import { IDL } from "@dfinity/candid"; +import { Principal } from "@dfinity/principal"; +import { SnsNeuronPermissionsParams } from "@dfinity/sns/src"; +import { toAddPermissionsRequest } from "@dfinity/sns/src/converters/governance.converters"; +import { SnsNeuronPermissionType } from "@dfinity/sns/src/enums/governance.enums"; +import { arrayOfNumberToUint8Array } from "@dfinity/utils"; +import { ManageNeuronFn } from "./sns-governance.idl"; +import { + bytesToHexString, + createBlob, + permissionMapper, + splitPrincipal, + writeToJson, +} from "./utils"; + +/** + * ISSUE: https://github.com/Zondax/ledger-icp/issues/187 + */ + +interface Params extends SnsNeuronPermissionsParams { + canisterId: Principal; +} + +const createTestVector = (params: Params) => { + const rawRequestBody = toAddPermissionsRequest(params); + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + const neuronIdString = bytesToHexString(Array.from(params.neuronId.id)); + const neuronIdOutputs = + neuronIdString + .match(/.{1,6}/g) + ?.reduce((acc, curr) => { + if (acc.length === 0) { + return [curr]; + } + const lastItem = acc[acc.length - 1]; + if (lastItem.length > 18) { + return [...acc, curr]; + } else if (lastItem.length < 18) { + return [...acc.slice(0, -1), `${lastItem} : ${curr}`]; + } + return acc; + }, [] as string[]) + ?.map( + (data, i, elements) => + `Neuron Id [${i + 1}/${elements.length}] : ${data}`, + ) || []; + const principalOutputs = splitPrincipal(params.principal).map( + (data, i, elements) => + `Principal Id [${i + 1}/${elements.length}] : ${data}`, + ); + const permissionOutputs = params.permissions.map( + (p) => `Add Permission : ${permissionMapper[p]}`, + ); + const output = [ + "Transaction type : Add Neuron Permissions", + ...canisterIdOutputs, + ...neuronIdOutputs, + ...principalOutputs, + ...permissionOutputs, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + canisterId: params.canisterId, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + neuronIdString: neuronIdString, + name: "Add Neuron Permissions", + canisterId: params.canisterId.toText(), + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const id1 = arrayOfNumberToUint8Array([ + 215, 232, 81, 101, 44, 208, 50, 186, 94, 215, 107, 23, 246, 38, 170, 71, + 130, 159, 6, 229, 35, 90, 13, 88, 14, 150, 211, 114, 119, 41, 234, 36, + ]); + const id2 = arrayOfNumberToUint8Array([ + 163, 110, 20, 60, 134, 231, 228, 113, 231, 199, 25, 80, 100, 135, 207, + 122, 108, 244, 13, 167, 99, 97, 174, 175, 238, 81, 51, 225, 217, 172, 234, + 61, + ]); + const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", + ); + const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", + ); + const canisterId1 = Principal.fromText("ppmzm-3aaaa-aaaaa-aacpq-cai"); + const canisterId2 = Principal.fromText("s24we-diaaa-aaaaa-aaaka-cai"); + const vectors = [ + createTestVector({ + neuronId: { id: id1 }, + principal: principal1, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_SUBMIT_PROPOSAL, + ], + canisterId: canisterId1, + }), + createTestVector({ + neuronId: { id: id1 }, + principal: principal2, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_SUBMIT_PROPOSAL, + ], + canisterId: canisterId1, + }), + createTestVector({ + neuronId: { id: id2 }, + principal: principal2, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_SUBMIT_PROPOSAL, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_CONFIGURE_DISSOLVE_STATE, + ], + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id1 }, + principal: principal1, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_DISBURSE, + ], + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id1 }, + principal: principal2, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_DISBURSE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_CONFIGURE_DISSOLVE_STATE, + ], + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id2 }, + principal: principal2, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_MANAGE_VOTING_PERMISSION, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_DISBURSE, + ], + canisterId: canisterId1, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "sns-add-neuron-permissions.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230118-sns-manage-neuron.ts b/scripts/test-vectors/20230118-sns-manage-neuron.ts new file mode 100644 index 000000000..14936d11c --- /dev/null +++ b/scripts/test-vectors/20230118-sns-manage-neuron.ts @@ -0,0 +1,227 @@ +import { IDL } from "@dfinity/candid"; +import { encodeIcrcAccount } from "@dfinity/ledger/src/utils/ledger.utils"; +import { E8S_PER_TOKEN } from "@dfinity/nns/src/constants/constants"; +import { Principal } from "@dfinity/principal"; +import { SnsDisburseNeuronParams, SnsNeuronId } from "@dfinity/sns/src"; +import { + toDisburseNeuronRequest, + toStartDissolvingNeuronRequest, + toStopDissolvingNeuronRequest, +} from "@dfinity/sns/src/converters/governance.converters"; +import { arrayOfNumberToUint8Array } from "@dfinity/utils"; +import { ManageNeuronFn } from "./sns-governance.idl"; +import { + bytesToHexString, + createBlob, + defaultCaller, + splitPrincipal, + splitString, + writeToJson, +} from "./utils"; + +/** + * Issue: https://github.com/Zondax/ledger-icp/issues/189 + */ + +interface DisburseParams extends SnsDisburseNeuronParams { + canisterId: Principal; +} + +const createDisburseVector = (params: DisburseParams) => { + const rawRequestBody = toDisburseNeuronRequest(params); + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + const neuronIdString = bytesToHexString(Array.from(params.neuronId.id)); + const neuronIdOutputs = splitString(neuronIdString, "Neuron Id"); + const disburseToAccountStr = encodeIcrcAccount( + params.toAccount ?? { + owner: defaultCaller, + }, + ); + const disburseToOutputs = splitString(disburseToAccountStr, "Disburse to"); + const amount = params.amount + ? Number(params.amount) / Number(E8S_PER_TOKEN) + : "All"; + const amountOutputs = [`Amount : ${amount}`]; + const output = [ + "Transaction type : Disburse Neuron", + ...canisterIdOutputs, + ...neuronIdOutputs, + ...disburseToOutputs, + ...amountOutputs, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + canisterId: params.canisterId, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + neuronIdString: neuronIdString, + name: "Disburse Neuron", + canisterId: params.canisterId.toText(), + candid_request: rawRequestBody, + }; +}; + +interface SetDissolveParams { + canisterId: Principal; + neuronId: SnsNeuronId; +} + +const createStopDissolveVector = (params: SetDissolveParams) => { + const rawRequestBody = toStopDissolvingNeuronRequest(params.neuronId); + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + const neuronIdString = bytesToHexString(Array.from(params.neuronId.id)); + const neuronIdOutputs = splitString(neuronIdString, "Neuron Id"); + const output = [ + "Transaction type : Stop Dissolve Neuron", + ...canisterIdOutputs, + ...neuronIdOutputs, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + canisterId: params.canisterId, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + neuronIdString: neuronIdString, + name: "Stop Dissolve Neuron", + canisterId: params.canisterId.toText(), + candid_request: rawRequestBody, + }; +}; + +const createStartDissolveVector = (params: SetDissolveParams) => { + const rawRequestBody = toStartDissolvingNeuronRequest(params.neuronId); + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + const neuronIdString = bytesToHexString(Array.from(params.neuronId.id)); + const neuronIdOutputs = splitString(neuronIdString, "Neuron Id"); + const output = [ + "Transaction type : Start Dissolve Neuron", + ...canisterIdOutputs, + ...neuronIdOutputs, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + canisterId: params.canisterId, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + neuronIdString: neuronIdString, + name: "Start Dissolve Neuron", + canisterId: params.canisterId.toText(), + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const id1 = arrayOfNumberToUint8Array([ + 215, 232, 81, 101, 44, 208, 50, 186, 94, 215, 107, 23, 246, 38, 170, 71, + 130, 159, 6, 229, 35, 90, 13, 88, 14, 150, 211, 114, 119, 41, 234, 36, + ]); + const id2 = arrayOfNumberToUint8Array([ + 163, 110, 20, 60, 134, 231, 228, 113, 231, 199, 25, 80, 100, 135, 207, + 122, 108, 244, 13, 167, 99, 97, 174, 175, 238, 81, 51, 225, 217, 172, 234, + 61, + ]); + const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", + ); + const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", + ); + const snsAccount1 = { + owner: principal1, + subaccount: id1, + }; + const snsAccount2 = { + owner: principal2, + subaccount: id2, + }; + const canisterId1 = Principal.fromText("ppmzm-3aaaa-aaaaa-aacpq-cai"); + const canisterId2 = Principal.fromText("s24we-diaaa-aaaaa-aaaka-cai"); + const vectors = [ + createDisburseVector({ + neuronId: { id: id1 }, + amount: BigInt(50_000_000), + canisterId: canisterId1, + }), + createDisburseVector({ + neuronId: { id: id1 }, + canisterId: canisterId1, + }), + createDisburseVector({ + neuronId: { id: id2 }, + amount: BigInt(200_000_000), + canisterId: canisterId2, + }), + createDisburseVector({ + neuronId: { id: id2 }, + amount: BigInt(200_000_000), + toAccount: snsAccount1, + canisterId: canisterId2, + }), + createDisburseVector({ + neuronId: { id: id1 }, + amount: BigInt(2870_000_000), + toAccount: snsAccount2, + canisterId: canisterId1, + }), + createStartDissolveVector({ + neuronId: { id: id1 }, + canisterId: canisterId1, + }), + createStartDissolveVector({ + neuronId: { id: id2 }, + canisterId: canisterId1, + }), + createStartDissolveVector({ + neuronId: { id: id1 }, + canisterId: canisterId2, + }), + createStartDissolveVector({ + neuronId: { id: id2 }, + canisterId: canisterId2, + }), + createStopDissolveVector({ + neuronId: { id: id1 }, + canisterId: canisterId1, + }), + createStopDissolveVector({ + neuronId: { id: id2 }, + canisterId: canisterId1, + }), + createStopDissolveVector({ + neuronId: { id: id1 }, + canisterId: canisterId2, + }), + createStopDissolveVector({ + neuronId: { id: id2 }, + canisterId: canisterId2, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "sns-manage-neuron-actions.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230118-sns-remove-hotkey.ts b/scripts/test-vectors/20230118-sns-remove-hotkey.ts new file mode 100644 index 000000000..e7550528b --- /dev/null +++ b/scripts/test-vectors/20230118-sns-remove-hotkey.ts @@ -0,0 +1,149 @@ +import { IDL } from "@dfinity/candid"; +import { Principal } from "@dfinity/principal"; +import { SnsNeuronPermissionsParams } from "@dfinity/sns/src"; +import { toRemovePermissionsRequest } from "@dfinity/sns/src/converters/governance.converters"; +import { SnsNeuronPermissionType } from "@dfinity/sns/src/enums/governance.enums"; +import { arrayOfNumberToUint8Array } from "@dfinity/utils"; +import { ManageNeuronFn } from "./sns-governance.idl"; +import { + bytesToHexString, + createBlob, + permissionMapper, + splitPrincipal, + splitString, + writeToJson, +} from "./utils"; + +/** + * Issue: https://github.com/Zondax/ledger-icp/issues/188 + */ + +interface Params extends SnsNeuronPermissionsParams { + canisterId: Principal; +} + +const createTestVector = (params: Params) => { + const rawRequestBody = toRemovePermissionsRequest(params); + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + const neuronIdString = bytesToHexString(Array.from(params.neuronId.id)); + const neuronIdOutputs = splitString(neuronIdString, "Neuron Id"); + const principalOutputs = splitPrincipal(params.principal).map( + (data, i, elements) => + `Principal Id [${i + 1}/${elements.length}] : ${data}`, + ); + const permissionOutputs = params.permissions.map( + (p) => `Remove Permission : ${permissionMapper[p]}`, + ); + const output = [ + "Transaction type : Remove Neuron Permissions", + ...canisterIdOutputs, + ...neuronIdOutputs, + ...principalOutputs, + ...permissionOutputs, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + canisterId: params.canisterId, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + neuronIdString: neuronIdString, + name: "Remove Neuron Permissions", + canisterId: params.canisterId.toText(), + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const id1 = arrayOfNumberToUint8Array([ + 215, 232, 81, 101, 44, 208, 50, 186, 94, 215, 107, 23, 246, 38, 170, 71, + 130, 159, 6, 229, 35, 90, 13, 88, 14, 150, 211, 114, 119, 41, 234, 36, + ]); + const id2 = arrayOfNumberToUint8Array([ + 163, 110, 20, 60, 134, 231, 228, 113, 231, 199, 25, 80, 100, 135, 207, + 122, 108, 244, 13, 167, 99, 97, 174, 175, 238, 81, 51, 225, 217, 172, 234, + 61, + ]); + const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", + ); + const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", + ); + const canisterId1 = Principal.fromText("ppmzm-3aaaa-aaaaa-aacpq-cai"); + const canisterId2 = Principal.fromText("s24we-diaaa-aaaaa-aaaka-cai"); + const vectors = [ + createTestVector({ + neuronId: { id: id1 }, + principal: principal1, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_SUBMIT_PROPOSAL, + ], + canisterId: canisterId1, + }), + createTestVector({ + neuronId: { id: id1 }, + principal: principal2, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_SUBMIT_PROPOSAL, + ], + canisterId: canisterId1, + }), + createTestVector({ + neuronId: { id: id2 }, + principal: principal2, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_SUBMIT_PROPOSAL, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_CONFIGURE_DISSOLVE_STATE, + ], + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id1 }, + principal: principal1, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_DISBURSE, + ], + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id1 }, + principal: principal2, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_DISBURSE, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_CONFIGURE_DISSOLVE_STATE, + ], + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id2 }, + principal: principal2, + permissions: [ + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_MANAGE_VOTING_PERMISSION, + SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_DISBURSE, + ], + canisterId: canisterId1, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "sns-remove-neuron-permissions.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230123-icrc-1.ts b/scripts/test-vectors/20230123-icrc-1.ts new file mode 100644 index 000000000..9a8afbb77 --- /dev/null +++ b/scripts/test-vectors/20230123-icrc-1.ts @@ -0,0 +1,297 @@ +import { IDL } from "@dfinity/candid"; +import { toTransferArg } from "@dfinity/ledger/src/converters/ledger.converters"; +import { TransferParams } from "@dfinity/ledger/src/types/ledger.params"; +import { E8S_PER_TOKEN } from "@dfinity/nns/src/constants/constants"; +import { Principal } from "@dfinity/principal"; +import { + arrayOfNumberToUint8Array, + fromNullable, + numberToUint8Array, + uint8ArrayToBigInt, +} from "@dfinity/utils"; +import { MAINNET_LEDGER_CANISTER_ID } from "../../packages/nns/src/constants/canister_ids"; +import { transferFn } from "./sns-ledger.idl"; +import { + createBlob, + defaultCaller, + splitAccount, + splitPrincipal, + writeToJson, +} from "./utils"; + +/** + * Issue: https://github.com/Zondax/ledger-icp/issues/190 + */ + +/** + * Specific business logic. + * + * - When is ICP and when not. + * - Convert memo to bigint. + * - When to show fee and when not. + * - Default fee for ICP. + * - Textual representation of accounts. + * - How to create the "source" account. + * - When to show "ICP" and when "Tokens". + */ + +interface Params extends TransferParams { + canisterId: Principal; + owner: Principal; +} + +// Fee is optional, if not provided, it will be set to 10000 which is the ICP fee +const ICP_DEFAULT_FEE_E8S = 10_000; + +const createTestVector = (params: Params) => { + const rawRequestBody = toTransferArg(params); + const isICP = + params.canisterId.toText() === MAINNET_LEDGER_CANISTER_ID.toText(); + + let outputTxType = isICP ? "Send ICP" : "Send Tokens"; + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + + const fromOutputs = splitAccount( + { + owner: params.owner, + subaccount: params.from_subaccount, + }, + "From account", + ); + const toOutputs = splitAccount( + { + owner: params.to.owner, + subaccount: fromNullable(params.to.subaccount), + }, + "To account", + ); + + const amountToken = Number(params.amount) / Number(E8S_PER_TOKEN); + const paymentOutput = isICP + ? `Payment (ICP) : ${amountToken}` + : `Payment (Tokens) : ${amountToken}`; + + // Do not show fee if it's not present in the request body. + // Except if it's ICP, in which case we always show the fee. + let feeOutput: string | undefined; + if (isICP || params.fee !== undefined) { + const feeToken = + Number(params.fee ?? ICP_DEFAULT_FEE_E8S) / Number(E8S_PER_TOKEN); + feeOutput = isICP + ? `Maximum fee (ICP) : ${feeToken}` + : `Maximum fee (Tokens) : ${feeToken}`; + } + + const memoOutput = + params.memo !== undefined + ? `Memo : ${uint8ArrayToBigInt(params.memo).toString()}` + : "Memo : 0"; + + const output = [ + outputTxType, + ...(isICP ? [] : canisterIdOutputs), + ...fromOutputs, + ...toOutputs, + paymentOutput, + ...(feeOutput !== undefined ? [feeOutput] : []), + memoOutput, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(transferFn.argTypes, [rawRequestBody]), + methodName: "icrc1_transfer", + canisterId: params.canisterId, + caller: params.owner, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + isICP, + name: "ICRC1 Transfer", + canisterId: params.canisterId.toText(), + caller: params.owner.toText() ?? defaultCaller.toText(), + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const subaccount1 = arrayOfNumberToUint8Array([ + 215, 232, 81, 101, 44, 208, 50, 186, 94, 215, 107, 23, 246, 38, 170, 71, + 130, 159, 6, 229, 35, 90, 13, 88, 14, 150, 211, 114, 119, 41, 234, 36, + ]); + const subaccount2 = arrayOfNumberToUint8Array([ + 163, 110, 20, 60, 134, 231, 228, 113, 231, 199, 25, 80, 100, 135, 207, + 122, 108, 244, 13, 167, 99, 97, 174, 175, 238, 81, 51, 225, 217, 172, 234, + 61, + ]); + const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", + ); + const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", + ); + const canisterId1 = Principal.fromText("ppmzm-3aaaa-aaaaa-aacpq-cai"); + const canisterId2 = Principal.fromText("s24we-diaaa-aaaaa-aaaka-cai"); + const vectors = [ + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + canisterId: canisterId1, + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + from_subaccount: subaccount1, + fee: BigInt(10_000), + canisterId: canisterId1, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [subaccount2], + }, + amount: BigInt(330_000_000), + from_subaccount: subaccount1, + fee: BigInt(10_000), + canisterId: canisterId1, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [subaccount2], + }, + amount: BigInt(330_000_000), + memo: numberToUint8Array(11223312), + fee: BigInt(30_000), + canisterId: canisterId2, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [], + }, + amount: BigInt(314_000_000), + memo: numberToUint8Array(11223312), + canisterId: canisterId1, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(1331_400_000), + memo: numberToUint8Array(11223312), + from_subaccount: subaccount2, + fee: BigInt(20_000), + canisterId: canisterId1, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + from_subaccount: subaccount2, + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + fee: BigInt(100_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + from_subaccount: subaccount2, + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + from_subaccount: subaccount2, + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + ]; + + writeToJson({ + data: vectors, + fileName: "icrc-1.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230125-sns-stake-maturity.ts b/scripts/test-vectors/20230125-sns-stake-maturity.ts new file mode 100644 index 000000000..525b4770e --- /dev/null +++ b/scripts/test-vectors/20230125-sns-stake-maturity.ts @@ -0,0 +1,104 @@ +import { IDL } from "@dfinity/candid"; +import { Principal } from "@dfinity/principal"; +import { SnsNeuronStakeMaturityParams } from "@dfinity/sns/src"; +import { toStakeMaturityRequest } from "@dfinity/sns/src/converters/governance.converters"; +import { arrayOfNumberToUint8Array } from "@dfinity/utils"; +import { ManageNeuronFn } from "./sns-governance.idl"; +import { + bytesToHexString, + createBlob, + splitPrincipal, + splitString, + writeToJson, +} from "./utils"; + +/** + * Issue: https://github.com/Zondax/ledger-icp/issues/192 + */ + +interface StakeMaturityParams extends SnsNeuronStakeMaturityParams { + canisterId: Principal; +} + +const createStakeMaturityVector = (params: StakeMaturityParams) => { + const rawRequestBody = toStakeMaturityRequest(params); + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + const neuronIdString = bytesToHexString(Array.from(params.neuronId.id)); + const neuronIdOutputs = splitString(neuronIdString, "Neuron Id"); + const percentageOutput = `Percentage to stake : ${ + params.percentageToStake ?? 100 + }`; + const output = [ + "Transaction type : Stake Maturity Neuron", + ...canisterIdOutputs, + ...neuronIdOutputs, + percentageOutput, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + canisterId: params.canisterId, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + neuronIdString: neuronIdString, + name: "Stake Maturity Neuron", + canisterId: params.canisterId.toText(), + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const id1 = arrayOfNumberToUint8Array([ + 215, 232, 81, 101, 44, 208, 50, 186, 94, 215, 107, 23, 246, 38, 170, 71, + 130, 159, 6, 229, 35, 90, 13, 88, 14, 150, 211, 114, 119, 41, 234, 36, + ]); + const id2 = arrayOfNumberToUint8Array([ + 163, 110, 20, 60, 134, 231, 228, 113, 231, 199, 25, 80, 100, 135, 207, + 122, 108, 244, 13, 167, 99, 97, 174, 175, 238, 81, 51, 225, 217, 172, 234, + 61, + ]); + const canisterId1 = Principal.fromText("ppmzm-3aaaa-aaaaa-aacpq-cai"); + const canisterId2 = Principal.fromText("s24we-diaaa-aaaaa-aaaka-cai"); + const vectors = [ + createStakeMaturityVector({ + neuronId: { id: id1 }, + canisterId: canisterId1, + }), + createStakeMaturityVector({ + neuronId: { id: id2 }, + canisterId: canisterId1, + }), + createStakeMaturityVector({ + neuronId: { id: id1 }, + canisterId: canisterId2, + percentageToStake: 50, + }), + createStakeMaturityVector({ + neuronId: { id: id2 }, + canisterId: canisterId2, + percentageToStake: 100, + }), + createStakeMaturityVector({ + neuronId: { id: id1 }, + canisterId: canisterId1, + percentageToStake: 33, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "sns-stake-maturity.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230321-disburse.ts b/scripts/test-vectors/20230321-disburse.ts new file mode 100644 index 000000000..d3978775d --- /dev/null +++ b/scripts/test-vectors/20230321-disburse.ts @@ -0,0 +1,90 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { AccountIdentifier } from "@dfinity/nns/src/account_identifier"; +import { toDisburseNeuronRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); +const amount1 = BigInt(20_000_000); +const amount2 = BigInt(25_000_000); +const account1 = AccountIdentifier.fromHex( + "d4685b31b51450508aff0331584df7692a84467b680326f5c5f7d30ae711682f", +); +const account2 = AccountIdentifier.fromHex( + "b1cebc8480a0afc91198a87ddf52c6ca7eb7ccddb0cb398064f71d2bbaf2f72b", +); + +const createDisburseVector = ({ + neuronId, + toAccountIdentifier, + amount, +}: { + neuronId: NeuronId; + toAccountIdentifier?: AccountIdentifier; + amount?: bigint; +}) => { + const rawRequestBody = toDisburseNeuronRequest({ + neuronId, + toAccountIdentifier, + amount, + }); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Join Community Fund", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createDisburseVector({ neuronId: mockNeuronId }), + createDisburseVector({ neuronId: mockNeuronId2 }), + createDisburseVector({ neuronId: mockNeuronId2, amount: amount1 }), + createDisburseVector({ + neuronId: mockNeuronId2, + amount: amount1, + toAccountIdentifier: account1, + }), + createDisburseVector({ + neuronId: mockNeuronId, + toAccountIdentifier: account1, + }), + createDisburseVector({ + neuronId: mockNeuronId, + amount: amount2, + toAccountIdentifier: account1, + }), + createDisburseVector({ + neuronId: mockNeuronId2, + amount: amount1, + toAccountIdentifier: account2, + }), + createDisburseVector({ + neuronId: mockNeuronId, + toAccountIdentifier: account2, + }), + createDisburseVector({ + neuronId: mockNeuronId, + amount: amount2, + toAccountIdentifier: account2, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "disburse.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230321-join-cf.ts b/scripts/test-vectors/20230321-join-cf.ts new file mode 100644 index 000000000..2addc3333 --- /dev/null +++ b/scripts/test-vectors/20230321-join-cf.ts @@ -0,0 +1,40 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toJoinCommunityFundRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createJoinCFVector = (neuronId: NeuronId) => { + const rawRequestBody = toJoinCommunityFundRequest(neuronId); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Join Community Fund", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createJoinCFVector(mockNeuronId), + createJoinCFVector(mockNeuronId2), + ]; + + writeToJson({ + data: vectors, + fileName: "join-cf.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230511-add-hotkey.ts b/scripts/test-vectors/20230511-add-hotkey.ts new file mode 100644 index 000000000..7b0a5f82a --- /dev/null +++ b/scripts/test-vectors/20230511-add-hotkey.ts @@ -0,0 +1,70 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toAddHotkeyRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { Principal } from "@dfinity/principal"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); +const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", +); +const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", +); + +const createAddHotkeyVector = ({ + neuronId, + principal, +}: { + neuronId: NeuronId; + principal: Principal; +}) => { + const rawRequestBody = toAddHotkeyRequest({ + neuronId, + principal, + }); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Add Hotkey", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createAddHotkeyVector({ + neuronId: mockNeuronId, + principal: principal1, + }), + createAddHotkeyVector({ + neuronId: mockNeuronId, + principal: principal2, + }), + createAddHotkeyVector({ + neuronId: mockNeuronId2, + principal: principal1, + }), + createAddHotkeyVector({ + neuronId: mockNeuronId2, + principal: principal2, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "add-hotkey.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230511-follow.ts b/scripts/test-vectors/20230511-follow.ts new file mode 100644 index 000000000..f919fe567 --- /dev/null +++ b/scripts/test-vectors/20230511-follow.ts @@ -0,0 +1,92 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toManageNeuronsFollowRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { Topic } from "@dfinity/nns/src/enums/governance.enums"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); +const mockNeuronId3 = BigInt(4053576662908); +const mockNeuronId4 = BigInt(999888123); +const mockNeuronId5 = BigInt(3141242222); +const topic1 = Topic.ExchangeRate; +const topic2 = Topic.Governance; +const topic3 = Topic.SnsAndCommunityFund; + +const createFollowVector = ({ + neuronId, + topic, + followees, +}: { + neuronId: NeuronId; + topic: Topic; + followees: Array; +}) => { + const rawRequestBody = toManageNeuronsFollowRequest({ + neuronId, + topic, + followees, + }); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Follow", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createFollowVector({ + neuronId: mockNeuronId, + topic: topic1, + followees: [mockNeuronId2, mockNeuronId3], + }), + createFollowVector({ + neuronId: mockNeuronId2, + topic: topic2, + followees: [mockNeuronId3, mockNeuronId4], + }), + createFollowVector({ + neuronId: mockNeuronId3, + topic: topic3, + followees: [mockNeuronId4, mockNeuronId5], + }), + createFollowVector({ + neuronId: mockNeuronId3, + topic: topic3, + followees: [mockNeuronId4, mockNeuronId5], + }), + createFollowVector({ + neuronId: mockNeuronId4, + topic: topic1, + followees: [mockNeuronId, mockNeuronId2, mockNeuronId4, mockNeuronId5], + }), + createFollowVector({ + neuronId: mockNeuronId4, + topic: topic2, + followees: [mockNeuronId5], + }), + createFollowVector({ + neuronId: mockNeuronId4, + topic: topic3, + followees: [mockNeuronId3, mockNeuronId5, mockNeuronId2], + }), + ]; + + writeToJson({ + data: vectors, + fileName: "follow.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230511-register-vote.ts b/scripts/test-vectors/20230511-register-vote.ts new file mode 100644 index 000000000..ae53dd001 --- /dev/null +++ b/scripts/test-vectors/20230511-register-vote.ts @@ -0,0 +1,86 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId, ProposalId } from "@dfinity/nns/src"; +import { toRegisterVoteRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { Vote } from "@dfinity/nns/src/enums/governance.enums"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); +const proposalId1 = BigInt(1223); +const proposalId2 = BigInt(442233); +const proposalId3 = BigInt(233); +const vote1 = Vote.Yes; +const vote2 = Vote.No; + +const createRegisterVoteVector = ({ + neuronId, + proposalId, + vote, +}: { + neuronId: NeuronId; + proposalId: ProposalId; + vote: Vote; +}) => { + const rawRequestBody = toRegisterVoteRequest({ + neuronId, + proposalId, + vote, + }); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Register Vote", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createRegisterVoteVector({ + neuronId: mockNeuronId, + vote: vote1, + proposalId: proposalId1, + }), + createRegisterVoteVector({ + neuronId: mockNeuronId2, + vote: vote1, + proposalId: proposalId1, + }), + createRegisterVoteVector({ + neuronId: mockNeuronId, + vote: vote2, + proposalId: proposalId3, + }), + createRegisterVoteVector({ + neuronId: mockNeuronId, + vote: vote1, + proposalId: proposalId2, + }), + createRegisterVoteVector({ + neuronId: mockNeuronId2, + vote: vote2, + proposalId: proposalId1, + }), + createRegisterVoteVector({ + neuronId: mockNeuronId2, + vote: vote1, + proposalId: proposalId3, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "register-vote.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230511-remove-hotkey.ts b/scripts/test-vectors/20230511-remove-hotkey.ts new file mode 100644 index 000000000..aa6885d2c --- /dev/null +++ b/scripts/test-vectors/20230511-remove-hotkey.ts @@ -0,0 +1,70 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toRemoveHotkeyRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { Principal } from "@dfinity/principal"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); +const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", +); +const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", +); + +const createRemoveHotkeyVector = ({ + neuronId, + principal, +}: { + neuronId: NeuronId; + principal: Principal; +}) => { + const rawRequestBody = toRemoveHotkeyRequest({ + neuronId, + principal, + }); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Remove Hotkey", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createRemoveHotkeyVector({ + neuronId: mockNeuronId, + principal: principal1, + }), + createRemoveHotkeyVector({ + neuronId: mockNeuronId, + principal: principal2, + }), + createRemoveHotkeyVector({ + neuronId: mockNeuronId2, + principal: principal1, + }), + createRemoveHotkeyVector({ + neuronId: mockNeuronId2, + principal: principal2, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "remove-hotkey.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230516-merge-maturity.ts b/scripts/test-vectors/20230516-merge-maturity.ts new file mode 100644 index 000000000..b64bc51a3 --- /dev/null +++ b/scripts/test-vectors/20230516-merge-maturity.ts @@ -0,0 +1,63 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toMergeMaturityRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createMergeMaturityVector = ({ + neuronId, + percentageToMerge, +}: { + neuronId: NeuronId; + percentageToMerge: number; +}) => { + const rawRequestBody = toMergeMaturityRequest({ + neuronId, + percentageToMerge, + }); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Merge Maturity", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createMergeMaturityVector({ + neuronId: mockNeuronId, + percentageToMerge: 100, + }), + createMergeMaturityVector({ + neuronId: mockNeuronId, + percentageToMerge: 10, + }), + createMergeMaturityVector({ + neuronId: mockNeuronId2, + percentageToMerge: 50, + }), + createMergeMaturityVector({ + neuronId: mockNeuronId2, + percentageToMerge: 84, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "merge-maturity.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230516-send-icp-stake-neuron.ts b/scripts/test-vectors/20230516-send-icp-stake-neuron.ts new file mode 100644 index 000000000..f92a1635e --- /dev/null +++ b/scripts/test-vectors/20230516-send-icp-stake-neuron.ts @@ -0,0 +1,221 @@ +import { IDL } from "@dfinity/candid"; +import { + AccountIdentifier, + SubAccount, +} from "@dfinity/nns/src/account_identifier"; +import { toTransferRawRequest } from "@dfinity/nns/src/canisters/ledger/ledger.request.converts"; +import { + MAINNET_GOVERNANCE_CANISTER_ID, + MAINNET_LEDGER_CANISTER_ID, +} from "@dfinity/nns/src/constants/canister_ids"; +import { Principal } from "@dfinity/principal"; +import { asciiStringToByteArray, uint8ArrayToBigInt } from "@dfinity/utils"; +import { arrayOfNumberToUint8Array } from "@dfinity/utils/src"; +import { sha256 } from "js-sha256"; +import { TransferFn } from "./ledger.idl"; +import { createBlob, writeToJson } from "./utils"; + +const account1 = AccountIdentifier.fromHex( + "d3e13d4777e22367532053190b6c6ccf57444a61337e996242b1abfb52cf92c8", +); +const account2 = AccountIdentifier.fromPrincipal({ + principal: Principal.fromText( + "bwz3t-ercuj-owo6s-4adfr-sbu4o-l72hg-kfhc5-5sapm-tj6bn-3scho-uqe", + ), +}); + +const defaultCaller = Principal.fromText( + "5upke-tazvi-6ufqc-i3v6r-j4gpu-dpwti-obhal-yb5xj-ue32x-ktkql-rqe", +); +const caller1 = Principal.fromText( + "bwz3t-ercuj-owo6s-4adfr-sbu4o-l72hg-kfhc5-5sapm-tj6bn-3scho-uqe", +); + +const subaccount1 = [ + 10, 0, 0, 0, 0, 0, 48, 0, 75, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, +]; +const subaccount2 = [ + 29, 2, 220, 105, 83, 29, 110, 131, 117, 207, 8, 232, 14, 110, 205, 215, 59, + 147, 176, 255, 96, 204, 41, 123, 138, 63, 234, 83, 28, 2, 0, 0, +]; +const createdAt1 = + BigInt(new Date("05-20-1992 21:33:00").getTime()) * BigInt(1e6); +const createdAt2 = + BigInt(new Date("05-28-2011 22:33:00").getTime()) * BigInt(1e6); + +const randomBytes1 = new Uint8Array([136, 67, 218, 72, 46, 254, 79, 124]); +const randomBytes2 = new Uint8Array([202, 204, 126, 241, 130, 112, 8, 122]); +const randomBytes3 = new Uint8Array([178, 247, 215, 62, 199, 137, 175, 189]); + +const buildNeuronStakeSubAccount = ( + nonce: Uint8Array, + principal: Principal, +): SubAccount => { + const padding = asciiStringToByteArray("neuron-stake"); + const shaObj = sha256.create(); + shaObj.update([0x0c, ...padding, ...principal.toUint8Array(), ...nonce]); + return SubAccount.fromBytes(new Uint8Array(shaObj.array())) as SubAccount; +}; + +const createSendIcpVector = ({ + to, + amount, + memo, + fee, + fromSubAccount, + createdAt, + caller = defaultCaller, + randomBytes, +}: { + to?: AccountIdentifier; + amount: bigint; + memo?: bigint; + fee: bigint; + fromSubAccount?: number[]; + createdAt?: bigint; + caller: Principal; + randomBytes?: Uint8Array; +}) => { + const isStakeNeuron = to === undefined; + let accountIdentifier; + let nonce; + if (randomBytes !== undefined) { + nonce = uint8ArrayToBigInt(randomBytes); + const toSubAccount = buildNeuronStakeSubAccount(randomBytes, caller); + accountIdentifier = AccountIdentifier.fromPrincipal({ + principal: MAINNET_GOVERNANCE_CANISTER_ID, + subAccount: toSubAccount, + }); + } + const toAccount = to ?? accountIdentifier; + + const rawRequestBody = toTransferRawRequest({ + to: toAccount as AccountIdentifier, + amount, + // Used in HW + memo: memo ?? nonce ?? BigInt(0), + fee, + fromSubAccount, + createdAt, + }); + + const subAccount = + fromSubAccount === undefined + ? undefined + : (SubAccount.fromBytes( + arrayOfNumberToUint8Array(fromSubAccount), + ) as SubAccount); + + return { + blob_candid: createBlob({ + arg: IDL.encode(TransferFn.argTypes, [rawRequestBody]), + methodName: "transfer", + canisterId: MAINNET_LEDGER_CANISTER_ID, + }), + name: isStakeNeuron ? "Stake Neuron" : "Send ICP", + screen: { + fromAccount: AccountIdentifier.fromPrincipal({ + principal: caller, + subAccount, + }).toHex(), + toAccount: isStakeNeuron ? "Stake Neuron" : to?.toHex(), + }, + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createSendIcpVector({ + to: account1, + caller: defaultCaller, + amount: BigInt(1_000_000_000), + fee: BigInt(10_000), + memo: BigInt(0), + }), + createSendIcpVector({ + to: account2, + caller: defaultCaller, + amount: BigInt(333_000_000_000), + fee: BigInt(10_000), + memo: BigInt(0), + fromSubAccount: subaccount1, + }), + createSendIcpVector({ + to: account2, + caller: defaultCaller, + amount: BigInt(1_432_222_000), + fee: BigInt(10_000), + memo: BigInt(0), + createdAt: createdAt1, + }), + createSendIcpVector({ + to: account1, + amount: BigInt(2_000_000_000), + fee: BigInt(10_000), + memo: BigInt(128371233), + createdAt: createdAt2, + caller: caller1, + }), + createSendIcpVector({ + to: account1, + amount: BigInt(1_222_000_345), + fee: BigInt(10_000), + memo: BigInt(0), + fromSubAccount: subaccount2, + caller: caller1, + }), + createSendIcpVector({ + to: account1, + caller: defaultCaller, + amount: BigInt(1_000_000_000), + fee: BigInt(10_000), + memo: BigInt(12123242222), + fromSubAccount: subaccount1, + createdAt: createdAt1, + }), + createSendIcpVector({ + to: account2, + caller: defaultCaller, + amount: BigInt(100_000_000_000), + fee: BigInt(20_000), + memo: BigInt(9984628273), + }), + // Stake Neuron + createSendIcpVector({ + amount: BigInt(100_000_000_000), + fee: BigInt(10_000), + caller: defaultCaller, + fromSubAccount: subaccount1, + randomBytes: randomBytes1, + }), + createSendIcpVector({ + amount: BigInt(100_000_000_000), + fee: BigInt(10_000), + caller: defaultCaller, + fromSubAccount: subaccount2, + randomBytes: randomBytes2, + }), + createSendIcpVector({ + amount: BigInt(230_000_000_000), + fee: BigInt(10_000), + caller: defaultCaller, + createdAt: createdAt1, + randomBytes: randomBytes3, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "send-icp-stake-neuron.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230516-spawn-neuron.ts b/scripts/test-vectors/20230516-spawn-neuron.ts new file mode 100644 index 000000000..6733d0764 --- /dev/null +++ b/scripts/test-vectors/20230516-spawn-neuron.ts @@ -0,0 +1,89 @@ +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toSpawnNeuronRequest } from "@dfinity/nns/src/canisters/governance/request.converters"; +import { Principal } from "@dfinity/principal"; +import { ManageNeuronFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); +const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", +); +const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", +); + +const createSpawnNeuronVector = ({ + neuronId, + percentageToSpawn, + newController, + nonce, +}: { + neuronId: NeuronId; + percentageToSpawn?: number; + newController?: Principal; + nonce?: bigint; +}) => { + const rawRequestBody = toSpawnNeuronRequest({ + neuronId, + percentageToSpawn, + newController, + nonce, + }); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + }), + name: "Spawn Neuron", + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const vectors = [ + createSpawnNeuronVector({ + neuronId: mockNeuronId, + }), + createSpawnNeuronVector({ + neuronId: mockNeuronId, + percentageToSpawn: 100, + }), + createSpawnNeuronVector({ + neuronId: mockNeuronId, + percentageToSpawn: 10, + newController: principal1, + }), + createSpawnNeuronVector({ + neuronId: mockNeuronId, + percentageToSpawn: 33, + newController: principal2, + nonce: BigInt(1444), + }), + createSpawnNeuronVector({ + neuronId: mockNeuronId2, + percentageToSpawn: 50, + newController: principal2, + nonce: BigInt(1444), + }), + createSpawnNeuronVector({ + neuronId: mockNeuronId2, + percentageToSpawn: 84, + nonce: BigInt(1444), + }), + ]; + + writeToJson({ + data: vectors, + fileName: "spawn-neuron.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230524-icrc-1.ts b/scripts/test-vectors/20230524-icrc-1.ts new file mode 100644 index 000000000..9a8afbb77 --- /dev/null +++ b/scripts/test-vectors/20230524-icrc-1.ts @@ -0,0 +1,297 @@ +import { IDL } from "@dfinity/candid"; +import { toTransferArg } from "@dfinity/ledger/src/converters/ledger.converters"; +import { TransferParams } from "@dfinity/ledger/src/types/ledger.params"; +import { E8S_PER_TOKEN } from "@dfinity/nns/src/constants/constants"; +import { Principal } from "@dfinity/principal"; +import { + arrayOfNumberToUint8Array, + fromNullable, + numberToUint8Array, + uint8ArrayToBigInt, +} from "@dfinity/utils"; +import { MAINNET_LEDGER_CANISTER_ID } from "../../packages/nns/src/constants/canister_ids"; +import { transferFn } from "./sns-ledger.idl"; +import { + createBlob, + defaultCaller, + splitAccount, + splitPrincipal, + writeToJson, +} from "./utils"; + +/** + * Issue: https://github.com/Zondax/ledger-icp/issues/190 + */ + +/** + * Specific business logic. + * + * - When is ICP and when not. + * - Convert memo to bigint. + * - When to show fee and when not. + * - Default fee for ICP. + * - Textual representation of accounts. + * - How to create the "source" account. + * - When to show "ICP" and when "Tokens". + */ + +interface Params extends TransferParams { + canisterId: Principal; + owner: Principal; +} + +// Fee is optional, if not provided, it will be set to 10000 which is the ICP fee +const ICP_DEFAULT_FEE_E8S = 10_000; + +const createTestVector = (params: Params) => { + const rawRequestBody = toTransferArg(params); + const isICP = + params.canisterId.toText() === MAINNET_LEDGER_CANISTER_ID.toText(); + + let outputTxType = isICP ? "Send ICP" : "Send Tokens"; + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + + const fromOutputs = splitAccount( + { + owner: params.owner, + subaccount: params.from_subaccount, + }, + "From account", + ); + const toOutputs = splitAccount( + { + owner: params.to.owner, + subaccount: fromNullable(params.to.subaccount), + }, + "To account", + ); + + const amountToken = Number(params.amount) / Number(E8S_PER_TOKEN); + const paymentOutput = isICP + ? `Payment (ICP) : ${amountToken}` + : `Payment (Tokens) : ${amountToken}`; + + // Do not show fee if it's not present in the request body. + // Except if it's ICP, in which case we always show the fee. + let feeOutput: string | undefined; + if (isICP || params.fee !== undefined) { + const feeToken = + Number(params.fee ?? ICP_DEFAULT_FEE_E8S) / Number(E8S_PER_TOKEN); + feeOutput = isICP + ? `Maximum fee (ICP) : ${feeToken}` + : `Maximum fee (Tokens) : ${feeToken}`; + } + + const memoOutput = + params.memo !== undefined + ? `Memo : ${uint8ArrayToBigInt(params.memo).toString()}` + : "Memo : 0"; + + const output = [ + outputTxType, + ...(isICP ? [] : canisterIdOutputs), + ...fromOutputs, + ...toOutputs, + paymentOutput, + ...(feeOutput !== undefined ? [feeOutput] : []), + memoOutput, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(transferFn.argTypes, [rawRequestBody]), + methodName: "icrc1_transfer", + canisterId: params.canisterId, + caller: params.owner, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + isICP, + name: "ICRC1 Transfer", + canisterId: params.canisterId.toText(), + caller: params.owner.toText() ?? defaultCaller.toText(), + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const subaccount1 = arrayOfNumberToUint8Array([ + 215, 232, 81, 101, 44, 208, 50, 186, 94, 215, 107, 23, 246, 38, 170, 71, + 130, 159, 6, 229, 35, 90, 13, 88, 14, 150, 211, 114, 119, 41, 234, 36, + ]); + const subaccount2 = arrayOfNumberToUint8Array([ + 163, 110, 20, 60, 134, 231, 228, 113, 231, 199, 25, 80, 100, 135, 207, + 122, 108, 244, 13, 167, 99, 97, 174, 175, 238, 81, 51, 225, 217, 172, 234, + 61, + ]); + const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", + ); + const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", + ); + const canisterId1 = Principal.fromText("ppmzm-3aaaa-aaaaa-aacpq-cai"); + const canisterId2 = Principal.fromText("s24we-diaaa-aaaaa-aaaka-cai"); + const vectors = [ + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + canisterId: canisterId1, + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + from_subaccount: subaccount1, + fee: BigInt(10_000), + canisterId: canisterId1, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [subaccount2], + }, + amount: BigInt(330_000_000), + from_subaccount: subaccount1, + fee: BigInt(10_000), + canisterId: canisterId1, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [subaccount2], + }, + amount: BigInt(330_000_000), + memo: numberToUint8Array(11223312), + fee: BigInt(30_000), + canisterId: canisterId2, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [], + }, + amount: BigInt(314_000_000), + memo: numberToUint8Array(11223312), + canisterId: canisterId1, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(1331_400_000), + memo: numberToUint8Array(11223312), + from_subaccount: subaccount2, + fee: BigInt(20_000), + canisterId: canisterId1, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + from_subaccount: subaccount2, + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + fee: BigInt(100_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + from_subaccount: subaccount2, + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + from_subaccount: subaccount2, + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + ]; + + writeToJson({ + data: vectors, + fileName: "icrc-1.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230712-sns-set-dissolve-timestamp.ts b/scripts/test-vectors/20230712-sns-set-dissolve-timestamp.ts new file mode 100644 index 000000000..1f94bed2b --- /dev/null +++ b/scripts/test-vectors/20230712-sns-set-dissolve-timestamp.ts @@ -0,0 +1,128 @@ +import { IDL } from "@dfinity/candid"; +import { Principal } from "@dfinity/principal"; +import { SnsSetDissolveTimestampParams } from "@dfinity/sns/src"; +import { toSetDissolveTimestampRequest } from "@dfinity/sns/src/converters/governance.converters"; +import { arrayOfNumberToUint8Array } from "@dfinity/utils"; +import { + SECONDS_IN_DAY, + SECONDS_IN_MONTH, + SECONDS_IN_YEAR, + secondsToDissolveDelayDuration, +} from "./date-utils"; +import { ManageNeuronFn } from "./sns-governance.idl"; +import { bytesToHexString, createBlob, writeToJson } from "./utils"; + +/** + * Issue: https://github.com/Zondax/ledger-icp/issues/212 + */ + +interface Params extends SnsSetDissolveTimestampParams { + canisterId: Principal; +} +const nowInSeconds = Math.round(Date.now() / 1000); + +const createTestVector = (params: Params) => { + const rawRequestBody = toSetDissolveTimestampRequest(params); + const neuronIdString = bytesToHexString(Array.from(params.neuronId.id)); + const timestampLabel = secondsToDissolveDelayDuration( + params.dissolveTimestampSeconds - BigInt(nowInSeconds), + ); + console.log("in da test vector", params.dissolveTimestampSeconds); + return { + blob_candid: createBlob({ + arg: IDL.encode(ManageNeuronFn.argTypes, [rawRequestBody]), + methodName: "manage_neuron", + canisterId: params.canisterId, + }), + nowInSeconds, + timestampLabel, + neuronIdString: neuronIdString, + name: "Set Dissolve Delay Timestamp", + canisterId: params.canisterId.toText(), + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const id1 = arrayOfNumberToUint8Array([ + 215, 232, 81, 101, 44, 208, 50, 186, 94, 215, 107, 23, 246, 38, 170, 71, + 130, 159, 6, 229, 35, 90, 13, 88, 14, 150, 211, 114, 119, 41, 234, 36, + ]); + const id2 = arrayOfNumberToUint8Array([ + 163, 110, 20, 60, 134, 231, 228, 113, 231, 199, 25, 80, 100, 135, 207, + 122, 108, 244, 13, 167, 99, 97, 174, 175, 238, 81, 51, 225, 217, 172, 234, + 61, + ]); + const canisterId1 = Principal.fromText("ppmzm-3aaaa-aaaaa-aacpq-cai"); + const canisterId2 = Principal.fromText("s24we-diaaa-aaaaa-aaaka-cai"); + const inOneYear = BigInt(nowInSeconds + SECONDS_IN_YEAR); + const nextYearAndAHalf = BigInt( + Math.round(nowInSeconds + SECONDS_IN_YEAR + SECONDS_IN_MONTH * 6), + ); + const inOneYearMonthsAndDays = BigInt( + Math.round( + nowInSeconds + + SECONDS_IN_YEAR + + SECONDS_IN_MONTH * 3 + + SECONDS_IN_DAY * 18, + ), + ); + const inSixMonths = BigInt(Math.round(nowInSeconds + SECONDS_IN_MONTH * 6)); + const inEightYears = BigInt(Math.round(nowInSeconds + SECONDS_IN_YEAR * 8)); + + const vectors = [ + createTestVector({ + neuronId: { id: id1 }, + dissolveTimestampSeconds: inOneYear, + canisterId: canisterId1, + }), + createTestVector({ + neuronId: { id: id1 }, + dissolveTimestampSeconds: inSixMonths, + canisterId: canisterId1, + }), + createTestVector({ + neuronId: { id: id1 }, + dissolveTimestampSeconds: inOneYearMonthsAndDays, + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id1 }, + dissolveTimestampSeconds: nextYearAndAHalf, + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id2 }, + dissolveTimestampSeconds: inOneYearMonthsAndDays, + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id2 }, + dissolveTimestampSeconds: inSixMonths, + canisterId: canisterId2, + }), + createTestVector({ + neuronId: { id: id2 }, + dissolveTimestampSeconds: inEightYears, + canisterId: canisterId1, + }), + createTestVector({ + neuronId: { id: id2 }, + dissolveTimestampSeconds: nextYearAndAHalf, + canisterId: canisterId1, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "sns-set-dissolve-delay-timestamp.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230921-icrc-1-issue.ts b/scripts/test-vectors/20230921-icrc-1-issue.ts new file mode 100644 index 000000000..a6cb1bd2f --- /dev/null +++ b/scripts/test-vectors/20230921-icrc-1-issue.ts @@ -0,0 +1,98 @@ +import { IDL } from "@dfinity/candid"; +import { toTransferArg } from "@dfinity/ledger/src/converters/ledger.converters"; +import { TransferParams } from "@dfinity/ledger/src/types/ledger.params"; +import { Principal } from "@dfinity/principal"; +import { uint8ArrayToBigInt } from "@dfinity/utils"; +import { transferFn } from "./sns-ledger.idl"; +import { createBlob, defaultCaller, writeToJson } from "./utils"; + +/** + * Issue: https://github.com/Zondax/ledger-icp/issues/218 + */ + +/** + * Principals can be up to 29 bytes long. + * + * Source: https://wiki.internetcomputer.org/wiki/Principal + */ + +interface Params extends TransferParams { + canisterId: Principal; + owner: Principal; + isWorking: boolean; +} + +// Fee is optional, if not provided, it will be set to 10000 which is the ICP fee +const ICP_DEFAULT_FEE_E8S = 10_000; + +const createTestVector = (params: Params) => { + const rawRequestBody = toTransferArg(params); + + const memoOutput = + params.memo !== undefined + ? `Memo : ${uint8ArrayToBigInt(params.memo).toString()}` + : "Memo : 0"; + + return { + blob_candid: createBlob({ + arg: IDL.encode(transferFn.argTypes, [rawRequestBody]), + methodName: "icrc1_transfer", + canisterId: params.canisterId, + caller: params.owner, + }), + isWorking: params.isWorking, + name: "ICRC1 ICP Transfer", + canisterId: params.canisterId.toText(), + caller: params.owner.toText() ?? defaultCaller.toText(), + candid_request: rawRequestBody, + }; +}; + +const main = () => { + try { + const notWorkingPrincipalDestination = Principal.fromText( + "ttwhn-ittl6-fnuml-tqdgz-tbyhu-f7jti-ft2cr-znzsu-biv4l-gpvg4-ba", + ); + const workingPrincipalDestination = Principal.fromText( + "mhfqd-kvmfi-k6dts-lgdui-wgyrn-5o4uv-jiokz-dk4v3-meds4-j4umt-vae", + ); + const fromPrincipal = Principal.from( + "q2fbu-chrgb-o7dmd-u5cfn-xgo4f-5lpcl-ldeca-wl4sn-5j7gb-ltm63-hqe", + ); + const ledgerCanister = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai"); + const workingTransfer: Params = { + owner: fromPrincipal, + to: { + owner: workingPrincipalDestination, + subaccount: [], + }, + isWorking: true, + fee: BigInt(10_000), + amount: BigInt(100_000_000), + canisterId: ledgerCanister, + created_at_time: BigInt(1629200000000000000), + }; + const vectors = [ + createTestVector(workingTransfer), + createTestVector({ + ...workingTransfer, + to: { + owner: notWorkingPrincipalDestination, + subaccount: [], + }, + isWorking: false, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "icrc-1-issue.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20230927-icrc-stake-neuron.ts b/scripts/test-vectors/20230927-icrc-stake-neuron.ts new file mode 100644 index 000000000..0d678e985 --- /dev/null +++ b/scripts/test-vectors/20230927-icrc-stake-neuron.ts @@ -0,0 +1,399 @@ +import { IDL } from "@dfinity/candid"; +import { MAINNET_LEDGER_CANISTER_ID } from "@dfinity/ledger-icp/src/constants/canister_ids"; +import { toTransferArg } from "@dfinity/ledger-icrc/src/converters/ledger.converters"; +import { TransferParams } from "@dfinity/ledger-icrc/src/types/ledger.params"; +import { MAINNET_GOVERNANCE_CANISTER_ID } from "@dfinity/nns/src/constants/canister_ids"; +import { E8S_PER_TOKEN } from "@dfinity/nns/src/constants/constants"; +import { Principal } from "@dfinity/principal"; +import { + arrayOfNumberToUint8Array, + asciiStringToByteArray, + fromNullable, + numberToUint8Array, + uint8ArrayToBigInt, +} from "@dfinity/utils"; +import { sha256 } from "@noble/hashes/sha256"; +import randomBytes from "randombytes"; +import { transferFn } from "./sns-ledger.idl"; +import { + createBlob, + defaultCaller, + splitAccount, + splitPrincipal, + writeToJson, +} from "./utils"; + +/** + * Issue: https://github.com/Zondax/ledger-icp/issues/223 + */ + +interface Params extends TransferParams { + canisterId: Principal; + owner: Principal; + nonce?: bigint; +} + +// Fee is optional, if not provided, it will be set to 10000 which is the ICP fee +const ICP_DEFAULT_FEE_E8S = 10_000; + +const createTestVector = (params: Params) => { + const rawRequestBody = toTransferArg(params); + const isICP = + params.canisterId.toText() === MAINNET_LEDGER_CANISTER_ID.toText(); + + const isStakeNeuron = + isICP && + params.to.owner.toText() === MAINNET_GOVERNANCE_CANISTER_ID.toText(); + + let outputTxType = isStakeNeuron + ? "Stake Neuron" + : isICP + ? "Send ICP" + : "Send Tokens"; + const canisterIdOutputs = splitPrincipal(params.canisterId).map( + (data, i, elements) => + `Canister Id [${i + 1}/${elements.length}] : ${data}`, + ); + + const fromOutputs = splitAccount( + { + owner: params.owner, + subaccount: params.from_subaccount, + }, + "From account", + ); + const toOutputs = splitAccount( + { + owner: params.to.owner, + subaccount: fromNullable(params.to.subaccount), + }, + "To account", + ); + + const amountToken = Number(params.amount) / Number(E8S_PER_TOKEN); + const paymentOutput = isICP + ? `Payment (ICP) : ${amountToken}` + : `Payment (Tokens) : ${amountToken}`; + + // Do not show fee if it's not present in the request body. + // Except if it's ICP, in which case we always show the fee. + let feeOutput: string | undefined; + if (isICP || params.fee !== undefined) { + const feeToken = + Number(params.fee ?? ICP_DEFAULT_FEE_E8S) / Number(E8S_PER_TOKEN); + feeOutput = isICP + ? `Maximum fee (ICP) : ${feeToken}` + : `Maximum fee (Tokens) : ${feeToken}`; + } + + const memoOutput = + params.memo !== undefined + ? `Memo : ${uint8ArrayToBigInt(params.memo).toString()}` + : "Memo : 0"; + + const output = [ + outputTxType, + ...(isICP ? [] : canisterIdOutputs), + ...fromOutputs, + ...(isStakeNeuron ? [] : toOutputs), + paymentOutput, + ...(feeOutput !== undefined ? [feeOutput] : []), + memoOutput, + ]; + return { + blob_candid: createBlob({ + arg: IDL.encode(transferFn.argTypes, [rawRequestBody]), + methodName: "icrc1_transfer", + canisterId: params.canisterId, + caller: params.owner, + }), + output: output.map((element, index) => `${index + 1} | ${element}`), + isICP, + isStakeNeuron, + name: "ICRC1 Transfer", + nonce: params.nonce, + canisterId: params.canisterId.toText(), + caller: params.owner.toText() ?? defaultCaller.toText(), + candid_request: rawRequestBody, + }; +}; + +// Reference: Governance Canister `getNeuronStakeSubAccountBytes` private method. +const getNeuronStakeSubAccountBytes = ( + nonceBytes: Uint8Array, + principal: Principal, +): Uint8Array => { + const padding = asciiStringToByteArray("neuron-stake"); + const shaObj = sha256.create(); + shaObj.update( + arrayOfNumberToUint8Array([ + 0x0c, + ...padding, + ...principal.toUint8Array(), + ...nonceBytes, + ]), + ); + return shaObj.digest(); +}; + +const main = () => { + try { + const subaccount1 = arrayOfNumberToUint8Array([ + 215, 232, 81, 101, 44, 208, 50, 186, 94, 215, 107, 23, 246, 38, 170, 71, + 130, 159, 6, 229, 35, 90, 13, 88, 14, 150, 211, 114, 119, 41, 234, 36, + ]); + const subaccount2 = arrayOfNumberToUint8Array([ + 163, 110, 20, 60, 134, 231, 228, 113, 231, 199, 25, 80, 100, 135, 207, + 122, 108, 244, 13, 167, 99, 97, 174, 175, 238, 81, 51, 225, 217, 172, 234, + 61, + ]); + const principal1 = Principal.fromText( + "krpzt-buecq-u3umg-7kb7r-j5jpx-twqwa-3ykc4-y3cnk-7kwvw-5bq6z-mae", + ); + const principal2 = Principal.fromText( + "2dfd6-abjpf-eihu7-pwv6m-qnlbt-oszmg-kb26q-rvqms-onmuh-mwiq3-uqe", + ); + const shortPrincipal = Principal.fromText( + "ttwhn-ittl6-fnuml-tqdgz-tbyhu-f7jti-ft2cr-znzsu-biv4l-gpvg4-ba", + ); + + const nonceBytes1 = new Uint8Array(randomBytes(8)); + const nonce1 = uint8ArrayToBigInt(nonceBytes1); + const toSubAccount1 = getNeuronStakeSubAccountBytes( + nonceBytes1, + principal1, + ); + const nonceBytes1_2 = new Uint8Array(randomBytes(8)); + const nonce1_2 = uint8ArrayToBigInt(nonceBytes1_2); + const toSubAccount1_2 = getNeuronStakeSubAccountBytes( + nonceBytes1_2, + principal1, + ); + const nonceBytes2 = new Uint8Array(randomBytes(8)); + const nonce2 = uint8ArrayToBigInt(nonceBytes2); + const toSubAccount2 = getNeuronStakeSubAccountBytes( + nonceBytes2, + principal2, + ); + const nonceBytesShort = new Uint8Array(randomBytes(8)); + const nonceShort = uint8ArrayToBigInt(nonceBytesShort); + const toSubAccountShort = getNeuronStakeSubAccountBytes( + nonceBytesShort, + shortPrincipal, + ); + const canisterId1 = Principal.fromText("ppmzm-3aaaa-aaaaa-aacpq-cai"); + const canisterId2 = Principal.fromText("s24we-diaaa-aaaaa-aaaka-cai"); + const vectors = [ + createTestVector({ + owner: principal2, + to: { + owner: MAINNET_GOVERNANCE_CANISTER_ID, + subaccount: [toSubAccount1], + }, + amount: BigInt(131_400_000), + fee: BigInt(10_000), + from_subaccount: subaccount2, + memo: nonceBytes1, + nonce: nonce1, + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: MAINNET_GOVERNANCE_CANISTER_ID, + subaccount: [toSubAccount1_2], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + memo: nonceBytes1_2, + nonce: nonce1_2, + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249219635000000), + }), + createTestVector({ + owner: shortPrincipal, + to: { + owner: MAINNET_GOVERNANCE_CANISTER_ID, + subaccount: [toSubAccountShort], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + memo: nonceBytesShort, + nonce: nonceShort, + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249216635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: MAINNET_GOVERNANCE_CANISTER_ID, + subaccount: [toSubAccount2], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + memo: nonceBytes2, + nonce: nonce2, + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249219635000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + canisterId: canisterId1, + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + from_subaccount: subaccount1, + fee: BigInt(10_000), + canisterId: canisterId1, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [subaccount2], + }, + amount: BigInt(330_000_000), + from_subaccount: subaccount1, + fee: BigInt(10_000), + canisterId: canisterId1, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [subaccount2], + }, + amount: BigInt(330_000_000), + memo: numberToUint8Array(11223312), + fee: BigInt(30_000), + canisterId: canisterId2, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [], + }, + amount: BigInt(314_000_000), + memo: numberToUint8Array(11223312), + canisterId: canisterId1, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(1331_400_000), + memo: numberToUint8Array(11223312), + from_subaccount: subaccount2, + fee: BigInt(20_000), + canisterId: canisterId1, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal1, + to: { + owner: principal2, + subaccount: [], + }, + amount: BigInt(100_000_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + from_subaccount: subaccount2, + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(31_400_000), + fee: BigInt(100_000), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + from_subaccount: subaccount2, + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1629200000000000000), + }), + createTestVector({ + owner: principal2, + to: { + owner: principal1, + subaccount: [subaccount1], + }, + amount: BigInt(131_400_000), + fee: BigInt(30_000), + from_subaccount: subaccount2, + memo: numberToUint8Array(11223312), + canisterId: MAINNET_LEDGER_CANISTER_ID, + created_at_time: BigInt(1675249266635000000), + }), + ]; + + writeToJson({ + data: vectors, + fileName: "icrc-stake-neuron.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20240722-list-neurons.ts b/scripts/test-vectors/20240722-list-neurons.ts new file mode 100644 index 000000000..d93778677 --- /dev/null +++ b/scripts/test-vectors/20240722-list-neurons.ts @@ -0,0 +1,87 @@ +// Date represents the date of the issue with the test vectors sent to Zondax: +// https://github.com/Zondax/ledger-icp/issues/166 +// We'll keep each script for reproducibility. +import { IDL } from "@dfinity/candid"; +import { NeuronId } from "@dfinity/nns/src"; +import { toNullable } from "@dfinity/utils/src"; +import { ListNeuronsFn as ListNeuronsFnWithoutFlag } from "./governance-without-empty-neurons-flag.idl"; +import { ListNeuronsFn } from "./governance.idl"; +import { createBlob, writeToJson } from "./utils"; + +const mockNeuronId = BigInt(15374508381553347371); +const mockNeuronId2 = BigInt(8836564053576662908); + +const createListNeurons = ({ + neuronIds, + includeEmptyNeurons, +}: { + neuronIds?: NeuronId[]; + includeEmptyNeurons?: boolean | null; +}) => { + const rawRequestBody = { + neuron_ids: neuronIds ?? [], + include_neurons_readable_by_caller: neuronIds ? false : true, + ...(includeEmptyNeurons === null + ? {} + : { + include_empty_neurons_readable_by_caller: + toNullable(includeEmptyNeurons), + }), + }; + + const test = { + blob_candid: createBlob({ + arg: IDL.encode( + includeEmptyNeurons === null + ? ListNeuronsFnWithoutFlag.argTypes + : ListNeuronsFn.argTypes, + [rawRequestBody], + ), + methodName: "list_neurons", + }), + name: `List Neurons ${ + includeEmptyNeurons === null ? "- no new `includeEmptyNeurons` param" : "" + }`, + candid_request: rawRequestBody, + output: ["0 | Transaction type : List Own Neurons"], + }; + neuronIds?.forEach((id, idx) => { + test.output.push(`${idx + 1} | Neuron ID ${idx + 1} : ${id}`); + }); + return test; +}; + +const main = () => { + try { + const vectors = [ + createListNeurons({}), + createListNeurons({ includeEmptyNeurons: true }), + createListNeurons({ includeEmptyNeurons: false }), + createListNeurons({ includeEmptyNeurons: null }), + createListNeurons({ neuronIds: [mockNeuronId, mockNeuronId2] }), + createListNeurons({ + neuronIds: [mockNeuronId, mockNeuronId2], + includeEmptyNeurons: true, + }), + createListNeurons({ + neuronIds: [mockNeuronId], + includeEmptyNeurons: false, + }), + createListNeurons({ + neuronIds: [mockNeuronId], + includeEmptyNeurons: null, + }), + ]; + + writeToJson({ + data: vectors, + fileName: "list-neurons.json", + }); + console.log("File created successfully"); + } catch (error) { + console.log("There was an error"); + console.log(error); + } +}; + +main(); diff --git a/scripts/test-vectors/20240730-icrc21.ts b/scripts/test-vectors/20240730-icrc21.ts new file mode 100644 index 000000000..b96110035 --- /dev/null +++ b/scripts/test-vectors/20240730-icrc21.ts @@ -0,0 +1,61 @@ +import { Cbor } from "@dfinity/agent"; +import { Principal } from "@dfinity/principal"; +import { writeToJson } from "./utils"; + +function hexStringToArrayBuffer(hexString: string): ArrayBuffer { + // Remove any non-hexadecimal characters (e.g., spaces) + hexString = hexString.replace(/[^a-fA-F0-9]/g, ""); + + // Ensure the string has an even length + if (hexString.length % 2 !== 0) { + throw new Error("Invalid hex string"); + } + + // Create an ArrayBuffer with the appropriate length + var arrayBuffer = new ArrayBuffer(hexString.length / 2); + var uint8Array = new Uint8Array(arrayBuffer); + + // Convert each pair of hex characters to a byte + for (var i = 0; i < hexString.length; i += 2) { + uint8Array[i / 2] = parseInt(hexString.substr(i, 2), 16); + } + + return arrayBuffer; +} + +const callRequest = { + arg: hexStringToArrayBuffer("4449444C00017104746F6269"), + canister_id: Principal.fromHex("00000000006000FD0101"), + ingress_expiry: BigInt("1712666798482000000"), + method_name: "greet", + request_type: "query", + sender: Principal.fromHex("04"), +}; + +const consentMessageRequest = { + arg: hexStringToArrayBuffer( + "4449444C076D7B6C01D880C6D007716C02CBAEB581017AB183E7F1077A6B028BEABFC2067F8EF1C1EE0D026E036C02EFCEE7800401C4FBF2DB05046C03D6FCA70200E1EDEB4A7184F7FEE80A0501060C4449444C00017104746F626905677265657402656E01011E000300", + ), + canister_id: Principal.fromHex("00000000006000FD0101"), + ingress_expiry: BigInt("1712666698482000000"), + method_name: "icrc21_canister_call_consent_message", + nonce: hexStringToArrayBuffer("A3788C1805553FB69B20F08E87E23B13"), + request_type: "call", + sender: Principal.fromHex("04"), +}; + +const createCandidBlobs = () => { + const callRequestBlob = Buffer.from( + Cbor.encode({ content: callRequest }), + ).toString("hex"); + const consentMessageRequestBlob = Buffer.from( + Cbor.encode({ content: consentMessageRequest }), + ).toString("hex"); + + writeToJson({ + data: { callRequestBlob, consentMessageRequestBlob }, + fileName: "icrc21.json", + }); +}; + +createCandidBlobs(); diff --git a/scripts/test-vectors/date-utils.ts b/scripts/test-vectors/date-utils.ts new file mode 100644 index 000000000..5c980800a --- /dev/null +++ b/scripts/test-vectors/date-utils.ts @@ -0,0 +1,60 @@ +const SECONDS_IN_MINUTE = 60; +const SECONDS_IN_HOUR = SECONDS_IN_MINUTE * 60; +export const SECONDS_IN_DAY = SECONDS_IN_HOUR * 24; +// Taking into account 1/4 of leap year +export const SECONDS_IN_YEAR = ((4 * 365 + 1) * SECONDS_IN_DAY) / 4; +export const SECONDS_IN_MONTH = SECONDS_IN_YEAR / 12; + +type LabelKey = "year" | "month" | "day" | "hour" | "minute" | "second"; +type LabelInfo = { + labelKey: LabelKey; + amount: number; +}; +const createLabel = (labelKey: LabelKey, amount: bigint): LabelInfo => ({ + labelKey, + amount: Number(amount), +}); + +const labels: { [key: string]: string } = { + year: "year", + year_plural: "years", + month: "month", + month_plural: "months", + day: "day", + day_plural: "days", +}; + +/** + * Displays years, months and days. + * + * Uses constants for `year` and `month`: + * - a year = 365.25 * 24 * 60 * 60 + * - a month = 1 year / 12 + * - rounds up days + * + * @param seconds + */ +export const secondsToDissolveDelayDuration = (seconds: bigint): string => { + const years = seconds / BigInt(SECONDS_IN_YEAR); + const months = (seconds % BigInt(SECONDS_IN_YEAR)) / BigInt(SECONDS_IN_MONTH); + const days = BigInt( + Math.ceil((Number(seconds) % SECONDS_IN_MONTH) / SECONDS_IN_DAY), + ); + const periods = [ + createLabel("year", years), + createLabel("month", months), + createLabel("day", days), + ]; + + return periods + .filter(({ amount }) => amount > 0) + .map( + (labelInfo) => + `${labelInfo.amount} ${ + labelInfo.amount === 1 + ? labels[labelInfo.labelKey] + : labels[`${labelInfo.labelKey}_plural`] + }`, + ) + .join(", "); +}; diff --git a/scripts/test-vectors/governance-without-empty-neurons-flag.idl.ts b/scripts/test-vectors/governance-without-empty-neurons-flag.idl.ts new file mode 100644 index 000000000..2499a4a83 --- /dev/null +++ b/scripts/test-vectors/governance-without-empty-neurons-flag.idl.ts @@ -0,0 +1,472 @@ +import { IDL } from "@dfinity/candid"; + +const Proposal = IDL.Rec(); +const NeuronId = IDL.Record({ id: IDL.Nat64 }); +const Followees = IDL.Record({ followees: IDL.Vec(NeuronId) }); +const AccountIdentifier = IDL.Record({ hash: IDL.Vec(IDL.Nat8) }); +const NodeProvider = IDL.Record({ + id: IDL.Opt(IDL.Principal), + reward_account: IDL.Opt(AccountIdentifier), +}); +const RewardToNeuron = IDL.Record({ dissolve_delay_seconds: IDL.Nat64 }); +const RewardToAccount = IDL.Record({ + to_account: IDL.Opt(AccountIdentifier), +}); +const RewardMode = IDL.Variant({ + RewardToNeuron: RewardToNeuron, + RewardToAccount: RewardToAccount, +}); +const RewardNodeProvider = IDL.Record({ + node_provider: IDL.Opt(NodeProvider), + reward_mode: IDL.Opt(RewardMode), + amount_e8s: IDL.Nat64, +}); +const MostRecentMonthlyNodeProviderRewards = IDL.Record({ + timestamp: IDL.Nat64, + rewards: IDL.Vec(RewardNodeProvider), +}); +const GovernanceCachedMetrics = IDL.Record({ + not_dissolving_neurons_e8s_buckets: IDL.Vec( + IDL.Tuple(IDL.Nat64, IDL.Float64), + ), + garbage_collectable_neurons_count: IDL.Nat64, + neurons_with_invalid_stake_count: IDL.Nat64, + not_dissolving_neurons_count_buckets: IDL.Vec( + IDL.Tuple(IDL.Nat64, IDL.Nat64), + ), + total_supply_icp: IDL.Nat64, + neurons_with_less_than_6_months_dissolve_delay_count: IDL.Nat64, + dissolved_neurons_count: IDL.Nat64, + total_staked_e8s: IDL.Nat64, + not_dissolving_neurons_count: IDL.Nat64, + dissolved_neurons_e8s: IDL.Nat64, + neurons_with_less_than_6_months_dissolve_delay_e8s: IDL.Nat64, + dissolving_neurons_count_buckets: IDL.Vec(IDL.Tuple(IDL.Nat64, IDL.Nat64)), + dissolving_neurons_count: IDL.Nat64, + dissolving_neurons_e8s_buckets: IDL.Vec(IDL.Tuple(IDL.Nat64, IDL.Float64)), + community_fund_total_staked_e8s: IDL.Nat64, + timestamp_seconds: IDL.Nat64, +}); +const NetworkEconomics = IDL.Record({ + neuron_minimum_stake_e8s: IDL.Nat64, + max_proposals_to_keep_per_topic: IDL.Nat32, + neuron_management_fee_per_proposal_e8s: IDL.Nat64, + reject_cost_e8s: IDL.Nat64, + transaction_fee_e8s: IDL.Nat64, + neuron_spawn_dissolve_delay_seconds: IDL.Nat64, + minimum_icp_xdr_rate: IDL.Nat64, + maximum_node_provider_rewards_e8s: IDL.Nat64, +}); +const RewardEvent = IDL.Record({ + day_after_genesis: IDL.Nat64, + actual_timestamp_seconds: IDL.Nat64, + distributed_e8s_equivalent: IDL.Nat64, + settled_proposals: IDL.Vec(NeuronId), +}); +const NeuronStakeTransfer = IDL.Record({ + to_subaccount: IDL.Vec(IDL.Nat8), + neuron_stake_e8s: IDL.Nat64, + from: IDL.Opt(IDL.Principal), + memo: IDL.Nat64, + from_subaccount: IDL.Vec(IDL.Nat8), + transfer_timestamp: IDL.Nat64, + block_height: IDL.Nat64, +}); +const GovernanceError = IDL.Record({ + error_message: IDL.Text, + error_type: IDL.Int32, +}); +const CfNeuron = IDL.Record({ + nns_neuron_id: IDL.Nat64, + amount_icp_e8s: IDL.Nat64, +}); +const CfParticipant = IDL.Record({ + hotkey_principal: IDL.Text, + cf_neurons: IDL.Vec(CfNeuron), +}); +const Ballot = IDL.Record({ vote: IDL.Int32, voting_power: IDL.Nat64 }); +const Tally = IDL.Record({ + no: IDL.Nat64, + yes: IDL.Nat64, + total: IDL.Nat64, + timestamp_seconds: IDL.Nat64, +}); +const KnownNeuronData = IDL.Record({ + name: IDL.Text, + description: IDL.Opt(IDL.Text), +}); +const KnownNeuron = IDL.Record({ + id: IDL.Opt(NeuronId), + known_neuron_data: IDL.Opt(KnownNeuronData), +}); +const Spawn = IDL.Record({ + percentage_to_spawn: IDL.Opt(IDL.Nat32), + new_controller: IDL.Opt(IDL.Principal), + nonce: IDL.Opt(IDL.Nat64), +}); +const Split = IDL.Record({ amount_e8s: IDL.Nat64 }); +const Follow = IDL.Record({ + topic: IDL.Int32, + followees: IDL.Vec(NeuronId), +}); +const ClaimOrRefreshNeuronFromAccount = IDL.Record({ + controller: IDL.Opt(IDL.Principal), + memo: IDL.Nat64, +}); +const By = IDL.Variant({ + NeuronIdOrSubaccount: IDL.Record({}), + MemoAndController: ClaimOrRefreshNeuronFromAccount, + Memo: IDL.Nat64, +}); +const ClaimOrRefresh = IDL.Record({ by: IDL.Opt(By) }); +const RemoveHotKey = IDL.Record({ + hot_key_to_remove: IDL.Opt(IDL.Principal), +}); +const AddHotKey = IDL.Record({ new_hot_key: IDL.Opt(IDL.Principal) }); +const ChangeAutoStakeMaturity = IDL.Record({ + requested_setting_for_auto_stake_maturity: IDL.Bool, +}); +const IncreaseDissolveDelay = IDL.Record({ + additional_dissolve_delay_seconds: IDL.Nat32, +}); +const SetDissolveTimestamp = IDL.Record({ + dissolve_timestamp_seconds: IDL.Nat64, +}); +const Operation = IDL.Variant({ + RemoveHotKey: RemoveHotKey, + AddHotKey: AddHotKey, + ChangeAutoStakeMaturity: ChangeAutoStakeMaturity, + StopDissolving: IDL.Record({}), + StartDissolving: IDL.Record({}), + IncreaseDissolveDelay: IncreaseDissolveDelay, + JoinCommunityFund: IDL.Record({}), + LeaveCommunityFund: IDL.Record({}), + SetDissolveTimestamp: SetDissolveTimestamp, +}); +const Configure = IDL.Record({ operation: IDL.Opt(Operation) }); +const RegisterVote = IDL.Record({ + vote: IDL.Int32, + proposal: IDL.Opt(NeuronId), +}); +const Merge = IDL.Record({ source_neuron_id: IDL.Opt(NeuronId) }); +const DisburseToNeuron = IDL.Record({ + dissolve_delay_seconds: IDL.Nat64, + kyc_verified: IDL.Bool, + amount_e8s: IDL.Nat64, + new_controller: IDL.Opt(IDL.Principal), + nonce: IDL.Nat64, +}); +const StakeMaturity = IDL.Record({ + percentage_to_stake: IDL.Opt(IDL.Nat32), +}); +const MergeMaturity = IDL.Record({ percentage_to_merge: IDL.Nat32 }); +const Amount = IDL.Record({ e8s: IDL.Nat64 }); +const Disburse = IDL.Record({ + to_account: IDL.Opt(AccountIdentifier), + amount: IDL.Opt(Amount), +}); +const Command = IDL.Variant({ + Spawn: Spawn, + Split: Split, + Follow: Follow, + ClaimOrRefresh: ClaimOrRefresh, + Configure: Configure, + RegisterVote: RegisterVote, + Merge: Merge, + DisburseToNeuron: DisburseToNeuron, + MakeProposal: Proposal, + StakeMaturity: StakeMaturity, + MergeMaturity: MergeMaturity, + Disburse: Disburse, +}); +const NeuronIdOrSubaccount = IDL.Variant({ + Subaccount: IDL.Vec(IDL.Nat8), + NeuronId: NeuronId, +}); +const ManageNeuron = IDL.Record({ + id: IDL.Opt(NeuronId), + command: IDL.Opt(Command), + neuron_id_or_subaccount: IDL.Opt(NeuronIdOrSubaccount), +}); +const ExecuteNnsFunction = IDL.Record({ + nns_function: IDL.Int32, + payload: IDL.Vec(IDL.Nat8), +}); +const Params = IDL.Record({ + min_participant_icp_e8s: IDL.Nat64, + max_icp_e8s: IDL.Nat64, + swap_due_timestamp_seconds: IDL.Nat64, + min_participants: IDL.Nat32, + sns_token_e8s: IDL.Nat64, + max_participant_icp_e8s: IDL.Nat64, + min_icp_e8s: IDL.Nat64, +}); +const OpenSnsTokenSwap = IDL.Record({ + community_fund_investment_e8s: IDL.Opt(IDL.Nat64), + target_swap_canister_id: IDL.Opt(IDL.Principal), + params: IDL.Opt(Params), +}); +const TimeWindow = IDL.Record({ + start_timestamp_seconds: IDL.Nat64, + end_timestamp_seconds: IDL.Nat64, +}); +const SetOpenTimeWindowRequest = IDL.Record({ + open_time_window: IDL.Opt(TimeWindow), +}); +const SetSnsTokenSwapOpenTimeWindow = IDL.Record({ + request: IDL.Opt(SetOpenTimeWindowRequest), + swap_canister_id: IDL.Opt(IDL.Principal), +}); +const SetDefaultFollowees = IDL.Record({ + default_followees: IDL.Vec(IDL.Tuple(IDL.Int32, Followees)), +}); +const RewardNodeProviders = IDL.Record({ + use_registry_derived_rewards: IDL.Opt(IDL.Bool), + rewards: IDL.Vec(RewardNodeProvider), +}); +const ApproveGenesisKyc = IDL.Record({ + principals: IDL.Vec(IDL.Principal), +}); +const Change = IDL.Variant({ + ToRemove: NodeProvider, + ToAdd: NodeProvider, +}); +const AddOrRemoveNodeProvider = IDL.Record({ change: IDL.Opt(Change) }); +const Motion = IDL.Record({ motion_text: IDL.Text }); +const Action = IDL.Variant({ + RegisterKnownNeuron: KnownNeuron, + ManageNeuron: ManageNeuron, + ExecuteNnsFunction: ExecuteNnsFunction, + RewardNodeProvider: RewardNodeProvider, + OpenSnsTokenSwap: OpenSnsTokenSwap, + SetSnsTokenSwapOpenTimeWindow: SetSnsTokenSwapOpenTimeWindow, + SetDefaultFollowees: SetDefaultFollowees, + RewardNodeProviders: RewardNodeProviders, + ManageNetworkEconomics: NetworkEconomics, + ApproveGenesisKyc: ApproveGenesisKyc, + AddOrRemoveNodeProvider: AddOrRemoveNodeProvider, + Motion: Motion, +}); +Proposal.fill( + IDL.Record({ + url: IDL.Text, + title: IDL.Opt(IDL.Text), + action: IDL.Opt(Action), + summary: IDL.Text, + }), +); +const WaitForQuietState = IDL.Record({ + current_deadline_timestamp_seconds: IDL.Nat64, +}); +const ProposalData = IDL.Record({ + id: IDL.Opt(NeuronId), + failure_reason: IDL.Opt(GovernanceError), + cf_participants: IDL.Vec(CfParticipant), + ballots: IDL.Vec(IDL.Tuple(IDL.Nat64, Ballot)), + proposal_timestamp_seconds: IDL.Nat64, + reward_event_round: IDL.Nat64, + failed_timestamp_seconds: IDL.Nat64, + reject_cost_e8s: IDL.Nat64, + latest_tally: IDL.Opt(Tally), + sns_token_swap_lifecycle: IDL.Opt(IDL.Int32), + decided_timestamp_seconds: IDL.Nat64, + proposal: IDL.Opt(Proposal), + proposer: IDL.Opt(NeuronId), + wait_for_quiet_state: IDL.Opt(WaitForQuietState), + executed_timestamp_seconds: IDL.Nat64, + original_total_community_fund_maturity_e8s_equivalent: IDL.Opt(IDL.Nat64), +}); +const Command_2 = IDL.Variant({ + Spawn: NeuronId, + Split: Split, + Configure: Configure, + Merge: Merge, + DisburseToNeuron: DisburseToNeuron, + SyncCommand: IDL.Record({}), + ClaimOrRefreshNeuron: ClaimOrRefresh, + MergeMaturity: MergeMaturity, + Disburse: Disburse, +}); +const NeuronInFlightCommand = IDL.Record({ + command: IDL.Opt(Command_2), + timestamp: IDL.Nat64, +}); +const BallotInfo = IDL.Record({ + vote: IDL.Int32, + proposal_id: IDL.Opt(NeuronId), +}); +const DissolveState = IDL.Variant({ + DissolveDelaySeconds: IDL.Nat64, + WhenDissolvedTimestampSeconds: IDL.Nat64, +}); +const Neuron = IDL.Record({ + id: IDL.Opt(NeuronId), + staked_maturity_e8s_equivalent: IDL.Opt(IDL.Nat64), + controller: IDL.Opt(IDL.Principal), + recent_ballots: IDL.Vec(BallotInfo), + kyc_verified: IDL.Bool, + not_for_profit: IDL.Bool, + maturity_e8s_equivalent: IDL.Nat64, + cached_neuron_stake_e8s: IDL.Nat64, + created_timestamp_seconds: IDL.Nat64, + auto_stake_maturity: IDL.Opt(IDL.Bool), + aging_since_timestamp_seconds: IDL.Nat64, + hot_keys: IDL.Vec(IDL.Principal), + account: IDL.Vec(IDL.Nat8), + joined_community_fund_timestamp_seconds: IDL.Opt(IDL.Nat64), + dissolve_state: IDL.Opt(DissolveState), + followees: IDL.Vec(IDL.Tuple(IDL.Int32, Followees)), + neuron_fees_e8s: IDL.Nat64, + transfer: IDL.Opt(NeuronStakeTransfer), + known_neuron_data: IDL.Opt(KnownNeuronData), + spawn_at_timestamp_seconds: IDL.Opt(IDL.Nat64), +}); +const Governance = IDL.Record({ + default_followees: IDL.Vec(IDL.Tuple(IDL.Int32, Followees)), + most_recent_monthly_node_provider_rewards: IDL.Opt( + MostRecentMonthlyNodeProviderRewards, + ), + maturity_modulation_last_updated_at_timestamp_seconds: IDL.Opt(IDL.Nat64), + wait_for_quiet_threshold_seconds: IDL.Nat64, + metrics: IDL.Opt(GovernanceCachedMetrics), + node_providers: IDL.Vec(NodeProvider), + cached_daily_maturity_modulation_basis_points: IDL.Opt(IDL.Int32), + economics: IDL.Opt(NetworkEconomics), + spawning_neurons: IDL.Opt(IDL.Bool), + latest_reward_event: IDL.Opt(RewardEvent), + to_claim_transfers: IDL.Vec(NeuronStakeTransfer), + short_voting_period_seconds: IDL.Nat64, + proposals: IDL.Vec(IDL.Tuple(IDL.Nat64, ProposalData)), + in_flight_commands: IDL.Vec(IDL.Tuple(IDL.Nat64, NeuronInFlightCommand)), + neurons: IDL.Vec(IDL.Tuple(IDL.Nat64, Neuron)), + genesis_timestamp_seconds: IDL.Nat64, +}); +const Result = IDL.Variant({ Ok: IDL.Null, Err: GovernanceError }); +const Result_1 = IDL.Variant({ + Error: GovernanceError, + NeuronId: NeuronId, +}); +const ClaimOrRefreshNeuronFromAccountResponse = IDL.Record({ + result: IDL.Opt(Result_1), +}); +const Result_2 = IDL.Variant({ Ok: Neuron, Err: GovernanceError }); +const Result_3 = IDL.Variant({ + Ok: RewardNodeProviders, + Err: GovernanceError, +}); +const NeuronInfo = IDL.Record({ + dissolve_delay_seconds: IDL.Nat64, + recent_ballots: IDL.Vec(BallotInfo), + created_timestamp_seconds: IDL.Nat64, + state: IDL.Int32, + stake_e8s: IDL.Nat64, + joined_community_fund_timestamp_seconds: IDL.Opt(IDL.Nat64), + retrieved_at_timestamp_seconds: IDL.Nat64, + known_neuron_data: IDL.Opt(KnownNeuronData), + voting_power: IDL.Nat64, + age_seconds: IDL.Nat64, +}); +const Result_4 = IDL.Variant({ Ok: NeuronInfo, Err: GovernanceError }); +const Result_5 = IDL.Variant({ + Ok: NodeProvider, + Err: GovernanceError, +}); +const ProposalInfo = IDL.Record({ + id: IDL.Opt(NeuronId), + status: IDL.Int32, + topic: IDL.Int32, + failure_reason: IDL.Opt(GovernanceError), + ballots: IDL.Vec(IDL.Tuple(IDL.Nat64, Ballot)), + proposal_timestamp_seconds: IDL.Nat64, + reward_event_round: IDL.Nat64, + deadline_timestamp_seconds: IDL.Opt(IDL.Nat64), + failed_timestamp_seconds: IDL.Nat64, + reject_cost_e8s: IDL.Nat64, + latest_tally: IDL.Opt(Tally), + reward_status: IDL.Int32, + decided_timestamp_seconds: IDL.Nat64, + proposal: IDL.Opt(Proposal), + proposer: IDL.Opt(NeuronId), + executed_timestamp_seconds: IDL.Nat64, +}); +const ListKnownNeuronsResponse = IDL.Record({ + known_neurons: IDL.Vec(KnownNeuron), +}); +const ListNeurons = IDL.Record({ + neuron_ids: IDL.Vec(IDL.Nat64), + include_neurons_readable_by_caller: IDL.Bool, +}); +const ListNeuronsResponse = IDL.Record({ + neuron_infos: IDL.Vec(IDL.Tuple(IDL.Nat64, NeuronInfo)), + full_neurons: IDL.Vec(Neuron), +}); +const ListNodeProvidersResponse = IDL.Record({ + node_providers: IDL.Vec(NodeProvider), +}); +const ListProposalInfo = IDL.Record({ + include_reward_status: IDL.Vec(IDL.Int32), + before_proposal: IDL.Opt(NeuronId), + limit: IDL.Nat32, + exclude_topic: IDL.Vec(IDL.Int32), + include_status: IDL.Vec(IDL.Int32), +}); +const ListProposalInfoResponse = IDL.Record({ + proposal_info: IDL.Vec(ProposalInfo), +}); +const SpawnResponse = IDL.Record({ created_neuron_id: IDL.Opt(NeuronId) }); +const ClaimOrRefreshResponse = IDL.Record({ + refreshed_neuron_id: IDL.Opt(NeuronId), +}); +const MakeProposalResponse = IDL.Record({ + proposal_id: IDL.Opt(NeuronId), +}); +const StakeMaturityResponse = IDL.Record({ + maturity_e8s: IDL.Nat64, + staked_maturity_e8s: IDL.Nat64, +}); +const MergeMaturityResponse = IDL.Record({ + merged_maturity_e8s: IDL.Nat64, + new_stake_e8s: IDL.Nat64, +}); +const DisburseResponse = IDL.Record({ transfer_block_height: IDL.Nat64 }); +const Command_1 = IDL.Variant({ + Error: GovernanceError, + Spawn: SpawnResponse, + Split: SpawnResponse, + Follow: IDL.Record({}), + ClaimOrRefresh: ClaimOrRefreshResponse, + Configure: IDL.Record({}), + RegisterVote: IDL.Record({}), + Merge: IDL.Record({}), + DisburseToNeuron: SpawnResponse, + MakeProposal: MakeProposalResponse, + StakeMaturity: StakeMaturityResponse, + MergeMaturity: MergeMaturityResponse, + Disburse: DisburseResponse, +}); +const ManageNeuronResponse = IDL.Record({ command: IDL.Opt(Command_1) }); +const Committed = IDL.Record({ + sns_governance_canister_id: IDL.Opt(IDL.Principal), +}); +const Result_6 = IDL.Variant({ + Committed: Committed, + Aborted: IDL.Record({}), +}); +const SettleCommunityFundParticipation = IDL.Record({ + result: IDL.Opt(Result_6), + open_sns_token_swap_proposal_id: IDL.Opt(IDL.Nat64), +}); +const UpdateNodeProvider = IDL.Record({ + reward_account: IDL.Opt(AccountIdentifier), +}); + +export const ManageNeuronFn = IDL.Func( + [ManageNeuron], + [ManageNeuronResponse], + [], +); +export const ListNeuronsFn = IDL.Func( + [ListNeurons], + [ListNeuronsResponse], + ["query"], +); diff --git a/scripts/test-vectors/governance.idl.ts b/scripts/test-vectors/governance.idl.ts index b65279e21..81b353508 100644 --- a/scripts/test-vectors/governance.idl.ts +++ b/scripts/test-vectors/governance.idl.ts @@ -76,6 +76,14 @@ const GovernanceError = IDL.Record({ error_message: IDL.Text, error_type: IDL.Int32, }); +const CfNeuron = IDL.Record({ + nns_neuron_id: IDL.Nat64, + amount_icp_e8s: IDL.Nat64, +}); +const CfParticipant = IDL.Record({ + hotkey_principal: IDL.Text, + cf_neurons: IDL.Vec(CfNeuron), +}); const Ballot = IDL.Record({ vote: IDL.Int32, voting_power: IDL.Nat64 }); const Tally = IDL.Record({ no: IDL.Nat64, @@ -115,6 +123,9 @@ const RemoveHotKey = IDL.Record({ hot_key_to_remove: IDL.Opt(IDL.Principal), }); const AddHotKey = IDL.Record({ new_hot_key: IDL.Opt(IDL.Principal) }); +const ChangeAutoStakeMaturity = IDL.Record({ + requested_setting_for_auto_stake_maturity: IDL.Bool, +}); const IncreaseDissolveDelay = IDL.Record({ additional_dissolve_delay_seconds: IDL.Nat32, }); @@ -124,6 +135,7 @@ const SetDissolveTimestamp = IDL.Record({ const Operation = IDL.Variant({ RemoveHotKey: RemoveHotKey, AddHotKey: AddHotKey, + ChangeAutoStakeMaturity: ChangeAutoStakeMaturity, StopDissolving: IDL.Record({}), StartDissolving: IDL.Record({}), IncreaseDissolveDelay: IncreaseDissolveDelay, @@ -144,6 +156,9 @@ const DisburseToNeuron = IDL.Record({ new_controller: IDL.Opt(IDL.Principal), nonce: IDL.Nat64, }); +const StakeMaturity = IDL.Record({ + percentage_to_stake: IDL.Opt(IDL.Nat32), +}); const MergeMaturity = IDL.Record({ percentage_to_merge: IDL.Nat32 }); const Amount = IDL.Record({ e8s: IDL.Nat64 }); const Disburse = IDL.Record({ @@ -160,6 +175,7 @@ const Command = IDL.Variant({ Merge: Merge, DisburseToNeuron: DisburseToNeuron, MakeProposal: Proposal, + StakeMaturity: StakeMaturity, MergeMaturity: MergeMaturity, Disburse: Disburse, }); @@ -176,6 +192,20 @@ const ExecuteNnsFunction = IDL.Record({ nns_function: IDL.Int32, payload: IDL.Vec(IDL.Nat8), }); +const Params = IDL.Record({ + min_participant_icp_e8s: IDL.Nat64, + max_icp_e8s: IDL.Nat64, + swap_due_timestamp_seconds: IDL.Nat64, + min_participants: IDL.Nat32, + sns_token_e8s: IDL.Nat64, + max_participant_icp_e8s: IDL.Nat64, + min_icp_e8s: IDL.Nat64, +}); +const OpenSnsTokenSwap = IDL.Record({ + community_fund_investment_e8s: IDL.Opt(IDL.Nat64), + target_swap_canister_id: IDL.Opt(IDL.Principal), + params: IDL.Opt(Params), +}); const TimeWindow = IDL.Record({ start_timestamp_seconds: IDL.Nat64, end_timestamp_seconds: IDL.Nat64, @@ -208,6 +238,7 @@ const Action = IDL.Variant({ ManageNeuron: ManageNeuron, ExecuteNnsFunction: ExecuteNnsFunction, RewardNodeProvider: RewardNodeProvider, + OpenSnsTokenSwap: OpenSnsTokenSwap, SetSnsTokenSwapOpenTimeWindow: SetSnsTokenSwapOpenTimeWindow, SetDefaultFollowees: SetDefaultFollowees, RewardNodeProviders: RewardNodeProviders, @@ -230,17 +261,20 @@ const WaitForQuietState = IDL.Record({ const ProposalData = IDL.Record({ id: IDL.Opt(NeuronId), failure_reason: IDL.Opt(GovernanceError), + cf_participants: IDL.Vec(CfParticipant), ballots: IDL.Vec(IDL.Tuple(IDL.Nat64, Ballot)), proposal_timestamp_seconds: IDL.Nat64, reward_event_round: IDL.Nat64, failed_timestamp_seconds: IDL.Nat64, reject_cost_e8s: IDL.Nat64, latest_tally: IDL.Opt(Tally), + sns_token_swap_lifecycle: IDL.Opt(IDL.Int32), decided_timestamp_seconds: IDL.Nat64, proposal: IDL.Opt(Proposal), proposer: IDL.Opt(NeuronId), wait_for_quiet_state: IDL.Opt(WaitForQuietState), executed_timestamp_seconds: IDL.Nat64, + original_total_community_fund_maturity_e8s_equivalent: IDL.Opt(IDL.Nat64), }); const Command_2 = IDL.Variant({ Spawn: NeuronId, @@ -248,6 +282,7 @@ const Command_2 = IDL.Variant({ Configure: Configure, Merge: Merge, DisburseToNeuron: DisburseToNeuron, + SyncCommand: IDL.Record({}), ClaimOrRefreshNeuron: ClaimOrRefresh, MergeMaturity: MergeMaturity, Disburse: Disburse, @@ -266,6 +301,7 @@ const DissolveState = IDL.Variant({ }); const Neuron = IDL.Record({ id: IDL.Opt(NeuronId), + staked_maturity_e8s_equivalent: IDL.Opt(IDL.Nat64), controller: IDL.Opt(IDL.Principal), recent_ballots: IDL.Vec(BallotInfo), kyc_verified: IDL.Bool, @@ -273,6 +309,7 @@ const Neuron = IDL.Record({ maturity_e8s_equivalent: IDL.Nat64, cached_neuron_stake_e8s: IDL.Nat64, created_timestamp_seconds: IDL.Nat64, + auto_stake_maturity: IDL.Opt(IDL.Bool), aging_since_timestamp_seconds: IDL.Nat64, hot_keys: IDL.Vec(IDL.Principal), account: IDL.Vec(IDL.Nat8), @@ -289,10 +326,11 @@ const Governance = IDL.Record({ most_recent_monthly_node_provider_rewards: IDL.Opt( MostRecentMonthlyNodeProviderRewards, ), + maturity_modulation_last_updated_at_timestamp_seconds: IDL.Opt(IDL.Nat64), wait_for_quiet_threshold_seconds: IDL.Nat64, metrics: IDL.Opt(GovernanceCachedMetrics), - cached_daily_maturity_modulation: IDL.Opt(IDL.Float64), node_providers: IDL.Vec(NodeProvider), + cached_daily_maturity_modulation_basis_points: IDL.Opt(IDL.Int32), economics: IDL.Opt(NetworkEconomics), spawning_neurons: IDL.Opt(IDL.Bool), latest_reward_event: IDL.Opt(RewardEvent), @@ -301,7 +339,6 @@ const Governance = IDL.Record({ proposals: IDL.Vec(IDL.Tuple(IDL.Nat64, ProposalData)), in_flight_commands: IDL.Vec(IDL.Tuple(IDL.Nat64, NeuronInFlightCommand)), neurons: IDL.Vec(IDL.Tuple(IDL.Nat64, Neuron)), - last_updated_maturity_modulation_cache: IDL.Opt(IDL.Nat64), genesis_timestamp_seconds: IDL.Nat64, }); const Result = IDL.Variant({ Ok: IDL.Null, Err: GovernanceError }); @@ -358,6 +395,7 @@ const ListKnownNeuronsResponse = IDL.Record({ const ListNeurons = IDL.Record({ neuron_ids: IDL.Vec(IDL.Nat64), include_neurons_readable_by_caller: IDL.Bool, + include_empty_neurons_readable_by_caller: IDL.Opt(IDL.Bool), }); const ListNeuronsResponse = IDL.Record({ neuron_infos: IDL.Vec(IDL.Tuple(IDL.Nat64, NeuronInfo)), @@ -383,6 +421,10 @@ const ClaimOrRefreshResponse = IDL.Record({ const MakeProposalResponse = IDL.Record({ proposal_id: IDL.Opt(NeuronId), }); +const StakeMaturityResponse = IDL.Record({ + maturity_e8s: IDL.Nat64, + staked_maturity_e8s: IDL.Nat64, +}); const MergeMaturityResponse = IDL.Record({ merged_maturity_e8s: IDL.Nat64, new_stake_e8s: IDL.Nat64, @@ -399,15 +441,33 @@ const Command_1 = IDL.Variant({ Merge: IDL.Record({}), DisburseToNeuron: SpawnResponse, MakeProposal: MakeProposalResponse, + StakeMaturity: StakeMaturityResponse, MergeMaturity: MergeMaturityResponse, Disburse: DisburseResponse, }); const ManageNeuronResponse = IDL.Record({ command: IDL.Opt(Command_1) }); +const Committed = IDL.Record({ + sns_governance_canister_id: IDL.Opt(IDL.Principal), +}); +const Result_6 = IDL.Variant({ + Committed: Committed, + Aborted: IDL.Record({}), +}); +const SettleCommunityFundParticipation = IDL.Record({ + result: IDL.Opt(Result_6), + open_sns_token_swap_proposal_id: IDL.Opt(IDL.Nat64), +}); const UpdateNodeProvider = IDL.Record({ reward_account: IDL.Opt(AccountIdentifier), }); + export const ManageNeuronFn = IDL.Func( [ManageNeuron], [ManageNeuronResponse], [], ); +export const ListNeuronsFn = IDL.Func( + [ListNeurons], + [ListNeuronsResponse], + ["query"], +); diff --git a/scripts/test-vectors/sns-governance.idl.ts b/scripts/test-vectors/sns-governance.idl.ts new file mode 100644 index 000000000..452c7b4ef --- /dev/null +++ b/scripts/test-vectors/sns-governance.idl.ts @@ -0,0 +1,473 @@ +import { IDL } from "@dfinity/candid"; + +const GenericNervousSystemFunction = IDL.Record({ + validator_canister_id: IDL.Opt(IDL.Principal), + target_canister_id: IDL.Opt(IDL.Principal), + validator_method_name: IDL.Opt(IDL.Text), + target_method_name: IDL.Opt(IDL.Text), +}); +const FunctionType = IDL.Variant({ + NativeNervousSystemFunction: IDL.Record({}), + GenericNervousSystemFunction: GenericNervousSystemFunction, +}); +const NervousSystemFunction = IDL.Record({ + id: IDL.Nat64, + name: IDL.Text, + description: IDL.Opt(IDL.Text), + function_type: IDL.Opt(FunctionType), +}); +const GovernanceCachedMetrics = IDL.Record({ + not_dissolving_neurons_e8s_buckets: IDL.Vec( + IDL.Tuple(IDL.Nat64, IDL.Float64), + ), + garbage_collectable_neurons_count: IDL.Nat64, + neurons_with_invalid_stake_count: IDL.Nat64, + not_dissolving_neurons_count_buckets: IDL.Vec( + IDL.Tuple(IDL.Nat64, IDL.Nat64), + ), + neurons_with_less_than_6_months_dissolve_delay_count: IDL.Nat64, + dissolved_neurons_count: IDL.Nat64, + total_staked_e8s: IDL.Nat64, + total_supply_governance_tokens: IDL.Nat64, + not_dissolving_neurons_count: IDL.Nat64, + dissolved_neurons_e8s: IDL.Nat64, + neurons_with_less_than_6_months_dissolve_delay_e8s: IDL.Nat64, + dissolving_neurons_count_buckets: IDL.Vec(IDL.Tuple(IDL.Nat64, IDL.Nat64)), + dissolving_neurons_count: IDL.Nat64, + dissolving_neurons_e8s_buckets: IDL.Vec(IDL.Tuple(IDL.Nat64, IDL.Float64)), + timestamp_seconds: IDL.Nat64, +}); +const NeuronId = IDL.Record({ id: IDL.Vec(IDL.Nat8) }); +const Followees = IDL.Record({ followees: IDL.Vec(NeuronId) }); +const DefaultFollowees = IDL.Record({ + followees: IDL.Vec(IDL.Tuple(IDL.Nat64, Followees)), +}); +const NeuronPermissionList = IDL.Record({ + permissions: IDL.Vec(IDL.Int32), +}); +const VotingRewardsParameters = IDL.Record({ + final_reward_rate_basis_points: IDL.Opt(IDL.Nat64), + initial_reward_rate_basis_points: IDL.Opt(IDL.Nat64), + reward_rate_transition_duration_seconds: IDL.Opt(IDL.Nat64), + round_duration_seconds: IDL.Opt(IDL.Nat64), +}); +const NervousSystemParameters = IDL.Record({ + default_followees: IDL.Opt(DefaultFollowees), + max_dissolve_delay_seconds: IDL.Opt(IDL.Nat64), + max_dissolve_delay_bonus_percentage: IDL.Opt(IDL.Nat64), + max_followees_per_function: IDL.Opt(IDL.Nat64), + neuron_claimer_permissions: IDL.Opt(NeuronPermissionList), + neuron_minimum_stake_e8s: IDL.Opt(IDL.Nat64), + max_neuron_age_for_age_bonus: IDL.Opt(IDL.Nat64), + initial_voting_period_seconds: IDL.Opt(IDL.Nat64), + neuron_minimum_dissolve_delay_to_vote_seconds: IDL.Opt(IDL.Nat64), + reject_cost_e8s: IDL.Opt(IDL.Nat64), + max_proposals_to_keep_per_action: IDL.Opt(IDL.Nat32), + wait_for_quiet_deadline_increase_seconds: IDL.Opt(IDL.Nat64), + max_number_of_neurons: IDL.Opt(IDL.Nat64), + transaction_fee_e8s: IDL.Opt(IDL.Nat64), + max_number_of_proposals_with_ballots: IDL.Opt(IDL.Nat64), + max_age_bonus_percentage: IDL.Opt(IDL.Nat64), + neuron_grantable_permissions: IDL.Opt(NeuronPermissionList), + voting_rewards_parameters: IDL.Opt(VotingRewardsParameters), + max_number_of_principals_per_neuron: IDL.Opt(IDL.Nat64), +}); +const Version = IDL.Record({ + archive_wasm_hash: IDL.Vec(IDL.Nat8), + root_wasm_hash: IDL.Vec(IDL.Nat8), + swap_wasm_hash: IDL.Vec(IDL.Nat8), + ledger_wasm_hash: IDL.Vec(IDL.Nat8), + governance_wasm_hash: IDL.Vec(IDL.Nat8), + index_wasm_hash: IDL.Vec(IDL.Nat8), +}); +const ProposalId = IDL.Record({ id: IDL.Nat64 }); +const RewardEvent = IDL.Record({ + actual_timestamp_seconds: IDL.Nat64, + distributed_e8s_equivalent: IDL.Nat64, + round: IDL.Nat64, + settled_proposals: IDL.Vec(ProposalId), +}); +const UpgradeInProgress = IDL.Record({ + mark_failed_at_seconds: IDL.Nat64, + checking_upgrade_lock: IDL.Nat64, + proposal_id: IDL.Nat64, + target_version: IDL.Opt(Version), +}); +const GovernanceError = IDL.Record({ + error_message: IDL.Text, + error_type: IDL.Int32, +}); +const Ballot = IDL.Record({ + vote: IDL.Int32, + cast_timestamp_seconds: IDL.Nat64, + voting_power: IDL.Nat64, +}); +const Tally = IDL.Record({ + no: IDL.Nat64, + yes: IDL.Nat64, + total: IDL.Nat64, + timestamp_seconds: IDL.Nat64, +}); +const Subaccount = IDL.Record({ subaccount: IDL.Vec(IDL.Nat8) }); +const TransferSnsTreasuryFunds = IDL.Record({ + from_treasury: IDL.Int32, + to_principal: IDL.Opt(IDL.Principal), + to_subaccount: IDL.Opt(Subaccount), + memo: IDL.Opt(IDL.Nat64), + amount_e8s: IDL.Nat64, +}); +const UpgradeSnsControlledCanister = IDL.Record({ + new_canister_wasm: IDL.Vec(IDL.Nat8), + canister_id: IDL.Opt(IDL.Principal), +}); +const ManageSnsMetadata = IDL.Record({ + url: IDL.Opt(IDL.Text), + logo: IDL.Opt(IDL.Text), + name: IDL.Opt(IDL.Text), + description: IDL.Opt(IDL.Text), +}); +const ExecuteGenericNervousSystemFunction = IDL.Record({ + function_id: IDL.Nat64, + payload: IDL.Vec(IDL.Nat8), +}); +const Motion = IDL.Record({ motion_text: IDL.Text }); +const Action = IDL.Variant({ + ManageNervousSystemParameters: NervousSystemParameters, + AddGenericNervousSystemFunction: NervousSystemFunction, + RemoveGenericNervousSystemFunction: IDL.Nat64, + UpgradeSnsToNextVersion: IDL.Record({}), + TransferSnsTreasuryFunds: TransferSnsTreasuryFunds, + UpgradeSnsControlledCanister: UpgradeSnsControlledCanister, + Unspecified: IDL.Record({}), + ManageSnsMetadata: ManageSnsMetadata, + ExecuteGenericNervousSystemFunction: ExecuteGenericNervousSystemFunction, + Motion: Motion, +}); +const Proposal = IDL.Record({ + url: IDL.Text, + title: IDL.Text, + action: IDL.Opt(Action), + summary: IDL.Text, +}); +const WaitForQuietState = IDL.Record({ + current_deadline_timestamp_seconds: IDL.Nat64, +}); +const ProposalData = IDL.Record({ + id: IDL.Opt(ProposalId), + payload_text_rendering: IDL.Opt(IDL.Text), + action: IDL.Nat64, + failure_reason: IDL.Opt(GovernanceError), + ballots: IDL.Vec(IDL.Tuple(IDL.Text, Ballot)), + reward_event_round: IDL.Nat64, + failed_timestamp_seconds: IDL.Nat64, + proposal_creation_timestamp_seconds: IDL.Nat64, + initial_voting_period_seconds: IDL.Nat64, + reject_cost_e8s: IDL.Nat64, + latest_tally: IDL.Opt(Tally), + wait_for_quiet_deadline_increase_seconds: IDL.Nat64, + decided_timestamp_seconds: IDL.Nat64, + proposal: IDL.Opt(Proposal), + proposer: IDL.Opt(NeuronId), + wait_for_quiet_state: IDL.Opt(WaitForQuietState), + is_eligible_for_rewards: IDL.Bool, + executed_timestamp_seconds: IDL.Nat64, +}); +const Split = IDL.Record({ memo: IDL.Nat64, amount_e8s: IDL.Nat64 }); +const Follow = IDL.Record({ + function_id: IDL.Nat64, + followees: IDL.Vec(NeuronId), +}); +const Account = IDL.Record({ + owner: IDL.Opt(IDL.Principal), + subaccount: IDL.Opt(Subaccount), +}); +const DisburseMaturity = IDL.Record({ + to_account: IDL.Opt(Account), + percentage_to_disburse: IDL.Nat32, +}); +const ChangeAutoStakeMaturity = IDL.Record({ + requested_setting_for_auto_stake_maturity: IDL.Bool, +}); +const IncreaseDissolveDelay = IDL.Record({ + additional_dissolve_delay_seconds: IDL.Nat32, +}); +const SetDissolveTimestamp = IDL.Record({ + dissolve_timestamp_seconds: IDL.Nat64, +}); +const Operation = IDL.Variant({ + ChangeAutoStakeMaturity: ChangeAutoStakeMaturity, + StopDissolving: IDL.Record({}), + StartDissolving: IDL.Record({}), + IncreaseDissolveDelay: IncreaseDissolveDelay, + SetDissolveTimestamp: SetDissolveTimestamp, +}); +const Configure = IDL.Record({ operation: IDL.Opt(Operation) }); +const RegisterVote = IDL.Record({ + vote: IDL.Int32, + proposal: IDL.Opt(ProposalId), +}); +const MemoAndController = IDL.Record({ + controller: IDL.Opt(IDL.Principal), + memo: IDL.Nat64, +}); +const By = IDL.Variant({ + MemoAndController: MemoAndController, + NeuronId: IDL.Record({}), +}); +const ClaimOrRefresh = IDL.Record({ by: IDL.Opt(By) }); +const RemoveNeuronPermissions = IDL.Record({ + permissions_to_remove: IDL.Opt(NeuronPermissionList), + principal_id: IDL.Opt(IDL.Principal), +}); +const AddNeuronPermissions = IDL.Record({ + permissions_to_add: IDL.Opt(NeuronPermissionList), + principal_id: IDL.Opt(IDL.Principal), +}); +const MergeMaturity = IDL.Record({ percentage_to_merge: IDL.Nat32 }); +const Amount = IDL.Record({ e8s: IDL.Nat64 }); +const Disburse = IDL.Record({ + to_account: IDL.Opt(Account), + amount: IDL.Opt(Amount), +}); +const Command_2 = IDL.Variant({ + Split: Split, + Follow: Follow, + DisburseMaturity: DisburseMaturity, + Configure: Configure, + RegisterVote: RegisterVote, + SyncCommand: IDL.Record({}), + MakeProposal: Proposal, + ClaimOrRefreshNeuron: ClaimOrRefresh, + RemoveNeuronPermissions: RemoveNeuronPermissions, + AddNeuronPermissions: AddNeuronPermissions, + MergeMaturity: MergeMaturity, + Disburse: Disburse, +}); +const NeuronInFlightCommand = IDL.Record({ + command: IDL.Opt(Command_2), + timestamp: IDL.Nat64, +}); +const NeuronPermission = IDL.Record({ + principal: IDL.Opt(IDL.Principal), + permission_type: IDL.Vec(IDL.Int32), +}); +const DissolveState = IDL.Variant({ + DissolveDelaySeconds: IDL.Nat64, + WhenDissolvedTimestampSeconds: IDL.Nat64, +}); +const Neuron = IDL.Record({ + id: IDL.Opt(NeuronId), + staked_maturity_e8s_equivalent: IDL.Opt(IDL.Nat64), + permissions: IDL.Vec(NeuronPermission), + maturity_e8s_equivalent: IDL.Nat64, + cached_neuron_stake_e8s: IDL.Nat64, + created_timestamp_seconds: IDL.Nat64, + source_nns_neuron_id: IDL.Opt(IDL.Nat64), + auto_stake_maturity: IDL.Opt(IDL.Bool), + aging_since_timestamp_seconds: IDL.Nat64, + dissolve_state: IDL.Opt(DissolveState), + voting_power_percentage_multiplier: IDL.Nat64, + followees: IDL.Vec(IDL.Tuple(IDL.Nat64, Followees)), + neuron_fees_e8s: IDL.Nat64, +}); +const Governance = IDL.Record({ + root_canister_id: IDL.Opt(IDL.Principal), + id_to_nervous_system_functions: IDL.Vec( + IDL.Tuple(IDL.Nat64, NervousSystemFunction), + ), + metrics: IDL.Opt(GovernanceCachedMetrics), + mode: IDL.Int32, + parameters: IDL.Opt(NervousSystemParameters), + deployed_version: IDL.Opt(Version), + sns_initialization_parameters: IDL.Text, + latest_reward_event: IDL.Opt(RewardEvent), + pending_version: IDL.Opt(UpgradeInProgress), + swap_canister_id: IDL.Opt(IDL.Principal), + ledger_canister_id: IDL.Opt(IDL.Principal), + proposals: IDL.Vec(IDL.Tuple(IDL.Nat64, ProposalData)), + in_flight_commands: IDL.Vec(IDL.Tuple(IDL.Text, NeuronInFlightCommand)), + sns_metadata: IDL.Opt(ManageSnsMetadata), + neurons: IDL.Vec(IDL.Tuple(IDL.Text, Neuron)), + genesis_timestamp_seconds: IDL.Nat64, +}); +const NeuronParameters = IDL.Record({ + controller: IDL.Opt(IDL.Principal), + dissolve_delay_seconds: IDL.Opt(IDL.Nat64), + memo: IDL.Opt(IDL.Nat64), + source_nns_neuron_id: IDL.Opt(IDL.Nat64), + stake_e8s: IDL.Opt(IDL.Nat64), + hotkey: IDL.Opt(IDL.Principal), +}); +const ClaimSwapNeuronsRequest = IDL.Record({ + neuron_parameters: IDL.Vec(NeuronParameters), +}); +const ClaimSwapNeuronsResponse = IDL.Record({ + skipped_claims: IDL.Nat32, + successful_claims: IDL.Nat32, + failed_claims: IDL.Nat32, +}); +const GetMetadataResponse = IDL.Record({ + url: IDL.Opt(IDL.Text), + logo: IDL.Opt(IDL.Text), + name: IDL.Opt(IDL.Text), + description: IDL.Opt(IDL.Text), +}); +const GetNeuron = IDL.Record({ neuron_id: IDL.Opt(NeuronId) }); +const Result = IDL.Variant({ Error: GovernanceError, Neuron: Neuron }); +const GetNeuronResponse = IDL.Record({ result: IDL.Opt(Result) }); +const GetProposal = IDL.Record({ proposal_id: IDL.Opt(ProposalId) }); +const Result_1 = IDL.Variant({ + Error: GovernanceError, + Proposal: ProposalData, +}); +const GetProposalResponse = IDL.Record({ result: IDL.Opt(Result_1) }); +const CanisterStatusType = IDL.Variant({ + stopped: IDL.Null, + stopping: IDL.Null, + running: IDL.Null, +}); +const DefiniteCanisterSettingsArgs = IDL.Record({ + controller: IDL.Principal, + freezing_threshold: IDL.Nat, + controllers: IDL.Vec(IDL.Principal), + memory_allocation: IDL.Nat, + compute_allocation: IDL.Nat, +}); +const CanisterStatusResultV2 = IDL.Record({ + controller: IDL.Principal, + status: CanisterStatusType, + freezing_threshold: IDL.Nat, + balance: IDL.Vec(IDL.Tuple(IDL.Vec(IDL.Nat8), IDL.Nat)), + memory_size: IDL.Nat, + cycles: IDL.Nat, + settings: DefiniteCanisterSettingsArgs, + idle_cycles_burned_per_day: IDL.Nat, + module_hash: IDL.Opt(IDL.Vec(IDL.Nat8)), +}); +const GetRunningSnsVersionResponse = IDL.Record({ + deployed_version: IDL.Opt(Version), + pending_version: IDL.Opt(UpgradeInProgress), +}); +const GetSnsInitializationParametersResponse = IDL.Record({ + sns_initialization_parameters: IDL.Text, +}); +const ListNervousSystemFunctionsResponse = IDL.Record({ + reserved_ids: IDL.Vec(IDL.Nat64), + functions: IDL.Vec(NervousSystemFunction), +}); +const ListNeurons = IDL.Record({ + of_principal: IDL.Opt(IDL.Principal), + limit: IDL.Nat32, + start_page_at: IDL.Opt(NeuronId), +}); +const ListNeuronsResponse = IDL.Record({ neurons: IDL.Vec(Neuron) }); +const ListProposals = IDL.Record({ + include_reward_status: IDL.Vec(IDL.Int32), + before_proposal: IDL.Opt(ProposalId), + limit: IDL.Nat32, + exclude_type: IDL.Vec(IDL.Nat64), + include_status: IDL.Vec(IDL.Int32), +}); +const ListProposalsResponse = IDL.Record({ + proposals: IDL.Vec(ProposalData), +}); +const StakeMaturity = IDL.Record({ + percentage_to_stake: IDL.Opt(IDL.Nat32), +}); +const Command = IDL.Variant({ + Split: Split, + Follow: Follow, + DisburseMaturity: DisburseMaturity, + ClaimOrRefresh: ClaimOrRefresh, + Configure: Configure, + RegisterVote: RegisterVote, + MakeProposal: Proposal, + StakeMaturity: StakeMaturity, + RemoveNeuronPermissions: RemoveNeuronPermissions, + AddNeuronPermissions: AddNeuronPermissions, + MergeMaturity: MergeMaturity, + Disburse: Disburse, +}); +const ManageNeuron = IDL.Record({ + subaccount: IDL.Vec(IDL.Nat8), + command: IDL.Opt(Command), +}); +const SplitResponse = IDL.Record({ created_neuron_id: IDL.Opt(NeuronId) }); +const DisburseMaturityResponse = IDL.Record({ + transfer_block_height: IDL.Nat64, + amount_disbursed_e8s: IDL.Nat64, +}); +const ClaimOrRefreshResponse = IDL.Record({ + refreshed_neuron_id: IDL.Opt(NeuronId), +}); +const StakeMaturityResponse = IDL.Record({ + maturity_e8s: IDL.Nat64, + staked_maturity_e8s: IDL.Nat64, +}); +const MergeMaturityResponse = IDL.Record({ + merged_maturity_e8s: IDL.Nat64, + new_stake_e8s: IDL.Nat64, +}); +const DisburseResponse = IDL.Record({ transfer_block_height: IDL.Nat64 }); +const Command_1 = IDL.Variant({ + Error: GovernanceError, + Split: SplitResponse, + Follow: IDL.Record({}), + DisburseMaturity: DisburseMaturityResponse, + ClaimOrRefresh: ClaimOrRefreshResponse, + Configure: IDL.Record({}), + RegisterVote: IDL.Record({}), + MakeProposal: GetProposal, + RemoveNeuronPermission: IDL.Record({}), + StakeMaturity: StakeMaturityResponse, + MergeMaturity: MergeMaturityResponse, + Disburse: DisburseResponse, + AddNeuronPermission: IDL.Record({}), +}); +const ManageNeuronResponse = IDL.Record({ command: IDL.Opt(Command_1) }); +const SetMode = IDL.Record({ mode: IDL.Int32 }); + +// SNS Neuron transactions are always of this type +export const ManageNeuronFn = IDL.Func( + [ManageNeuron], + [ManageNeuronResponse], + [], +); + +// List of endpoints +IDL.Service({ + claim_swap_neurons: IDL.Func( + [ClaimSwapNeuronsRequest], + [ClaimSwapNeuronsResponse], + [], + ), + get_build_metadata: IDL.Func([], [IDL.Text], ["query"]), + get_metadata: IDL.Func([IDL.Record({})], [GetMetadataResponse], ["query"]), + get_nervous_system_parameters: IDL.Func( + [IDL.Null], + [NervousSystemParameters], + ["query"], + ), + get_neuron: IDL.Func([GetNeuron], [GetNeuronResponse], ["query"]), + get_proposal: IDL.Func([GetProposal], [GetProposalResponse], ["query"]), + get_root_canister_status: IDL.Func([IDL.Null], [CanisterStatusResultV2], []), + get_running_sns_version: IDL.Func( + [IDL.Record({})], + [GetRunningSnsVersionResponse], + ["query"], + ), + get_sns_initialization_parameters: IDL.Func( + [IDL.Record({})], + [GetSnsInitializationParametersResponse], + ["query"], + ), + list_nervous_system_functions: IDL.Func( + [], + [ListNervousSystemFunctionsResponse], + ["query"], + ), + list_neurons: IDL.Func([ListNeurons], [ListNeuronsResponse], ["query"]), + list_proposals: IDL.Func([ListProposals], [ListProposalsResponse], ["query"]), + manage_neuron: IDL.Func([ManageNeuron], [ManageNeuronResponse], []), + set_mode: IDL.Func([SetMode], [IDL.Record({})], []), +}); diff --git a/scripts/test-vectors/sns-ledger.idl.ts b/scripts/test-vectors/sns-ledger.idl.ts new file mode 100644 index 000000000..a6a2f6c03 --- /dev/null +++ b/scripts/test-vectors/sns-ledger.idl.ts @@ -0,0 +1,79 @@ +import { IDL } from "@dfinity/candid"; + +const Value = IDL.Variant({ + Int: IDL.Int, + Nat: IDL.Nat, + Blob: IDL.Vec(IDL.Nat8), + Text: IDL.Text, +}); +const Subaccount = IDL.Vec(IDL.Nat8); +const Account = IDL.Record({ + owner: IDL.Principal, + subaccount: IDL.Opt(Subaccount), +}); +const InitArgs = IDL.Record({ + token_symbol: IDL.Text, + transfer_fee: IDL.Nat64, + metadata: IDL.Vec(IDL.Tuple(IDL.Text, Value)), + minting_account: Account, + initial_balances: IDL.Vec(IDL.Tuple(Account, IDL.Nat64)), + archive_options: IDL.Record({ + num_blocks_to_archive: IDL.Nat64, + trigger_threshold: IDL.Nat64, + max_message_size_bytes: IDL.Opt(IDL.Nat64), + cycles_for_archive_creation: IDL.Opt(IDL.Nat64), + node_max_memory_size_bytes: IDL.Opt(IDL.Nat64), + controller_id: IDL.Principal, + }), + token_name: IDL.Text, +}); +const Tokens = IDL.Nat; +const Timestamp = IDL.Nat64; +const TransferArg = IDL.Record({ + to: Account, + fee: IDL.Opt(Tokens), + memo: IDL.Opt(IDL.Vec(IDL.Nat8)), + from_subaccount: IDL.Opt(Subaccount), + created_at_time: IDL.Opt(Timestamp), + amount: Tokens, +}); +const BlockIndex = IDL.Nat; +const TransferError = IDL.Variant({ + GenericError: IDL.Record({ + message: IDL.Text, + error_code: IDL.Nat, + }), + TemporarilyUnavailable: IDL.Null, + BadBurn: IDL.Record({ min_burn_amount: Tokens }), + Duplicate: IDL.Record({ duplicate_of: BlockIndex }), + BadFee: IDL.Record({ expected_fee: Tokens }), + CreatedInFuture: IDL.Record({ ledger_time: IDL.Nat64 }), + TooOld: IDL.Null, + InsufficientFunds: IDL.Record({ balance: Tokens }), +}); +const TransferResult = IDL.Variant({ + Ok: BlockIndex, + Err: TransferError, +}); +const service = IDL.Service({ + icrc1_balance_of: IDL.Func([Account], [Tokens], ["query"]), + icrc1_decimals: IDL.Func([], [IDL.Nat8], ["query"]), + icrc1_fee: IDL.Func([], [Tokens], ["query"]), + icrc1_metadata: IDL.Func( + [], + [IDL.Vec(IDL.Tuple(IDL.Text, Value))], + ["query"], + ), + icrc1_minting_account: IDL.Func([], [IDL.Opt(Account)], ["query"]), + icrc1_name: IDL.Func([], [IDL.Text], ["query"]), + icrc1_supported_standards: IDL.Func( + [], + [IDL.Vec(IDL.Record({ url: IDL.Text, name: IDL.Text }))], + ["query"], + ), + icrc1_symbol: IDL.Func([], [IDL.Text], ["query"]), + icrc1_total_supply: IDL.Func([], [Tokens], ["query"]), + icrc1_transfer: IDL.Func([TransferArg], [TransferResult], []), +}); + +export const transferFn = IDL.Func([TransferArg], [TransferResult], []); diff --git a/scripts/test-vectors/utils.ts b/scripts/test-vectors/utils.ts index 74195d250..368c29533 100644 --- a/scripts/test-vectors/utils.ts +++ b/scripts/test-vectors/utils.ts @@ -1,14 +1,16 @@ import { - AnonymousIdentity, CallRequest, Cbor, Expiry, ReadRequest, SubmitRequestType, } from "@dfinity/agent"; +import { IcrcAccount } from "@dfinity/ledger-icrc/src/types/ledger.responses"; +import { encodeIcrcAccount } from "@dfinity/ledger-icrc/src/utils/ledger.utils"; import { Principal } from "@dfinity/principal"; import { writeFileSync } from "fs"; import { MAINNET_GOVERNANCE_CANISTER_ID } from "../../packages/nns/src/constants/canister_ids"; +import { SnsNeuronPermissionType } from "../../packages/sns/src/enums/governance.enums"; /** * Changes needed to match the `arg` inside the blob_proto with the Hardware Wallet CLI blob. @@ -31,7 +33,7 @@ import { MAINNET_GOVERNANCE_CANISTER_ID } from "../../packages/nns/src/constants * - `hardwareWallet: false` in the method. */ -function _prepareCborForLedger( +export function _prepareCborForLedger( request: ReadRequest | CallRequest, ): ArrayBuffer { return Cbor.encode({ content: request }); @@ -40,34 +42,46 @@ function _prepareCborForLedger( // Default delta for ingress expiry is 5 minutes. const DEFAULT_INGRESS_EXPIRY_DELTA_IN_MSECS = 5 * 60 * 1000; +export const defaultCaller = Principal.fromText( + "5upke-tazvi-6ufqc-i3v6r-j4gpu-dpwti-obhal-yb5xj-ue32x-ktkql-rqe", +); + const createCallRequest = ({ arg, methodName, + canisterId, + caller, }: { arg: ArrayBuffer; methodName: string; + canisterId?: Principal; + caller?: Principal; }): CallRequest => ({ request_type: SubmitRequestType.Call, - canister_id: MAINNET_GOVERNANCE_CANISTER_ID, + canister_id: canisterId ?? MAINNET_GOVERNANCE_CANISTER_ID, method_name: methodName, arg, - sender: new AnonymousIdentity().getPrincipal(), - // sender: Principal.fromText( - // "q2fbu-chrgb-o7dmd-u5cfn-xgo4f-5lpcl-ldeca-wl4sn-5j7gb-ltm63-hqe" - // ), + // Use this principal to match the principal used in Zondax integration tests. + sender: caller ?? defaultCaller, ingress_expiry: new Expiry(DEFAULT_INGRESS_EXPIRY_DELTA_IN_MSECS), }); export const createBlob = ({ arg, methodName, + canisterId, + caller, }: { arg: ArrayBuffer; methodName: string; + canisterId?: Principal; + caller?: Principal; }): string => { const callRequestCandid = createCallRequest({ arg, methodName, + caller, + canisterId, }); const candidBlob = _prepareCborForLedger(callRequestCandid); return Buffer.from(candidBlob).toString("hex"); @@ -98,3 +112,77 @@ export const writeToJson = ({ console.log(error); } }; + +export const splitPrincipal = (principal: Principal): string[] => { + const text = principal.toText(); + const split = text.split("-"); + const groupedInThrees = []; + for (let i = 0; i < split.length; i += 3) { + groupedInThrees.push(split.slice(i, i + 3).join("-")); + } + const lines = []; + for (let i = 0; i < groupedInThrees.length; i += 2) { + lines.push(groupedInThrees.slice(i, i + 2).join(" : ")); + } + return lines; +}; + +export const splitString = ( + textToSplit: string, + screenText: string, +): string[] => { + return ( + textToSplit + .match(/.{1,8}/g) + ?.reduce((acc, curr) => { + if (acc.length === 0) { + return [curr]; + } + const lastItem = acc[acc.length - 1]; + if (lastItem.length > 18) { + return [...acc, curr]; + } else if (lastItem.length < 18) { + return [...acc.slice(0, -1), `${lastItem} : ${curr}`]; + } + return acc; + }, [] as string[]) + ?.map( + (data, i, elements) => + `${screenText} [${i + 1}/${elements.length}] : ${data}`, + ) || [] + ); +}; + +export const splitAccount = ( + account: IcrcAccount, + screenText: string, +): string[] => { + return splitString(encodeIcrcAccount(account), screenText); +}; + +export const permissionMapper: Record = { + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_VOTE]: "Vote", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_SUBMIT_PROPOSAL]: + "Submit Proposal", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_CONFIGURE_DISSOLVE_STATE]: + "Configure Dissolve State", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_DISBURSE]: "Disburse Neuron", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_DISBURSE_MATURITY]: + "Disburse Maturity", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_MANAGE_PRINCIPALS]: + "Manage Principals", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_MANAGE_VOTING_PERMISSION]: + "Manage Voting Permission", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_SPLIT]: "Split Neuron", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_STAKE_MATURITY]: + "Stake Maturity", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_UNSPECIFIED]: "Unspecified", + [SnsNeuronPermissionType.NEURON_PERMISSION_TYPE_MERGE_MATURITY]: + "Merge Maturity", +}; + +export const bytesToHexString = (bytes: number[]): string => + bytes.reduce( + (str, byte) => `${str}${byte.toString(16).padStart(2, "0")}`, + "", + );