From 545db816d69e3188305dee42f6e0714ac2312f31 Mon Sep 17 00:00:00 2001 From: mstrasinskis <98811342+mstrasinskis@users.noreply.github.com> Date: Fri, 30 Jun 2023 21:24:13 +0200 Subject: [PATCH] 1 proposal support (#369) # Motivation Add `CreateServiceNervousSystem` <-> `RawCreateServiceNervousSystem` transformations. Required for [nns-dapp pr](https://github.com/dfinity/nns-dapp/pull/2753). # Changes - add all related transform functions # Tests - Manually compared that after `RawCreateServiceNervousSystem to CreateServiceNervousSystem` transformation all fields and values match. # Screenshot | Before | After | |--------|--------| | ![image](https://github.com/dfinity/ic-js/assets/98811342/86b2c867-82ab-4ae2-925e-1c331311dd97)
![image](https://github.com/dfinity/ic-js/assets/98811342/748b6f6d-fe0a-42fd-b27d-db02a0f5eb0b) | ![image](https://github.com/dfinity/nns-dapp/assets/98811342/2dad9709-9a37-4408-99ce-de2c5b22d1ec) --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- package.json | 2 +- packages/ic-management/README.md | 81 ++++- .../governance/request.converters.ts | 304 +++++++++++++++++- .../governance/response.converters.ts | 282 +++++++++++++++- .../nns/src/types/governance_converters.ts | 92 +++++- 5 files changed, 745 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 2ee23980..bfdf66a0 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ { "name": "@dfinity/nns", "path": "./packages/nns/dist/index.js", - "limit": "30 kB", + "limit": "32 kB", "ignore": [ "@dfinity/agent", "@dfinity/candid", diff --git a/packages/ic-management/README.md b/packages/ic-management/README.md index cb82d5ad..c1e6e047 100644 --- a/packages/ic-management/README.md +++ b/packages/ic-management/README.md @@ -57,8 +57,15 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); #### Methods - [create](#gear-create) -- [canisterStatus](#gear-canisterstatus) +- [createCanister](#gear-createcanister) - [updateSettings](#gear-updatesettings) +- [installCode](#gear-installcode) +- [uninstallCode](#gear-uninstallcode) +- [startCanister](#gear-startcanister) +- [stopCanister](#gear-stopcanister) +- [canisterStatus](#gear-canisterstatus) +- [canisterInfo](#gear-canisterinfo) +- [deleteCanister](#gear-deletecanister) ##### :gear: create @@ -66,21 +73,77 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); | -------- | ---------------------------------------------------------------- | | `create` | `(options: ICManagementCanisterOptions) => ICManagementCanister` | -##### :gear: canisterStatus +##### :gear: createCanister -Returns canister details (memory size, status, etc.) +Create a new canister -| Method | Type | -| ---------------- | ------------------------------------------------------------ | -| `canisterStatus` | `(canisterId: Principal) => Promise` | +| Method | Type | +| ---------------- | ------------------------------------------------------------------------------------ | +| `createCanister` | `({ settings, senderCanisterVerion, }?: CreateCanisterParams) => Promise` | ##### :gear: updateSettings Update canister settings -| Method | Type | -| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| `updateSettings` | `({ canisterId, settings: { controllers, freezingThreshold, memoryAllocation, computeAllocation, }, }: UpdateSettingsParams) => Promise` | +| Method | Type | +| ---------------- | ------------------------------------------------------------------------------------------ | +| `updateSettings` | `({ canisterId, senderCanisterVerion, settings, }: UpdateSettingsParams) => Promise` | + +##### :gear: installCode + +Install code to a canister + +| Method | Type | +| ------------- | ---------------------------------------------------------------------------------------------------- | +| `installCode` | `({ mode, canisterId, wasmModule, arg, senderCanisterVerion, }: InstallCodeParams) => Promise` | + +##### :gear: uninstallCode + +Uninstall code from a canister + +| Method | Type | +| --------------- | ------------------------------------------------------------------------------- | +| `uninstallCode` | `({ canisterId, senderCanisterVerion, }: UninstallCodeParams) => Promise` | + +##### :gear: startCanister + +Start a canister + +| Method | Type | +| --------------- | ------------------------------------------ | +| `startCanister` | `(canisterId: Principal) => Promise` | + +##### :gear: stopCanister + +Stop a canister + +| Method | Type | +| -------------- | ------------------------------------------ | +| `stopCanister` | `(canisterId: Principal) => Promise` | + +##### :gear: canisterStatus + +Get canister details (memory size, status, etc.) + +| Method | Type | +| ---------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| `canisterStatus` | `(canisterId: Principal) => Promise<{ status: { stopped: null; } or { stopping: null; } | { running: null; }; memory_size: bigint; cycles: bigint; settings: definite_canister_settings; idle_cycles_burned_per_day: bigint; module_hash: [] | [...]; }>` | + +##### :gear: canisterInfo + +Get canister info (controllers, module hash, changes, etc.) + +| Method | Type | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `canisterInfo` | `({ canisterId, numRequestChanges, }: CanisterInfoParams) => Promise<{ controllers: Principal[]; module_hash: [] or [Uint8Array]; recent_changes: change[]; total_num_changes: bigint; }>` | + +##### :gear: deleteCanister + +Deletes a canister + +| Method | Type | +| ---------------- | ------------------------------------------ | +| `deleteCanister` | `(canisterId: Principal) => Promise` | diff --git a/packages/nns/src/canisters/governance/request.converters.ts b/packages/nns/src/canisters/governance/request.converters.ts index a93ff631..0e9fbcf1 100644 --- a/packages/nns/src/canisters/governance/request.converters.ts +++ b/packages/nns/src/canisters/governance/request.converters.ts @@ -7,15 +7,29 @@ import type { By as RawBy, Change as RawChange, Command as RawCommand, + CreateServiceNervousSystem as RawCreateServiceNervousSystem, + DeveloperDistribution as RawDeveloperDistribution, + Duration as RawDuration, Followees as RawFollowees, + GovernanceParameters as RawGovernanceParameters, + Image as RawImage, + InitialTokenDistribution as RawInitialTokenDistribution, + LedgerParameters as RawLedgerParameters, ListNeurons as RawListNeurons, ListProposalInfo, ManageNeuron as RawManageNeuron, + NeuronBasketConstructionParameters as RawNeuronBasketConstructionParameters, + NeuronDistribution as RawNeuronDistribution, NeuronId as RawNeuronId, NeuronIdOrSubaccount as RawNeuronIdOrSubaccount, NodeProvider as RawNodeProvider, Operation as RawOperation, + Percentage as RawPercentage, RewardMode as RawRewardMode, + SwapDistribution as RawSwapDistribution, + SwapParameters as RawSwapParameters, + Tokens as RawTokens, + VotingRewardParameters as RawVotingRewardParameters, } from "../../../candid/governance"; import type { AccountIdentifier as AccountIdentifierClass } from "../../account_identifier"; import type { Vote } from "../../enums/governance.enums"; @@ -27,16 +41,30 @@ import type { Change, ClaimOrRefreshNeuronRequest, Command, + CreateServiceNervousSystem, + DeveloperDistribution, DisburseToNeuronRequest, + Duration, FollowRequest, + GovernanceParameters, + Image, + InitialTokenDistribution, + LedgerParameters, ListProposalsRequest, MakeProposalRequest, ManageNeuron, + NeuronBasketConstructionParameters, + NeuronDistribution, NeuronIdOrSubaccount, NodeProvider, Operation, + Percentage, ProposalId, RewardMode, + SwapDistribution, + SwapParameters, + Tokens, + VotingRewardParameters, } from "../../types/governance_converters"; import { accountIdentifierToBytes } from "../../utils/account_identifier.utils"; @@ -64,6 +92,275 @@ const fromNeuronIdOrSubaccount = ( throw new UnsupportedValueError(neuronIdOrSubaccount); }; +const fromPercentage = (percentage: Percentage): RawPercentage => + percentage.basisPoints !== undefined + ? { basis_points: [percentage.basisPoints] } + : { basis_points: [] }; + +const fromDuration = (duration: Duration): RawDuration => + duration.seconds !== undefined + ? { seconds: [duration.seconds] } + : { seconds: [] }; + +const fromTokens = (tokens: Tokens): RawTokens => + tokens.e8s !== undefined ? { e8s: [tokens.e8s] } : { e8s: [] }; + +const fromImage = (image: Image): RawImage => + image.base64Encoding !== undefined + ? { base64_encoding: [image.base64Encoding] } + : { base64_encoding: [] }; + +const fromVotingRewardParameters = ( + votingRewardParameters: VotingRewardParameters +): RawVotingRewardParameters => ({ + reward_rate_transition_duration: + votingRewardParameters.rewardRateTransitionDuration !== undefined + ? [fromDuration(votingRewardParameters.rewardRateTransitionDuration)] + : [], + initial_reward_rate: + votingRewardParameters.initialRewardRate !== undefined + ? [fromPercentage(votingRewardParameters.initialRewardRate)] + : [], + final_reward_rate: + votingRewardParameters.finalRewardRate !== undefined + ? [fromPercentage(votingRewardParameters.finalRewardRate)] + : [], +}); + +const fromLedgerParameters = ( + ledgerParameters: LedgerParameters +): RawLedgerParameters => ({ + transaction_fee: + ledgerParameters.transactionFee !== undefined + ? [fromTokens(ledgerParameters.transactionFee)] + : [], + token_symbol: + ledgerParameters.tokenSymbol !== undefined + ? [ledgerParameters.tokenSymbol] + : [], + token_logo: + ledgerParameters.tokenLogo !== undefined + ? [fromImage(ledgerParameters.tokenLogo)] + : [], + token_name: + ledgerParameters.tokenName !== undefined + ? [ledgerParameters.tokenName] + : [], +}); + +const fromSwapParameters = ( + swapParameters: SwapParameters +): RawSwapParameters => ({ + minimum_participants: + swapParameters.minimumParticipants !== undefined + ? [swapParameters.minimumParticipants] + : [], + neuron_basket_construction_parameters: + swapParameters.neuronBasketConstructionParameters !== undefined + ? [ + fromNeuronBasketConstructionParameters( + swapParameters.neuronBasketConstructionParameters + ), + ] + : [], + maximum_participant_icp: + swapParameters.maximumParticipantIcp !== undefined + ? [fromTokens(swapParameters.maximumParticipantIcp)] + : [], + minimum_icp: + swapParameters.minimumIcp !== undefined + ? [fromTokens(swapParameters.minimumIcp)] + : [], + minimum_participant_icp: + swapParameters.minimumParticipantIcp !== undefined + ? [fromTokens(swapParameters.minimumParticipantIcp)] + : [], + maximum_icp: + swapParameters.maximumIcp !== undefined + ? [fromTokens(swapParameters.maximumIcp)] + : [], +}); + +const fromNeuronBasketConstructionParameters = ( + neuronBasketConstructionParameters: NeuronBasketConstructionParameters +): RawNeuronBasketConstructionParameters => ({ + dissolve_delay_interval: + neuronBasketConstructionParameters.dissolveDelayInterval !== undefined + ? [fromDuration(neuronBasketConstructionParameters.dissolveDelayInterval)] + : [], + count: + neuronBasketConstructionParameters.count !== undefined + ? [neuronBasketConstructionParameters.count] + : [], +}); + +const fromGovernanceParameters = ( + governanceParameters: GovernanceParameters +): RawGovernanceParameters => ({ + neuron_maximum_dissolve_delay_bonus: + governanceParameters.neuronMaximumDissolveDelayBonus !== undefined + ? [fromPercentage(governanceParameters.neuronMaximumDissolveDelayBonus)] + : [], + neuron_maximum_age_for_age_bonus: + governanceParameters.neuronMaximumAgeForAgeBonus !== undefined + ? [fromDuration(governanceParameters.neuronMaximumAgeForAgeBonus)] + : [], + neuron_maximum_dissolve_delay: + governanceParameters.neuronMaximumDissolveDelay !== undefined + ? [fromDuration(governanceParameters.neuronMaximumDissolveDelay)] + : [], + neuron_minimum_dissolve_delay_to_vote: + governanceParameters.neuronMinimumDissolveDelayToVote !== undefined + ? [fromDuration(governanceParameters.neuronMinimumDissolveDelayToVote)] + : [], + neuron_maximum_age_bonus: + governanceParameters.neuronMaximumAgeBonus !== undefined + ? [fromPercentage(governanceParameters.neuronMaximumAgeBonus)] + : [], + neuron_minimum_stake: + governanceParameters.neuronMinimumStake !== undefined + ? [fromTokens(governanceParameters.neuronMinimumStake)] + : [], + proposal_wait_for_quiet_deadline_increase: + governanceParameters.proposalWaitForQuietDeadlineIncrease !== undefined + ? [ + fromDuration( + governanceParameters.proposalWaitForQuietDeadlineIncrease + ), + ] + : [], + proposal_initial_voting_period: + governanceParameters.proposalInitialVotingPeriod !== undefined + ? [fromDuration(governanceParameters.proposalInitialVotingPeriod)] + : [], + proposal_rejection_fee: + governanceParameters.proposalRejectionFee !== undefined + ? [fromTokens(governanceParameters.proposalRejectionFee)] + : [], + voting_reward_parameters: + governanceParameters.votingRewardParameters !== undefined + ? [ + fromVotingRewardParameters( + governanceParameters.votingRewardParameters + ), + ] + : [], +}); + +const fromSwapDistribution = ( + swapDistribution: SwapDistribution +): RawSwapDistribution => ({ + total: + swapDistribution.total !== undefined + ? [fromTokens(swapDistribution.total)] + : [], +}); + +const fromInitialTokenDistribution = ( + initialTokenDistribution: InitialTokenDistribution +): RawInitialTokenDistribution => ({ + treasury_distribution: + initialTokenDistribution.treasuryDistribution !== undefined + ? [fromSwapDistribution(initialTokenDistribution.treasuryDistribution)] + : [], + developer_distribution: + initialTokenDistribution.developerDistribution !== undefined + ? [ + fromDeveloperDistribution( + initialTokenDistribution.developerDistribution + ), + ] + : [], + swap_distribution: + initialTokenDistribution.swapDistribution !== undefined + ? [fromSwapDistribution(initialTokenDistribution.swapDistribution)] + : [], +}); + +const fromNeuronDistribution = ( + neuronDistribution: NeuronDistribution +): RawNeuronDistribution => ({ + controller: + neuronDistribution.controller !== undefined + ? [Principal.fromText(neuronDistribution.controller)] + : [], + dissolve_delay: + neuronDistribution.dissolveDelay !== undefined + ? [fromDuration(neuronDistribution.dissolveDelay)] + : [], + memo: neuronDistribution.memo !== undefined ? [neuronDistribution.memo] : [], + vesting_period: + neuronDistribution.vestingPeriod !== undefined + ? [fromDuration(neuronDistribution.vestingPeriod)] + : [], + stake: + neuronDistribution.stake !== undefined + ? [fromTokens(neuronDistribution.stake)] + : [], +}); + +const fromDeveloperDistribution = ( + developerDistribution: DeveloperDistribution +): RawDeveloperDistribution => ({ + developer_neurons: developerDistribution.developerNeurons.map( + fromNeuronDistribution + ), +}); + +const fromCreateServiceNervousSystem = ( + createServiceNervousSystem: CreateServiceNervousSystem +): RawCreateServiceNervousSystem => ({ + url: + createServiceNervousSystem.url !== undefined + ? [createServiceNervousSystem.url] + : [], + governance_parameters: + createServiceNervousSystem.governanceParameters !== undefined + ? [ + fromGovernanceParameters( + createServiceNervousSystem.governanceParameters + ), + ] + : [], + fallback_controller_principal_ids: + createServiceNervousSystem.fallbackControllerPrincipalIds.map( + Principal.fromText + ), + logo: + createServiceNervousSystem.logo !== undefined + ? [fromImage(createServiceNervousSystem.logo)] + : [], + name: + createServiceNervousSystem.name !== undefined + ? [createServiceNervousSystem.name] + : [], + ledger_parameters: + createServiceNervousSystem.ledgerParameters !== undefined + ? [fromLedgerParameters(createServiceNervousSystem.ledgerParameters)] + : [], + description: + createServiceNervousSystem.description !== undefined + ? [createServiceNervousSystem.description] + : [], + dapp_canisters: createServiceNervousSystem.dappCanisters.map( + (principalId) => ({ + id: [Principal.fromText(principalId)], + }) + ), + swap_parameters: + createServiceNervousSystem.swapParameters !== undefined + ? [fromSwapParameters(createServiceNervousSystem.swapParameters)] + : [], + initial_token_distribution: + createServiceNervousSystem.initialTokenDistribution !== undefined + ? [ + fromInitialTokenDistribution( + createServiceNervousSystem.initialTokenDistribution + ), + ] + : [], +}); + const fromAction = (action: Action): RawAction => { if ("ExecuteNnsFunction" in action) { const executeNnsFunction = action.ExecuteNnsFunction; @@ -258,8 +555,11 @@ const fromAction = (action: Action): RawAction => { } if ("CreateServiceNervousSystem" in action) { - // TODO: Convert from CreateServiceNervousSystem to RawCreateServiceNervousSystem - return action; + return { + CreateServiceNervousSystem: fromCreateServiceNervousSystem( + action.CreateServiceNervousSystem + ), + }; } // If there's a missing action, this line will cause a compiler error. diff --git a/packages/nns/src/canisters/governance/response.converters.ts b/packages/nns/src/canisters/governance/response.converters.ts index e65149b2..f7ae6f1a 100644 --- a/packages/nns/src/canisters/governance/response.converters.ts +++ b/packages/nns/src/canisters/governance/response.converters.ts @@ -7,6 +7,7 @@ import type { } from "@dfinity/nns-proto"; import { Principal } from "@dfinity/principal"; import { fromNullable, uint8ArrayToArrayOfNumber } from "@dfinity/utils"; +import { fromDefinedNullable } from "@dfinity/utils/src"; import type { Map } from "google-protobuf"; import type { AccountIdentifier as RawAccountIdentifier, @@ -15,30 +16,45 @@ import type { Ballot as RawBallot, BallotInfo as RawBallotInfo, By as RawBy, + Canister as RawCanister, Change as RawChange, Command as RawCommand, + DeveloperDistribution as RawDeveloperDistribution, DissolveState as RawDissolveState, + Duration as RawDuration, Followees as RawFollowees, + GovernanceParameters as RawGovernanceParameters, + Image as RawImage, + InitialTokenDistribution as RawInitialTokenDistribution, KnownNeuron as RawKnownNeuron, + LedgerParameters as RawLedgerParameters, ListNeuronsResponse as RawListNeuronsResponse, ListProposalInfoResponse as RawListProposalInfoResponse, Neuron as RawNeuron, + NeuronBasketConstructionParameters as RawNeuronBasketConstructionParameters, + NeuronDistribution as RawNeuronDistribution, NeuronId as RawNeuronId, NeuronIdOrSubaccount as RawNeuronIdOrSubaccount, NeuronInfo as RawNeuronInfo, NodeProvider as RawNodeProvider, Operation as RawOperation, Params, + Percentage as RawPercentage, Proposal as RawProposal, ProposalInfo as RawProposalInfo, RewardMode as RawRewardMode, + SwapDistribution as RawSwapDistribution, + SwapParameters as RawSwapParameters, Tally as RawTally, + Tokens as RawTokens, + VotingRewardParameters as RawVotingRewardParameters, } from "../../../candid/governance"; import { AccountIdentifier, SubAccount } from "../../account_identifier"; import { NeuronState } from "../../enums/governance.enums"; import { UnsupportedValueError } from "../../errors/governance.errors"; import type { AccountIdentifier as AccountIdentifierString, + CanisterIdString, E8s, NeuronId, } from "../../types/common"; @@ -49,19 +65,32 @@ import type { By, Change, Command, + DeveloperDistribution, DissolveState, + Duration, Followees, + GovernanceParameters, + Image, + InitialTokenDistribution, KnownNeuron, + LedgerParameters, ListProposalsResponse, Neuron, + NeuronBasketConstructionParameters, + NeuronDistribution, NeuronIdOrSubaccount, NeuronInfo, NodeProvider, Operation, + Percentage, Proposal, ProposalInfo, RewardMode, + SwapDistribution, + SwapParameters, Tally, + Tokens, + VotingRewardParameters, } from "../../types/governance_converters"; import { accountIdentifierFromBytes, @@ -393,8 +422,35 @@ const toAction = (action: RawAction): Action => { } if ("CreateServiceNervousSystem" in action) { - // TODO: Convert from RawCreateServiceNervousSystem to CreateServiceNervousSystem (no arrays as optionals) - return action; + const createServiceNervousSystem = action.CreateServiceNervousSystem; + return { + CreateServiceNervousSystem: { + url: fromNullable(createServiceNervousSystem.url), + governanceParameters: toGovernanceParameters( + fromNullable(createServiceNervousSystem.governance_parameters) + ), + fallbackControllerPrincipalIds: + createServiceNervousSystem.fallback_controller_principal_ids.map( + (principalId) => principalId.toString() + ), + logo: toImage(fromNullable(createServiceNervousSystem.logo)), + name: fromNullable(createServiceNervousSystem.name), + ledgerParameters: toLedgerParameters( + fromNullable(createServiceNervousSystem.ledger_parameters) + ), + description: fromNullable(createServiceNervousSystem.description), + dappCanisters: + (createServiceNervousSystem.dapp_canisters.map( + toCanisterIdString + ) as CanisterIdString[]) ?? [], + swapParameters: toSwapParameters( + fromNullable(createServiceNervousSystem.swap_parameters) + ), + initialTokenDistribution: toInitialTokenDistribution( + fromNullable(createServiceNervousSystem.initial_token_distribution) + ), + }, + }; } throw new UnsupportedValueError(action); @@ -914,3 +970,225 @@ export const convertPbNeuronToNeuronInfo = : convertPbNeuronToFullNeuron({ pbNeuron, pbNeuronInfo, canisterId }), }; }; + +const toPercentage = ( + percentage: RawPercentage | undefined +): Percentage | undefined => { + return percentage === undefined + ? undefined + : { + basisPoints: fromNullable(percentage.basis_points), + }; +}; + +const toDuration = ( + duration: RawDuration | undefined +): Duration | undefined => { + return duration === undefined + ? undefined + : { + seconds: fromNullable(duration.seconds), + }; +}; + +const toTokens = (tokens: RawTokens | undefined): Tokens | undefined => { + return tokens === undefined + ? undefined + : { + e8s: fromNullable(tokens.e8s), + }; +}; + +const toCanisterIdString = ( + canister: RawCanister | undefined +): CanisterIdString | undefined => { + return canister === undefined + ? undefined + : canister.id.length === 0 + ? undefined + : fromDefinedNullable(canister.id).toString(); +}; + +const toImage = (image: RawImage | undefined): Image | undefined => { + return image === undefined + ? undefined + : { + base64Encoding: fromNullable(image.base64_encoding), + }; +}; + +const toLedgerParameters = ( + ledgerParameters: RawLedgerParameters | undefined +): LedgerParameters | undefined => { + return ledgerParameters === undefined + ? undefined + : { + transactionFee: toTokens( + fromNullable(ledgerParameters.transaction_fee) + ), + tokenSymbol: fromNullable(ledgerParameters.token_symbol), + tokenLogo: toImage(fromNullable(ledgerParameters.token_logo)), + tokenName: fromNullable(ledgerParameters.token_name), + }; +}; + +const toVotingRewardParameters = ( + votingRewardParameters: RawVotingRewardParameters | undefined +): VotingRewardParameters | undefined => { + return votingRewardParameters === undefined + ? undefined + : { + rewardRateTransitionDuration: toDuration( + fromNullable(votingRewardParameters.reward_rate_transition_duration) + ), + initialRewardRate: toPercentage( + fromNullable(votingRewardParameters.initial_reward_rate) + ), + finalRewardRate: toPercentage( + fromNullable(votingRewardParameters.final_reward_rate) + ), + }; +}; + +const toGovernanceParameters = ( + governanceParameters: RawGovernanceParameters | undefined +): GovernanceParameters | undefined => { + return governanceParameters === undefined + ? undefined + : { + neuronMaximumDissolveDelayBonus: toPercentage( + fromNullable(governanceParameters.neuron_maximum_dissolve_delay_bonus) + ), + neuronMaximumAgeForAgeBonus: toDuration( + fromNullable(governanceParameters.neuron_maximum_age_for_age_bonus) + ), + neuronMaximumDissolveDelay: toDuration( + fromNullable(governanceParameters.neuron_maximum_dissolve_delay) + ), + neuronMinimumDissolveDelayToVote: toDuration( + fromNullable( + governanceParameters.neuron_minimum_dissolve_delay_to_vote + ) + ), + neuronMaximumAgeBonus: toPercentage( + fromNullable(governanceParameters.neuron_maximum_age_bonus) + ), + neuronMinimumStake: toTokens( + fromNullable(governanceParameters.neuron_minimum_stake) + ), + proposalWaitForQuietDeadlineIncrease: toDuration( + fromNullable( + governanceParameters.proposal_wait_for_quiet_deadline_increase + ) + ), + proposalInitialVotingPeriod: toDuration( + fromNullable(governanceParameters.proposal_initial_voting_period) + ), + proposalRejectionFee: toTokens( + fromNullable(governanceParameters.proposal_rejection_fee) + ), + votingRewardParameters: toVotingRewardParameters( + fromNullable(governanceParameters.voting_reward_parameters) + ), + }; +}; + +const toNeuronBasketConstructionParameters = ( + neuronBasketConstructionParameters: + | RawNeuronBasketConstructionParameters + | undefined +): NeuronBasketConstructionParameters | undefined => { + return neuronBasketConstructionParameters === undefined + ? undefined + : { + dissolveDelayInterval: toDuration( + fromNullable( + neuronBasketConstructionParameters.dissolve_delay_interval + ) + ), + count: fromNullable(neuronBasketConstructionParameters.count), + }; +}; + +const toSwapParameters = ( + swapParameters: RawSwapParameters | undefined +): SwapParameters | undefined => { + return swapParameters === undefined + ? undefined + : { + minimumParticipants: fromNullable(swapParameters.minimum_participants), + neuronBasketConstructionParameters: + toNeuronBasketConstructionParameters( + fromNullable(swapParameters.neuron_basket_construction_parameters) + ), + maximumParticipantIcp: toTokens( + fromNullable(swapParameters.maximum_participant_icp) + ), + minimumIcp: toTokens(fromNullable(swapParameters.minimum_icp)), + minimumParticipantIcp: toTokens( + fromNullable(swapParameters.minimum_participant_icp) + ), + maximumIcp: toTokens(fromNullable(swapParameters.maximum_icp)), + }; +}; + +const toSwapDistribution = ( + swapDistribution: RawSwapDistribution | undefined +): SwapDistribution | undefined => { + return swapDistribution === undefined + ? undefined + : { + total: toTokens(fromNullable(swapDistribution.total)), + }; +}; + +const toNeuronDistribution = ( + neuronDistribution: RawNeuronDistribution | undefined +): NeuronDistribution | undefined => { + return neuronDistribution === undefined + ? undefined + : { + controller: + neuronDistribution.controller.length === 0 + ? undefined + : neuronDistribution.controller[0].toString(), + dissolveDelay: toDuration( + fromNullable(neuronDistribution.dissolve_delay) + ), + memo: fromNullable(neuronDistribution.memo), + vestingPeriod: toDuration( + fromNullable(neuronDistribution.vesting_period) + ), + stake: toTokens(fromNullable(neuronDistribution.stake)), + }; +}; + +const toDeveloperDistribution = ( + developerDistribution: RawDeveloperDistribution | undefined +): DeveloperDistribution | undefined => { + return developerDistribution === undefined + ? undefined + : { + developerNeurons: developerDistribution.developer_neurons.map( + toNeuronDistribution + ) as Array, + }; +}; + +const toInitialTokenDistribution = ( + initialTokenDistribution: RawInitialTokenDistribution | undefined +): InitialTokenDistribution | undefined => { + return initialTokenDistribution === undefined + ? undefined + : { + treasuryDistribution: toSwapDistribution( + fromNullable(initialTokenDistribution.treasury_distribution) + ), + developerDistribution: toDeveloperDistribution( + fromNullable(initialTokenDistribution.developer_distribution) + ), + swapDistribution: toSwapDistribution( + fromNullable(initialTokenDistribution.swap_distribution) + ), + }; +}; diff --git a/packages/nns/src/types/governance_converters.ts b/packages/nns/src/types/governance_converters.ts index 19f41d38..8c9b67db 100644 --- a/packages/nns/src/types/governance_converters.ts +++ b/packages/nns/src/types/governance_converters.ts @@ -1,6 +1,5 @@ import type { DerEncodedPublicKey } from "@dfinity/agent"; import type { Principal } from "@dfinity/principal"; -import type { CreateServiceNervousSystem } from "../../candid/governance"; import type { NeuronState, ProposalRewardStatus, @@ -22,7 +21,6 @@ export type Action = | { ExecuteNnsFunction: ExecuteNnsFunction; } - // TODO: Create new CreateServiceNervousSystem type (don't use array for optional fields) | { CreateServiceNervousSystem: CreateServiceNervousSystem } | { ManageNeuron: ManageNeuron } | { ApproveGenesisKyc: ApproveGenesisKyc } @@ -490,3 +488,93 @@ export interface MakeExecuteNnsFunctionProposalRequest { export interface ListNodeProvidersResponse { nodeProviders: NodeProvider[]; } + +export interface Percentage { + basisPoints?: bigint; +} + +export interface Duration { + seconds?: bigint; +} + +export interface Tokens { + e8s?: bigint; +} + +export interface Image { + base64Encoding?: string; +} + +export interface LedgerParameters { + transactionFee?: Tokens; + tokenSymbol?: string; + tokenLogo?: Image; + tokenName?: string; +} + +export interface VotingRewardParameters { + rewardRateTransitionDuration?: Duration; + initialRewardRate?: Percentage; + finalRewardRate?: Percentage; +} + +export interface GovernanceParameters { + neuronMaximumDissolveDelayBonus?: Percentage; + neuronMaximumAgeForAgeBonus?: Duration; + neuronMaximumDissolveDelay?: Duration; + neuronMinimumDissolveDelayToVote?: Duration; + neuronMaximumAgeBonus?: Percentage; + neuronMinimumStake?: Tokens; + proposalWaitForQuietDeadlineIncrease?: Duration; + proposalInitialVotingPeriod?: Duration; + proposalRejectionFee?: Tokens; + votingRewardParameters?: VotingRewardParameters; +} + +export interface NeuronBasketConstructionParameters { + dissolveDelayInterval?: Duration; + count?: bigint; +} +export interface SwapParameters { + minimumParticipants?: bigint; + neuronBasketConstructionParameters?: NeuronBasketConstructionParameters; + maximumParticipantIcp?: Tokens; + minimumIcp?: Tokens; + minimumParticipantIcp?: Tokens; + maximumIcp?: Tokens; +} + +export interface SwapDistribution { + total?: Tokens; +} + +export interface NeuronDistribution { + controller?: PrincipalString; + dissolveDelay?: Duration; + memo?: bigint; + vestingPeriod?: Duration; + stake?: Tokens; +} + +export interface DeveloperDistribution { + developerNeurons: Array; +} + +export interface InitialTokenDistribution { + treasuryDistribution?: SwapDistribution; + developerDistribution?: DeveloperDistribution; + swapDistribution?: SwapDistribution; +} + +export interface CreateServiceNervousSystem { + url?: string; + governanceParameters?: GovernanceParameters; + fallbackControllerPrincipalIds: Array; + logo?: Image; + name?: string; + ledgerParameters?: LedgerParameters; + description?: string; + dappCanisters: Array; + swapParameters?: SwapParameters; + initialTokenDistribution?: InitialTokenDistribution; +}