Skip to content

Commit e2eae97

Browse files
feat(nns): Define API for disburse maturity (#4138)
# Why Similar to SNS, we'd like to support disbursing maturity instead of spawning neurons from it, since most of the time the users just want the minted ICP by converting the maturity. # What * Define a new manage neuron command `DisburseMaturity` and corresponding response, in .proto, .rs and .did * Add `#[serde(with = "serde_bytes")]` to the subaccount bytes in the API type
1 parent 4050fa7 commit e2eae97

File tree

8 files changed

+241
-2
lines changed

8 files changed

+241
-2
lines changed

rs/nns/governance/api/src/ic_nns_governance.pb.v1.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,29 @@ pub mod manage_neuron {
10811081
)]
10821082
pub struct RefreshVotingPower {}
10831083

1084+
/// Disburse the maturity of a neuron to any ledger account. If an account
1085+
/// is not specified, the caller's account will be used. The caller can choose
1086+
/// a percentage of the current maturity to disburse to the ledger account. The
1087+
/// resulting amount to disburse must be greater than or equal to the
1088+
/// transaction fee.
1089+
#[derive(
1090+
candid::CandidType,
1091+
candid::Deserialize,
1092+
serde::Serialize,
1093+
comparable::Comparable,
1094+
Clone,
1095+
PartialEq,
1096+
::prost::Message,
1097+
)]
1098+
pub struct DisburseMaturity {
1099+
/// The percentage to disburse, from 1 to 100
1100+
#[prost(uint32, tag = "1")]
1101+
pub percentage_to_disburse: u32,
1102+
/// The (optional) principal to which to transfer the stake.
1103+
#[prost(message, optional, tag = "2")]
1104+
pub to_account: ::core::option::Option<super::Account>,
1105+
}
1106+
10841107
/// The ID of the neuron to manage. This can either be a subaccount or a neuron ID.
10851108
#[derive(candid::CandidType, candid::Deserialize, serde::Serialize, comparable::Comparable)]
10861109
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -1124,6 +1147,8 @@ pub mod manage_neuron {
11241147
StakeMaturity(StakeMaturity),
11251148
#[prost(message, tag = "16")]
11261149
RefreshVotingPower(RefreshVotingPower),
1150+
#[prost(message, tag = "17")]
1151+
DisburseMaturity(DisburseMaturity),
11271152
// KEEP THIS IN SYNC WITH ManageNeuronCommandRequest!
11281153
}
11291154
}
@@ -1254,6 +1279,21 @@ pub mod manage_neuron_response {
12541279
)]
12551280
pub struct RefreshVotingPowerResponse {}
12561281

1282+
#[derive(
1283+
candid::CandidType,
1284+
candid::Deserialize,
1285+
serde::Serialize,
1286+
comparable::Comparable,
1287+
Clone,
1288+
Copy,
1289+
PartialEq,
1290+
::prost::Message,
1291+
)]
1292+
pub struct DisburseMaturityResponse {
1293+
#[prost(uint64, optional, tag = "1")]
1294+
amount_disbursed_e8s: Option<u64>,
1295+
}
1296+
12571297
#[derive(candid::CandidType, candid::Deserialize, serde::Serialize, comparable::Comparable)]
12581298
#[allow(clippy::derive_partial_eq_without_eq)]
12591299
#[derive(Clone, PartialEq, ::prost::Oneof)]
@@ -1286,6 +1326,8 @@ pub mod manage_neuron_response {
12861326
StakeMaturity(StakeMaturityResponse),
12871327
#[prost(message, tag = "14")]
12881328
RefreshVotingPower(RefreshVotingPowerResponse),
1329+
#[prost(message, tag = "15")]
1330+
DisburseMaturity(DisburseMaturityResponse),
12891331
}
12901332

12911333
// Below, we should remove `manage_neuron_response::`, but that should be
@@ -1545,6 +1587,8 @@ pub enum ManageNeuronCommandRequest {
15451587
StakeMaturity(manage_neuron::StakeMaturity),
15461588
#[prost(message, tag = "16")]
15471589
RefreshVotingPower(manage_neuron::RefreshVotingPower),
1590+
#[prost(message, tag = "17")]
1591+
DisburseMaturity(manage_neuron::DisburseMaturity),
15481592
// KEEP THIS IN SYNC WITH manage_neuron::Command!
15491593
}
15501594

@@ -4181,6 +4225,30 @@ pub mod restore_aging_summary {
41814225
}
41824226
}
41834227
}
4228+
4229+
/// A Ledger account identified by the owner of the account `of` and
4230+
/// the `subaccount`. If the `subaccount` is not specified then the default
4231+
/// one is used.
4232+
#[derive(
4233+
candid::CandidType,
4234+
candid::Deserialize,
4235+
serde::Serialize,
4236+
comparable::Comparable,
4237+
Clone,
4238+
PartialEq,
4239+
::prost::Message,
4240+
)]
4241+
pub struct Account {
4242+
/// The owner of the account.
4243+
#[prost(message, optional, tag = "1")]
4244+
pub owner: ::core::option::Option<::ic_base_types::PrincipalId>,
4245+
/// The subaccount of the account. If not set then the default
4246+
/// subaccount (all bytes set to 0) is used.
4247+
#[prost(message, optional, tag = "2")]
4248+
#[serde(deserialize_with = "ic_utils::deserialize::deserialize_option_blob")]
4249+
pub subaccount: ::core::option::Option<Vec<u8>>,
4250+
}
4251+
41844252
/// Proposal types are organized into topics. Neurons can automatically
41854253
/// vote based on following other neurons, and these follow
41864254
/// relationships are defined per topic.

rs/nns/governance/api/src/proposal_submission_helpers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ impl From<ManageNeuronCommandRequest> for Command {
139139
ManageNeuronCommandRequest::Merge(v) => Command::Merge(v),
140140
ManageNeuronCommandRequest::StakeMaturity(v) => Command::StakeMaturity(v),
141141
ManageNeuronCommandRequest::RefreshVotingPower(v) => Command::RefreshVotingPower(v),
142+
ManageNeuronCommandRequest::DisburseMaturity(v) => Command::DisburseMaturity(v),
142143
}
143144
}
144145
}

rs/nns/governance/canister/governance.did

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,20 @@ type RefreshVotingPowerResponse = record {
123123
// minimal until we discover there is a "real need". YAGNI.
124124
};
125125

126+
type DisburseMaturity = record {
127+
percentage_to_disburse : nat32;
128+
to_account : opt Account;
129+
};
130+
131+
type Account = record {
132+
owner: opt principal;
133+
subaccount: opt blob;
134+
};
135+
136+
type DisburseMaturityResponse = record {
137+
amount_disbursed_e8s : opt nat64;
138+
};
139+
126140
// KEEP THIS IN SYNC WITH ManageNeuronCommandRequest!
127141
type Command = variant {
128142
Spawn : Spawn;
@@ -138,6 +152,7 @@ type Command = variant {
138152
MergeMaturity : MergeMaturity;
139153
Disburse : Disburse;
140154
RefreshVotingPower : RefreshVotingPower;
155+
DisburseMaturity : DisburseMaturity;
141156

142157
// KEEP THIS IN SYNC WITH ManageNeuronCommandRequest!
143158
};
@@ -157,6 +172,7 @@ type Command_1 = variant {
157172
MergeMaturity : MergeMaturityResponse;
158173
Disburse : DisburseResponse;
159174
RefreshVotingPower : RefreshVotingPowerResponse;
175+
DisburseMaturity : DisburseMaturityResponse;
160176
};
161177

162178
type Command_2 = variant {
@@ -540,6 +556,7 @@ type ManageNeuronCommandRequest = variant {
540556
MergeMaturity : MergeMaturity;
541557
Disburse : Disburse;
542558
RefreshVotingPower : RefreshVotingPower;
559+
DisburseMaturity : DisburseMaturity;
543560

544561
// KEEP THIS IN SYNC WITH COMMAND!
545562
};

rs/nns/governance/canister/governance_test.did

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,20 @@ type RefreshVotingPowerResponse = record {
123123
// minimal until we discover there is a "real need". YAGNI.
124124
};
125125

126+
type DisburseMaturity = record {
127+
percentage_to_disburse : nat32;
128+
to_account : opt Account;
129+
};
130+
131+
type Account = record {
132+
owner: opt principal;
133+
subaccount: opt blob;
134+
};
135+
136+
type DisburseMaturityResponse = record {
137+
amount_disbursed_e8s : opt nat64;
138+
};
139+
126140
type Command = variant {
127141
Spawn : Spawn;
128142
Split : Split;
@@ -137,6 +151,7 @@ type Command = variant {
137151
MergeMaturity : MergeMaturity;
138152
Disburse : Disburse;
139153
RefreshVotingPower : RefreshVotingPower;
154+
DisburseMaturity : DisburseMaturity;
140155
};
141156

142157
type Command_1 = variant {
@@ -154,6 +169,7 @@ type Command_1 = variant {
154169
MergeMaturity : MergeMaturityResponse;
155170
Disburse : DisburseResponse;
156171
RefreshVotingPower : RefreshVotingPowerResponse;
172+
DisburseMaturity : DisburseMaturityResponse;
157173
};
158174

159175
type Command_2 = variant {
@@ -517,6 +533,7 @@ type ManageNeuronCommandRequest = variant {
517533
MergeMaturity : MergeMaturity;
518534
Disburse : Disburse;
519535
RefreshVotingPower : RefreshVotingPower;
536+
DisburseMaturity : DisburseMaturity;
520537
};
521538

522539
type ManageNeuronRequest = record {

rs/nns/governance/proto/ic_nns_governance/pb/v1/governance.proto

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,19 @@ message ManageNeuron {
11001100
// fields in Neuron.
11011101
message RefreshVotingPower {}
11021102

1103+
// Disburse the maturity of a neuron to any ledger account. If an account
1104+
// is not specified, the caller's account will be used. The caller can choose
1105+
// a percentage of the current maturity to disburse to the ledger account. The
1106+
// resulting amount to disburse must be greater than or equal to the
1107+
// transaction fee.
1108+
message DisburseMaturity {
1109+
// The percentage to disburse, from 1 to 100
1110+
uint32 percentage_to_disburse = 1;
1111+
1112+
// The (optional) principal to which to transfer the stake.
1113+
Account to_account = 2;
1114+
}
1115+
11031116
oneof command {
11041117
Configure configure = 2;
11051118
Disburse disburse = 3;
@@ -1114,6 +1127,7 @@ message ManageNeuron {
11141127
Merge merge = 14;
11151128
StakeMaturity stake_maturity = 15;
11161129
RefreshVotingPower refresh_voting_power = 16;
1130+
DisburseMaturity disburse_maturity = 17;
11171131
}
11181132
}
11191133

@@ -2717,3 +2731,20 @@ message ProposalVotingStateMachine {
27172731
repeated ic_nns_common.pb.v1.NeuronId followers_to_check = 4;
27182732
map<uint64, Vote> recent_neuron_ballots_to_record = 5;
27192733
}
2734+
2735+
// A Ledger subaccount.
2736+
message Subaccount {
2737+
bytes subaccount = 1;
2738+
}
2739+
2740+
// A Ledger account identified by the owner of the account `of` and
2741+
// the `subaccount`. If the `subaccount` is not specified then the default
2742+
// one is used.
2743+
message Account {
2744+
// The owner of the account.
2745+
ic_base_types.pb.v1.PrincipalId owner = 1;
2746+
2747+
// The subaccount of the account. If not set then the default
2748+
// subaccount (all bytes set to 0) is used.
2749+
optional Subaccount subaccount = 2;
2750+
}

rs/nns/governance/src/gen/ic_nns_governance.pb.v1.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ pub struct ManageNeuron {
780780
pub neuron_id_or_subaccount: ::core::option::Option<manage_neuron::NeuronIdOrSubaccount>,
781781
#[prost(
782782
oneof = "manage_neuron::Command",
783-
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15, 16"
783+
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15, 16, 17"
784784
)]
785785
pub command: ::core::option::Option<manage_neuron::Command>,
786786
}
@@ -1270,6 +1270,28 @@ pub mod manage_neuron {
12701270
::prost::Message,
12711271
)]
12721272
pub struct RefreshVotingPower {}
1273+
/// Disburse the maturity of a neuron to any ledger account. If an account
1274+
/// is not specified, the caller's account will be used. The caller can choose
1275+
/// a percentage of the current maturity to disburse to the ledger account. The
1276+
/// resulting amount to disburse must be greater than or equal to the
1277+
/// transaction fee.
1278+
#[derive(
1279+
candid::CandidType,
1280+
candid::Deserialize,
1281+
serde::Serialize,
1282+
comparable::Comparable,
1283+
Clone,
1284+
PartialEq,
1285+
::prost::Message,
1286+
)]
1287+
pub struct DisburseMaturity {
1288+
/// The percentage to disburse, from 1 to 100
1289+
#[prost(uint32, tag = "1")]
1290+
pub percentage_to_disburse: u32,
1291+
/// The (optional) principal to which to transfer the stake.
1292+
#[prost(message, optional, tag = "2")]
1293+
pub to_account: ::core::option::Option<super::Account>,
1294+
}
12731295
/// The ID of the neuron to manage. This can either be a subaccount or a neuron ID.
12741296
#[derive(
12751297
candid::CandidType,
@@ -1323,6 +1345,8 @@ pub mod manage_neuron {
13231345
StakeMaturity(StakeMaturity),
13241346
#[prost(message, tag = "16")]
13251347
RefreshVotingPower(RefreshVotingPower),
1348+
#[prost(message, tag = "17")]
1349+
DisburseMaturity(DisburseMaturity),
13261350
}
13271351
}
13281352
#[derive(candid::CandidType, candid::Deserialize, serde::Serialize, comparable::Comparable)]
@@ -4214,6 +4238,41 @@ pub struct ProposalVotingStateMachine {
42144238
#[prost(map = "uint64, enumeration(Vote)", tag = "5")]
42154239
pub recent_neuron_ballots_to_record: ::std::collections::HashMap<u64, i32>,
42164240
}
4241+
/// A Ledger subaccount.
4242+
#[derive(
4243+
candid::CandidType,
4244+
candid::Deserialize,
4245+
serde::Serialize,
4246+
comparable::Comparable,
4247+
Clone,
4248+
PartialEq,
4249+
::prost::Message,
4250+
)]
4251+
pub struct Subaccount {
4252+
#[prost(bytes = "vec", tag = "1")]
4253+
pub subaccount: ::prost::alloc::vec::Vec<u8>,
4254+
}
4255+
/// A Ledger account identified by the owner of the account `of` and
4256+
/// the `subaccount`. If the `subaccount` is not specified then the default
4257+
/// one is used.
4258+
#[derive(
4259+
candid::CandidType,
4260+
candid::Deserialize,
4261+
serde::Serialize,
4262+
comparable::Comparable,
4263+
Clone,
4264+
PartialEq,
4265+
::prost::Message,
4266+
)]
4267+
pub struct Account {
4268+
/// The owner of the account.
4269+
#[prost(message, optional, tag = "1")]
4270+
pub owner: ::core::option::Option<::ic_base_types::PrincipalId>,
4271+
/// The subaccount of the account. If not set then the default
4272+
/// subaccount (all bytes set to 0) is used.
4273+
#[prost(message, optional, tag = "2")]
4274+
pub subaccount: ::core::option::Option<Subaccount>,
4275+
}
42174276
/// Proposal types are organized into topics. Neurons can automatically
42184277
/// vote based on following other neurons, and these follow
42194278
/// relationships are defined per topic.

rs/nns/governance/src/governance.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6332,6 +6332,10 @@ impl Governance {
63326332
Some(Command::RefreshVotingPower(_)) => self
63336333
.refresh_voting_power(&id, caller)
63346334
.map(ManageNeuronResponse::refresh_voting_power_response),
6335+
Some(Command::DisburseMaturity(_)) => Err(GovernanceError::new_with_message(
6336+
ErrorType::Unavailable,
6337+
"Disbursing maturity is not implemented yet.",
6338+
)),
63356339
None => panic!(),
63366340
}
63376341
}

0 commit comments

Comments
 (0)