@dfinity/nns-proto 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1547 @@
1
+ // This file contains the Protobuf definitions for the `swap` canister
2
+ // which can be used for an initial token swap (a.k.a. token swap or
3
+ // single price auction) to decentralise an application running on the
4
+ // Internet Computer, turning it into a decentralized application or
5
+ // "dapp". See the documentation of the `Swap` message for a high
6
+ // level overview.
7
+
8
+ syntax = "proto3";
9
+
10
+ package ic_sns_swap.pb.v1;
11
+
12
+ import "base_types.proto";
13
+ import "nervous_system.proto";
14
+
15
+ // Lifecycle states of the swap canister. The details of their meanings
16
+ // are provided in the documentation of the `Swap` message.
17
+ enum Lifecycle {
18
+ // The canister is incorrectly configured. Not a real lifecycle state.
19
+ LIFECYCLE_UNSPECIFIED = 0;
20
+ // In PENDING state, the canister is correctly initialized. Once SNS
21
+ // tokens have been transferred to the swap canister's account on
22
+ // the SNS ledger, a call to `open` with valid parameters will start
23
+ // the swap.
24
+ LIFECYCLE_PENDING = 1;
25
+ // In ADOPTED state, the proposal to start the decentralization swap
26
+ // has been adopted, and the swap will be automatically opened after a delay.
27
+ // In the legacy (non-one-proposal) flow, the swap delay is specified by
28
+ // params.sale_delay_seconds. In the one-proposal flow, the swap delay is
29
+ // specified by `init.swap_start_timestamp_seconds`.
30
+ LIFECYCLE_ADOPTED = 5;
31
+ // In OPEN state, prospective buyers can register for the token
32
+ // swap. The swap will be committed when the target (max) ICP has
33
+ // been reached or the swap's due date/time occurs, whichever
34
+ // happens first.
35
+ LIFECYCLE_OPEN = 2;
36
+ // In COMMITTED state the token price has been determined; on a call to
37
+ // finalize`, buyers receive their SNS neurons and the SNS governance canister
38
+ // receives the ICP.
39
+ LIFECYCLE_COMMITTED = 3;
40
+ // In ABORTED state the token swap has been aborted, e.g., because the due
41
+ // date/time occurred before the minimum (reserve) amount of ICP has been
42
+ // retrieved. On a call to `finalize`, participants get their ICP refunded.
43
+ LIFECYCLE_ABORTED = 4;
44
+ }
45
+
46
+ // The `swap` canister smart contract is used to perform a type of
47
+ // single-price auction (SNS/ICP) of one token type SNS for another token
48
+ // type ICP (this is typically ICP, but can be treated as a variable) at a
49
+ // specific date/time in the future.
50
+ //
51
+ // Such a single-price auction is typically used to decentralize an SNS,
52
+ // i.e., to ensure that a sufficient number of governance tokens of the
53
+ // SNS are distributed among different participants.
54
+ //
55
+ // State (lifecycle) diagram for the swap canister's state.
56
+ //
57
+ // ```text
58
+ // sufficient_participation
59
+ // && (swap_due || icp_target_reached)
60
+ // PENDING -------------------> ADOPTED ---------------------> OPEN -----------------------------------------> COMMITTED
61
+ // Swap receives a request The opening delay | |
62
+ // from NNS governance to has elapsed | not sufficient_participation |
63
+ // schedule opening | && (swap_due || icp_target_reached) |
64
+ // v v
65
+ // ABORTED ---------------------------------------> <DELETED>
66
+ // ```
67
+ //
68
+ // Here `sufficient_participation` means that the minimum number of
69
+ // participants `min_participants` has been reached, each contributing
70
+ // between `min_participant_icp_e8s` and `max_participant_icp_e8s`, and
71
+ // their total contributions add up to at least `min_icp_e8s` and at most
72
+ // `max_icp_e8s`.
73
+ //
74
+ // `icp_target_reached` means that the total amount of ICP contributed is
75
+ // equal to `max_icp_e8s`. (The total amount of ICP contributed should
76
+ // never be greater than `max_icp_e8s`.)
77
+ //
78
+ //
79
+ // The dramatis personae of the `swap` canister are as follows:
80
+ //
81
+ // - The swap canister itself.
82
+ //
83
+ // - The NNS governance canister - which is the only principal that can open
84
+ // the swap.
85
+ //
86
+ // - The governance canister of the SNS to be decentralized.
87
+ //
88
+ // - The ledger canister of the SNS, i.e., the ledger of the token type
89
+ // being sold.
90
+ //
91
+ // - The ICP ledger canister, or more generally of the base currency of
92
+ // the auction.
93
+ //
94
+ // - The root canister of the SNS to control aspects of the SNS not
95
+ // controlled by the SNS governance canister.
96
+ //
97
+ // When the swap canister is initialized, it must be configured with
98
+ // the canister IDs of the other participant canisters.
99
+ //
100
+ // The next step is to provide SNS tokens for the swap. This normally
101
+ // happens when the canister is in the PENDING state, and the amount
102
+ // is validated in the call to `open`.
103
+ //
104
+ // The request to open the swap has to originate from the NNS governance
105
+ // canister. The request specifies the parameters of the swap, i.e., the
106
+ // date/time at which the token swap will take place, the minimal number
107
+ // of participants, the minimum number of base tokens (ICP) of each
108
+ // participant, as well as the minimum and maximum number (reserve and
109
+ // target) of base tokens (ICP) of the swap.
110
+ //
111
+ // Step 0. The canister is created, specifying the initialization
112
+ // parameters, which are henceforth fixed for the lifetime of the
113
+ // canister.
114
+ //
115
+ // Step 1 (State PENDING). The swap canister is loaded with the right
116
+ // amount of SNS tokens. A call to `open` will then transition the
117
+ // canister to the OPEN state.
118
+ //
119
+ // Step 2a. (State ADOPTED). The field `params` is received as an argument
120
+ // to the call to `open` and is henceforth immutable. The amount of
121
+ // SNS token is verified against the SNS ledger. The swap will be
122
+ // opened after an optional delay. The transition to OPEN happens
123
+ // automatically (on the canister heartbeat) when the delay elapses.
124
+ //
125
+ // Step 2a. (State OPEN). The delay has elapsed and the swap is open
126
+ // for participants who can enter into the auction with a number of ICP
127
+ // tokens until either the target amount has been reached or the
128
+ // auction is due, i.e., the date/time of the auction has been
129
+ // reached. The transition to COMMITTED or ABORTED happens
130
+ // automatically (on the canister heartbeat) when the necessary
131
+ // conditions are fulfilled.
132
+ //
133
+ // Step 3a. (State COMMITTED). Tokens are allocated to participants at
134
+ // a single clearing price, i.e., the number of SNS tokens being
135
+ // offered divided by the total number of ICP tokens contributed to
136
+ // the swap. In this state, a call to `finalize` will create SNS
137
+ // neurons for each participant and transfer ICP to the SNS governance
138
+ // canister. The call to `finalize` does not happen automatically
139
+ // (i.e., on the canister heartbeat) so that there is a caller to
140
+ // respond to with potential errors.
141
+ //
142
+ // Step 3b. (State ABORTED). If the parameters of the swap have not
143
+ // been satisfied before the due date/time, the swap is aborted and
144
+ // the ICP tokens transferred back to their respective owners. The
145
+ // swap can also be aborted early if it is determined that the
146
+ // swap cannot possibly succeed, e.g., because the ICP ceiling has
147
+ // been reached and the minimum number of participants has not been.
148
+ //
149
+ // The `swap` canister can be deleted when all tokens registered with the
150
+ // `swap` canister have been disbursed to their rightful owners.
151
+ //
152
+ // The logic of this canister is based on the following principles.
153
+ //
154
+ // * Message fields are never removed.
155
+ //
156
+ // * Integer and enum fields can only have their values increase (with
157
+ // one exception, viz., the timestamp field for the start of a
158
+ // transfer is reset if the transfer fails).
159
+ //
160
+ // Data flow for the Neurons' Fund.
161
+ //
162
+ // - A SNS is created.
163
+ // - Proposal to open a decentralization swap for the SNS is submitted to
164
+ // the NNS.
165
+ // - ProposalToOpenDecentralizationSale
166
+ // - The Neurons' Fund investment amount
167
+ // - The parameters of the decentralization swap (`Params`).
168
+ // - Call to open swap:
169
+ // - Parameters
170
+ // - Neurons' Fund investments
171
+ // - NNS Proposal ID of the NNS proposal to open the swap.
172
+ // - On accept of proposal to open decentralization swap:
173
+ // - Compute the maturity contribution of each Neurons' Fund neuron and deduct
174
+ // this amount from the Neurons' Fund neuron.
175
+ // - The swap is informed about the corresponding amount of ICP
176
+ // (`CfParticipant`) in the call to open.
177
+ // - Call back to NNS governance after the swap is committed or aborted:
178
+ // - On committed swap:
179
+ // - Ask the NNS to mint the right amount of ICP for the SNS corresponding
180
+ // to the Neurons' Fund investment (the NNS governance canister keeps
181
+ // track of the total).
182
+ // - On aborted swap:
183
+ // - Send the information about Neurons' Fund participants
184
+ // (`CfParticipant`) back to NNS governance which will return it to
185
+ // the corresponding neurons. Assign the control of the dapp (now under
186
+ // the SNS control) back to the specified principals.
187
+ // - On reject of proposal to open decentralization swap:
188
+ // - Assign the control of the dapp (now under the SNS control) back to the
189
+ // specified principals.
190
+ message Swap {
191
+ reserved "state";
192
+ reserved 2;
193
+
194
+ reserved "cf_minting";
195
+ reserved 8;
196
+
197
+ // The current lifecycle of the swap.
198
+ Lifecycle lifecycle = 3;
199
+
200
+ // Specified on creation. That is, always specified and immutable.
201
+ Init init = 1;
202
+
203
+ // Specified in the transition from PENDING to OPEN and immutable
204
+ // thereafter.
205
+ Params params = 4;
206
+
207
+ // Neurons' Fund participation. Specified in the transition from
208
+ // PENDING to OPEN and immutable thereafter.
209
+ repeated CfParticipant cf_participants = 5;
210
+
211
+ // Empty in the PENDING state. In the OPEN state, new buyers can be
212
+ // added and existing buyers can increase their bids. In the
213
+ // COMMITTED and ABORTED states, the amount cannot be modified, and
214
+ // the transfer timestamps are filled in.
215
+ //
216
+ // The key is the textual representation of the buyer's principal
217
+ // and the value represents the bid.
218
+ map<string, BuyerState> buyers = 6;
219
+
220
+ // When the swap is committed, this field is initialized according
221
+ // to the outcome of the swap.
222
+ repeated SnsNeuronRecipe neuron_recipes = 7;
223
+
224
+ // Gets set to whatever value is in the corresponding field of OpenRequest
225
+ // (that field is required at the application level).
226
+ optional uint64 open_sns_token_swap_proposal_id = 9;
227
+
228
+ // A lock stored in Swap state. If set to true, then a finalize_swap
229
+ // call is in progress. In that case, new finalize_swap calls return
230
+ // immediately without doing any real work.
231
+ //
232
+ // The implementation of the lock should result in the lock being
233
+ // released when the finalize_swap method returns. If
234
+ // a lock is not released, upgrades of the Swap canister can
235
+ // release the lock in the post upgrade hook.
236
+ optional bool finalize_swap_in_progress = 10;
237
+
238
+ // The timestamp for the actual opening of the swap, with an optional delay
239
+ // (specified via params.sale_delay_seconds) after the adoption of the swap
240
+ // proposal. Gets set when NNS calls `open` upon the adoption of
241
+ // the swap proposal.
242
+ optional uint64 decentralization_sale_open_timestamp_seconds = 11;
243
+
244
+ // The timestamp for the actual termination of the swap (committed or aborted).
245
+ optional uint64 decentralization_swap_termination_timestamp_seconds = 21;
246
+
247
+ // This ticket id counter keeps track of the latest ticket id. Whenever a new
248
+ // ticket is created this counter is incremented. It ensures that ticket ids
249
+ // are unique. The ticket IDs are sequential and next_ticket_id is assigned to
250
+ // a users new ticket upon successfully requesting a new ticket. It is
251
+ // incremented after a user requests a new ticket successfully.
252
+ optional uint64 next_ticket_id = 12;
253
+
254
+ // The last time the purge_old_tickets routine was completed.
255
+ optional uint64 purge_old_tickets_last_completion_timestamp_nanoseconds = 13;
256
+
257
+ // The next principal bytes that should be checked by the next
258
+ // running purge_old_tickets routine.
259
+ optional bytes purge_old_tickets_next_principal = 14;
260
+
261
+ // Set to true when auto-finalization is attempted. Prevents auto-finalization
262
+ // from being attempted more than once.
263
+ optional bool already_tried_to_auto_finalize = 17;
264
+
265
+ // Set when auto-finalization finishes. Calling finalize manually has no effect
266
+ // on this parameter.
267
+ optional FinalizeSwapResponse auto_finalize_swap_response = 18;
268
+
269
+ // Amount of contributions from direct participants committed to this SNS so far.
270
+ optional uint64 direct_participation_icp_e8s = 19;
271
+
272
+ // Amount of contributions from the Neurons' Fund committed to this SNS so far.
273
+ optional uint64 neurons_fund_participation_icp_e8s = 20;
274
+ }
275
+
276
+ // The initialisation data of the canister. Always specified on
277
+ // canister creation, and cannot be modified afterwards.
278
+ //
279
+ // If the initialization parameters are incorrect, the swap will
280
+ // immediately be aborted.
281
+ message Init {
282
+ reserved 5, 7 to 10;
283
+ // The canister ID of the NNS governance canister. This is the only
284
+ // principal that can open the swap.
285
+ string nns_governance_canister_id = 1;
286
+
287
+ // The canister ID of the governance canister of the SNS that this
288
+ // token swap pertains to.
289
+ string sns_governance_canister_id = 2;
290
+
291
+ // The ledger canister of the SNS.
292
+ string sns_ledger_canister_id = 3;
293
+
294
+ // The ledger canister for the base token, typically ICP. The base
295
+ // token is typically ICP, but this assumption is not used anywhere,
296
+ // so, in principle, any token type can be used as base token.
297
+ string icp_ledger_canister_id = 4;
298
+
299
+ // Analogous to `sns_governance_canister_id`, but for the "root"
300
+ // canister instead of the governance canister.
301
+ string sns_root_canister_id = 12;
302
+
303
+ // If the swap is aborted, control of the canister(s) should be set to these
304
+ // principals. Must not be empty.
305
+ repeated string fallback_controller_principal_ids = 11;
306
+
307
+ // Same as SNS ledger. Must hold the same value as SNS ledger. Whether the
308
+ // values match is not checked. If they don't match things will break.
309
+ optional uint64 transaction_fee_e8s = 13;
310
+
311
+ // Same as SNS governance. Must hold the same value as SNS governance. Whether
312
+ // the values match is not checked. If they don't match things will break.
313
+ optional uint64 neuron_minimum_stake_e8s = 14;
314
+
315
+ // An optional text that swap participants should confirm before they may
316
+ // participate in the swap. If the field is set, its value should be plain
317
+ // text with at least 1 and at most 1,000 characters.
318
+ optional string confirmation_text = 15;
319
+
320
+ // An optional set of countries that should not participate in the swap.
321
+ optional ic_nervous_system.pb.v1.Countries restricted_countries = 16;
322
+
323
+ // The minimum number of buyers that must participate for the swap
324
+ // to take place. Must be greater than zero.
325
+ optional uint32 min_participants = 17;
326
+
327
+ // The total number of ICP that is required for this token swap to
328
+ // take place. This number divided by the number of SNS tokens being
329
+ // offered gives the seller's reserve price for the swap, i.e., the
330
+ // minimum number of ICP per SNS tokens that the seller of SNS
331
+ // tokens is willing to accept. If this amount is not achieved, the
332
+ // swap will be aborted (instead of committed) when the due date/time
333
+ // occurs. Must be smaller than or equal to `max_icp_e8s`.
334
+ optional uint64 min_icp_e8s = 18;
335
+
336
+ // The number of ICP that is "targeted" by this token swap. If this
337
+ // amount is achieved with sufficient participation, the swap will be
338
+ // triggered immediately, without waiting for the due date
339
+ // (`end_timestamp_seconds`). This means that an investor knows the minimum
340
+ // number of SNS tokens received per invested ICP. If this amount is achieved
341
+ // without reaching sufficient_participation, the swap will abort without
342
+ // waiting for the due date. Must be at least
343
+ // `min_participants * min_participant_icp_e8s`
344
+ optional uint64 max_icp_e8s = 19;
345
+
346
+ // The total number of ICP that is required to be "directly contributed"
347
+ // for this token swap to take place. This number divided by the number of SNS tokens being
348
+ // offered gives the seller's reserve price for the swap, i.e., the
349
+ // minimum number of ICP per SNS tokens that the seller of SNS
350
+ // tokens is willing to accept. If this amount is not achieved, the
351
+ // swap will be aborted (instead of committed) when the due date/time
352
+ // occurs. Must be smaller than or equal to `max_icp_e8s`.
353
+ optional uint64 min_direct_participation_icp_e8s = 30;
354
+
355
+ // The number of ICP that is "targeted" by this token swap. If this
356
+ // amount is achieved with sufficient participation, the swap will be
357
+ // triggered immediately, without waiting for the due date
358
+ // (`end_timestamp_seconds`). This means that an investor knows the minimum
359
+ // number of SNS tokens received per invested ICP. If this amount is achieved
360
+ // without reaching sufficient_participation, the swap will abort without
361
+ // waiting for the due date. Must be at least
362
+ // `min_participants * min_participant_icp_e8s`.
363
+ optional uint64 max_direct_participation_icp_e8s = 31;
364
+
365
+ // The minimum amount of ICP that each buyer must contribute to
366
+ // participate. Must be greater than zero.
367
+ optional uint64 min_participant_icp_e8s = 20;
368
+
369
+ // The maximum amount of ICP that each buyer can contribute. Must be
370
+ // greater than or equal to `min_participant_icp_e8s` and less than
371
+ // or equal to `max_icp_e8s`. Can effectively be disabled by
372
+ // setting it to `max_icp_e8s`.
373
+ optional uint64 max_participant_icp_e8s = 21;
374
+
375
+ // The date/time when the swap should start.
376
+ optional uint64 swap_start_timestamp_seconds = 22;
377
+
378
+ // The date/time when the swap is due, i.e., it will automatically
379
+ // end and commit or abort depending on whether the parameters have
380
+ // been fulfilled.
381
+ optional uint64 swap_due_timestamp_seconds = 23;
382
+
383
+ // The number of tokens (of `init.sns_ledger_canister_id`) that are
384
+ // being offered. The tokens are held in escrow for the SNS
385
+ // governance canister.
386
+ //
387
+ // Invariant for the OPEN state:
388
+ // ```text
389
+ // state.sns_token_e8s <= token_ledger.balance_of(<swap-canister>)
390
+ // ```
391
+ optional uint64 sns_token_e8s = 24;
392
+
393
+ // The construction parameters for the basket of neurons created for all
394
+ // investors in the decentralization swap. Each investor, whether via
395
+ // the Neurons' Fund or direct, will receive `count` Neurons with
396
+ // increasing dissolve delays. The total number of Tokens swapped for
397
+ // by the investor will be evenly distributed across the basket. This is
398
+ // effectively a vesting schedule to ensure there is a gradual release of
399
+ // SNS Tokens available to all investors instead of being liquid immediately.
400
+ // See `NeuronBasketConstructionParameters` for more details on how
401
+ // the basket is configured.
402
+ optional NeuronBasketConstructionParameters neuron_basket_construction_parameters = 25;
403
+
404
+ // The ID of the NNS proposal submitted to launch this SNS decentralization
405
+ // swap.
406
+ optional uint64 nns_proposal_id = 26;
407
+
408
+ // The Neurons' Fund participants of this SNS decentralization swap.
409
+ optional NeuronsFundParticipants neurons_fund_participants = 27;
410
+
411
+ // Controls whether swap finalization should be attempted automatically in the
412
+ // canister heartbeat. If set to false, `finalize_swap` must be called
413
+ // manually. Note: it is safe to call `finalize_swap` multiple times
414
+ // (regardless of the value of this field).
415
+ optional bool should_auto_finalize = 28;
416
+
417
+ // Constraints for the Neurons' Fund participation in this swap.
418
+ optional NeuronsFundParticipationConstraints neurons_fund_participation_constraints = 29;
419
+
420
+ // Whether Neurons' Fund participation is requested.
421
+ optional bool neurons_fund_participation = 32;
422
+ }
423
+
424
+ // Constraints for the Neurons' Fund participation in an SNS swap.
425
+ message NeuronsFundParticipationConstraints {
426
+ // The Neurons' Fund will not participate in this swap unless the direct
427
+ // contributions reach this threshold (in ICP e8s).
428
+ optional uint64 min_direct_participation_threshold_icp_e8s = 1;
429
+
430
+ // Maximum amount (in ICP e8s) of contributions from the Neurons' Fund to this swap.
431
+ optional uint64 max_neurons_fund_participation_icp_e8s = 2;
432
+
433
+ // List of intervals in which the given linear coefficients apply for scaling the
434
+ // ideal Neurons' Fund participation amount (down) to the effective Neurons' Fund
435
+ // participation amount.
436
+ repeated LinearScalingCoefficient coefficient_intervals = 3;
437
+
438
+ // The function used in the implementation of Matched Funding for mapping amounts of direct
439
+ // participation to "ideal" Neurons' Fund participation amounts. The value needs to be adjusted
440
+ // to a potentially smaller value due to SNS-specific participation constraints and
441
+ // the configuration of the Neurons' Fund at the time of the CreateServiceNervousSystem proposal
442
+ // execution.
443
+ optional IdealMatchedParticipationFunction ideal_matched_participation_function = 4;
444
+ }
445
+
446
+ // This function is called "ideal" because it serves as the guideline that the Neurons' Fund will
447
+ // try to follow, but may deviate from in order to satisfy SNS-specific participation constraints
448
+ // while allocating its overall participation amount among its neurons' maturity. In contrast,
449
+ // The "effective" matched participation function `crate::neurons_fund::MatchedParticipationFunction`
450
+ // is computed *based* on this one.
451
+ // TODO(NNS1-1589): Until the Jira ticket gets solved, this definition needs to be synchronized with
452
+ // that from nns/governance/proto/ic_nns_governance/pb/v1/governance.proto.
453
+ message IdealMatchedParticipationFunction {
454
+ // The encoding of the "ideal" matched participation function is defined in `crate::neurons_fund`.
455
+ // In the future, we could change this message to represent full abstract syntactic trees
456
+ // comprised of elementary mathematical operators, with literals and variables as tree leaves.
457
+ optional string serialized_representation = 1;
458
+ }
459
+
460
+ // Some Neurons' Fund neurons might be too small, and some might be too large to participate in a
461
+ // given SNS swap. This causes the need to adjust Neurons' Fund participation from an "ideal" amount
462
+ // to an "effective" amount.
463
+ // * The ideal-participation of the Neurons' Fund refers to the value dictated by some curve that
464
+ // specifies how direct contributions should be matched with Neurons' Fund maturity.
465
+ // * The effective-participation of the Neurons' Fund refers to the value that the NNS Governance
466
+ // can actually allocate, given (1) the configuration of the Neurons' Fund at the time of
467
+ // execution of the corresponding CreateServiceNervousSystem proposal and (2) the amount of direct
468
+ // participation.
469
+ //
470
+ // This structure represents the coefficients of a linear transformation used for
471
+ // mapping the Neurons' Fund ideal-participation to effective-participation on a given
472
+ // linear (semi-open) interval. Say we have the following function for matching direct
473
+ // participants' contributions: `f: ICP e8s -> ICP e8s`; then the *ideal* Neuron's Fund
474
+ // participation amount corresponding to the direct participation of `x` ICP e8s is
475
+ // `f(x)`, while the Neuron's Fund *effective* participation amount is:
476
+ // ```
477
+ // g(x) = (c.slope_numerator / c.slope_denominator) * f(x) + c.intercept
478
+ // ```
479
+ // where `c: LinearScalingCoefficient` with
480
+ // `c.from_direct_participation_icp_e8s <= x < c.to_direct_participation_icp_e8s`.
481
+ // Note that we represent the slope as a rational number (as opposed to floating point),
482
+ // enabling equality comparison between two instances of this structure.
483
+ message LinearScalingCoefficient {
484
+ // (Included) lower bound on the amount of direct participation (in ICP e8s) at which
485
+ // these coefficients apply.
486
+ optional uint64 from_direct_participation_icp_e8s = 1;
487
+ // (Excluded) upper bound on the amount of direct participation (in ICP e8s) at which
488
+ // these coefficients apply.
489
+ optional uint64 to_direct_participation_icp_e8s = 2;
490
+ // Numerator or the slope of the linear transformation.
491
+ optional uint64 slope_numerator = 3;
492
+ // Denominator or the slope of the linear transformation.
493
+ optional uint64 slope_denominator = 4;
494
+ // Intercept of the linear transformation (in ICP e8s).
495
+ optional uint64 intercept_icp_e8s = 5;
496
+ }
497
+
498
+ // Represents multiple Neurons' Fund participants.
499
+ message NeuronsFundParticipants {
500
+ repeated CfParticipant cf_participants = 1;
501
+ }
502
+
503
+ // Represents one NNS neuron from the Neurons' Fund participating in this swap.
504
+ message CfNeuron {
505
+ // The NNS neuron ID of the participating neuron.
506
+ fixed64 nns_neuron_id = 1;
507
+ // The amount of ICP that the Neurons' Fund invests associated
508
+ // with this neuron.
509
+ uint64 amount_icp_e8s = 2;
510
+
511
+ // Idempotency flag indicating whether the neuron recipes have been created for
512
+ // the CfNeuron. When set to true, it signifies that the action of creating neuron
513
+ // recipes has been performed on this structure. If the action is retried, this flag
514
+ // can be checked to avoid duplicate operations.
515
+ optional bool has_created_neuron_recipes = 3;
516
+ }
517
+
518
+ // Represents a Neurons' Fund participant, possibly with several neurons.
519
+ message CfParticipant {
520
+ // The principal that can vote on behalf of these Neurons' Fund neurons.
521
+ string hotkey_principal = 1;
522
+ // Information about the participating neurons. Must not be empty.
523
+ repeated CfNeuron cf_neurons = 2;
524
+ }
525
+
526
+ // The construction parameters for the basket of neurons created for all
527
+ // investors in the decentralization swap.
528
+ message NeuronBasketConstructionParameters {
529
+ // The number of neurons each investor will receive after the
530
+ // decentralization swap. The total tokens swapped for will be
531
+ // evenly distributed across the `count` neurons.
532
+ uint64 count = 1;
533
+
534
+ // The amount of additional time it takes for the next neuron to dissolve.
535
+ uint64 dissolve_delay_interval_seconds = 2;
536
+ }
537
+
538
+ // The parameters of the swap, provided in the call to `open`. Cannot
539
+ // be modified after the call to `open`.
540
+ message Params {
541
+ // The minimum number of buyers that must participate for the swap
542
+ // to take place. Must be greater than zero.
543
+ uint32 min_participants = 1;
544
+
545
+ // The total number of ICP that is required for this token swap to
546
+ // take place. This number divided by the number of SNS tokens being
547
+ // offered gives the seller's reserve price for the swap, i.e., the
548
+ // minimum number of ICP per SNS tokens that the seller of SNS
549
+ // tokens is willing to accept. If this amount is not achieved, the
550
+ // swap will be aborted (instead of committed) when the due date/time
551
+ // occurs. Must be smaller than or equal to `max_icp_e8s`.
552
+ uint64 min_icp_e8s = 2;
553
+
554
+ // The number of ICP that is "targeted" by this token swap. If this
555
+ // amount is achieved with sufficient participation, the swap will be
556
+ // triggered immediately, without waiting for the due date
557
+ // (`end_timestamp_seconds`). This means that an investor knows the minimum
558
+ // number of SNS tokens received per invested ICP. If this amount is achieved
559
+ // without reaching sufficient_participation, the swap will abort without
560
+ // waiting for the due date. Must be at least
561
+ // `min_participants * min_participant_icp_e8s`.
562
+ uint64 max_icp_e8s = 3;
563
+
564
+ // The total number of ICP that is required for this token swap to
565
+ // take place. This number divided by the number of SNS tokens being
566
+ // offered gives the seller's reserve price for the swap, i.e., the
567
+ // minimum number of ICP per SNS tokens that the seller of SNS
568
+ // tokens is willing to accept. If this amount is not achieved, the
569
+ // swap will be aborted (instead of committed) when the due date/time
570
+ // occurs. Must be smaller than or equal to `max_icp_e8s`.
571
+ optional uint64 min_direct_participation_icp_e8s = 10;
572
+
573
+ // The number of ICP that is "targeted" by this token swap. If this
574
+ // amount is achieved with sufficient participation, the swap will be
575
+ // triggered immediately, without waiting for the due date
576
+ // (`end_timestamp_seconds`). This means that an investor knows the minimum
577
+ // number of SNS tokens received per invested ICP. If this amount is achieved
578
+ // without reaching sufficient_participation, the swap will abort without
579
+ // waiting for the due date. Must be at least
580
+ // `min_participants * min_participant_icp_e8s`.
581
+ optional uint64 max_direct_participation_icp_e8s = 11;
582
+
583
+ // The minimum amount of ICP that each buyer must contribute to
584
+ // participate. Must be greater than zero.
585
+ uint64 min_participant_icp_e8s = 4;
586
+
587
+ // The maximum amount of ICP that each buyer can contribute. Must be
588
+ // greater than or equal to `min_participant_icp_e8s` and less than
589
+ // or equal to `max_icp_e8s`. Can effectively be disabled by
590
+ // setting it to `max_icp_e8s`.
591
+ uint64 max_participant_icp_e8s = 5;
592
+
593
+ // The date/time when the swap is due, i.e., it will automatically
594
+ // end and commit or abort depending on whether the parameters have
595
+ // been fulfilled.
596
+ uint64 swap_due_timestamp_seconds = 6;
597
+
598
+ // The number of tokens (of `init.sns_ledger_canister_id`) that are
599
+ // being offered. The tokens are held in escrow for the SNS
600
+ // governance canister.
601
+ //
602
+ // Invariant for the OPEN state:
603
+ // ```text
604
+ // state.sns_token_e8s <= token_ledger.balance_of(<swap-canister>)
605
+ // ```
606
+ uint64 sns_token_e8s = 7;
607
+
608
+ // The construction parameters for the basket of neurons created for all
609
+ // investors in the decentralization swap. Each investor, whether via
610
+ // the Neurons' Fund or direct, will receive `count` Neurons with
611
+ // increasing dissolve delays. The total number of Tokens swapped for
612
+ // by the investor will be evenly distributed across the basket. This is
613
+ // effectively a vesting schedule to ensure there is a gradual release of
614
+ // SNS Tokens available to all investors instead of being liquid immediately.
615
+ // See `NeuronBasketConstructionParameters` for more details on how
616
+ // the basket is configured.
617
+ NeuronBasketConstructionParameters neuron_basket_construction_parameters = 8;
618
+
619
+ // An optional delay, so that the actual swap does not get opened immediately
620
+ // after the adoption of the swap proposal.
621
+ optional uint64 sale_delay_seconds = 9;
622
+ }
623
+
624
+ message TransferableAmount {
625
+ // The amount in e8s equivalent that the participant committed to the Swap,
626
+ // which is held by the swap canister until the swap is committed or aborted.
627
+ uint64 amount_e8s = 1;
628
+
629
+ // When the transfer to refund or commit funds starts.
630
+ uint64 transfer_start_timestamp_seconds = 2;
631
+
632
+ // When the transfer to refund or commit succeeds.
633
+ uint64 transfer_success_timestamp_seconds = 3;
634
+
635
+ // The amount that was successfully transferred when swap commits or aborts
636
+ // (minus fees).
637
+ optional uint64 amount_transferred_e8s = 4;
638
+
639
+ // The fee charged when transferring from the swap canister;
640
+ optional uint64 transfer_fee_paid_e8s = 5;
641
+ }
642
+
643
+ message BuyerState {
644
+ reserved 1 to 4;
645
+ // The amount of ICP accepted from this buyer. ICP is accepted by
646
+ // first making a ledger transfer and then calling the method
647
+ // `refresh_buyer_token_e8s`.
648
+ //
649
+ // Can only be set when a buyer state record for a new buyer is
650
+ // created, which can only happen when the lifecycle state is
651
+ // `Open`. Must be at least `min_participant_icp_e8s`, and at most
652
+ // `max_participant_icp_e8s`.
653
+ //
654
+ // Invariant between canisters in the OPEN state:
655
+ //
656
+ // ```text
657
+ // icp.amount_e8 <= icp_ledger.balance_of(subaccount(swap_canister, P)),
658
+ // ```
659
+ //
660
+ // where `P` is the principal ID associated with this buyer's state.
661
+ //
662
+ // ownership
663
+ // * PENDING - a `BuyerState` cannot exist
664
+ // * OPEN - owned by the buyer, cannot be transferred out
665
+ // * COMMITTED - owned by the SNS governance canister, can be transferred out
666
+ // * ABORTED - owned by the buyer, can be transferred out
667
+ TransferableAmount icp = 5;
668
+
669
+ // Idempotency flag indicating whether the neuron recipes have been created for
670
+ // the BuyerState. When set to true, it signifies that the action of creating neuron
671
+ // recipes has been performed on this structure. If the action is retried, this flag
672
+ // can be checked to avoid duplicate operations.
673
+ optional bool has_created_neuron_recipes = 6;
674
+ }
675
+
676
+ // Information about a direct investor.
677
+ message DirectInvestment {
678
+ string buyer_principal = 1;
679
+ }
680
+
681
+ // Information about a Neurons' Fund investment. The NNS Governance
682
+ // canister is the controller of these neurons.
683
+ message CfInvestment {
684
+ string hotkey_principal = 1;
685
+ fixed64 nns_neuron_id = 2;
686
+ }
687
+
688
+ message TimeWindow {
689
+ uint64 start_timestamp_seconds = 1;
690
+ uint64 end_timestamp_seconds = 2;
691
+ }
692
+
693
+ message SnsNeuronRecipe {
694
+ TransferableAmount sns = 1;
695
+ oneof investor {
696
+ DirectInvestment direct = 2;
697
+ CfInvestment community_fund = 3;
698
+ }
699
+ // Attributes of the Neuron to be created from the SnsNeuronRecipe
700
+ NeuronAttributes neuron_attributes = 4;
701
+
702
+ // Attributes of the Neuron to be created from the SnsNeuronRecipe
703
+ message NeuronAttributes {
704
+ // The memo to be used when calculating the Neuron's staking account
705
+ // in the SNS Ledger.
706
+ // See `nervous_system_common::compute_neuron_staking_subaccount`.
707
+ // The memo is used along with the a principal_id of the "controller" of
708
+ // the neuron. In the case of the decentralization sale, that will either be
709
+ // the PrincipalId of NNS Governance canister for Neurons' Fund investors,
710
+ // or the PrincipalId of the direct investor.
711
+ uint64 memo = 1;
712
+
713
+ // The dissolve delay in seconds that the Neuron will be created with.
714
+ uint64 dissolve_delay_seconds = 2;
715
+
716
+ // The list of NeuronIds that the created Neuron will follow on all SNS
717
+ // proposal actions known to governance at the time. Additional followees
718
+ // and following relations can be added after neuron creation.
719
+ //
720
+ // TODO[NNS1-1589] Due to the dependency cycle, the `swap` canister's
721
+ // protobuf cannot directly depend on SNS Governance NeuronId type.
722
+ // The followees NeuronId's are of a duplicated type, which is converted to
723
+ // SNS governance NeuronId at the time.
724
+ // of claiming.
725
+ repeated NeuronId followees = 3;
726
+ }
727
+
728
+ // The status of the SnsNeuronRecipe's creation within SNS Governance. This
729
+ // field is used as a journal between calls of `finalize`.
730
+ optional ClaimedStatus claimed_status = 5;
731
+
732
+ // The various statuses of creation that a SnsNeuronRecipe can have in an SNS.
733
+ enum ClaimedStatus {
734
+ // Unused, here for PB lint purposes.
735
+ CLAIMED_STATUS_UNSPECIFIED = 0;
736
+
737
+ // The Neuron is pending creation and can be claimed in SNS Governance.
738
+ CLAIMED_STATUS_PENDING = 1;
739
+
740
+ // The Neuron has been created successfully in SNS Governance.
741
+ CLAIMED_STATUS_SUCCESS = 2;
742
+
743
+ // The Neuron has previously failed to be created in SNS Governance, but can
744
+ // be retried in the future.
745
+ CLAIMED_STATUS_FAILED = 3;
746
+
747
+ // The Neuron is invalid and was not created in SNS Governance. This neuron
748
+ // cannot be retried without manual intervention to update its
749
+ // `NeuronParameters`.
750
+ CLAIMED_STATUS_INVALID = 4;
751
+ }
752
+ }
753
+
754
+ //
755
+ // === Request/Response Messages
756
+ //
757
+
758
+ message OpenRequest {
759
+ // The parameters of the swap.
760
+ Params params = 1;
761
+ // Neurons' Fund participation.
762
+ repeated CfParticipant cf_participants = 2;
763
+ // The ID of the proposal whose execution consists of calling this method.
764
+ optional uint64 open_sns_token_swap_proposal_id = 3;
765
+ }
766
+
767
+ message OpenResponse {}
768
+
769
+ message GetCanisterStatusRequest {}
770
+
771
+ // TODO: introduce a limits on the number of buyers to include?
772
+ message GetStateRequest {}
773
+ message GetStateResponse {
774
+ Swap swap = 1;
775
+ DerivedState derived = 2;
776
+ }
777
+
778
+ message GetBuyerStateRequest {
779
+ // The principal_id of the user who's buyer state is being queried for.
780
+ ic_base_types.pb.v1.PrincipalId principal_id = 1;
781
+ }
782
+
783
+ message GetBuyerStateResponse {
784
+ BuyerState buyer_state = 1;
785
+ }
786
+
787
+ message GetBuyersTotalRequest {}
788
+
789
+ message GetBuyersTotalResponse {
790
+ // The total amount of ICP deposited by buyers.
791
+ uint64 buyers_total = 1;
792
+ }
793
+
794
+ message DerivedState {
795
+ uint64 buyer_total_icp_e8s = 1;
796
+ // Current number of non-Neurons' Fund swap participants
797
+ optional uint64 direct_participant_count = 3;
798
+ // Current number of Neurons' Fund swap participants. In particular, it's the
799
+ // number of unique controllers of the neurons participating
800
+ // in the Neurons' Fund.
801
+ optional uint64 cf_participant_count = 4;
802
+ // Current number of Neurons' Fund neurons participating in the swap
803
+ // May be greater than cf_participant_count if multiple neurons in
804
+ // the Neurons' Fund have the same controller.
805
+ optional uint64 cf_neuron_count = 5;
806
+ // Current approximate rate SNS tokens per ICP. Note that this should not be used for super
807
+ // precise financial accounting, because this is floating point.
808
+ float sns_tokens_per_icp = 2;
809
+ // Current amount of contributions from direct swap participants.
810
+ optional uint64 direct_participation_icp_e8s = 6;
811
+ // Current amount that the Neurons' Fund promises to participate with if the swap were to
812
+ // successfully finalize now. Until the swap's success criterium is satisfied, this value is
813
+ // merely a progress indicator.
814
+ optional uint64 neurons_fund_participation_icp_e8s = 7;
815
+ }
816
+
817
+ message SetOpenTimeWindowRequest {
818
+ // Duration must be between 1 and 90 days. The TimeWindow's
819
+ // end time but be greater than or equal to the TimeWindow's
820
+ // start time.
821
+ TimeWindow open_time_window = 1;
822
+ }
823
+
824
+ // Response if setting the open time window succeeded.
825
+ message SetOpenTimeWindowResponse {}
826
+
827
+ // Informs the swap canister that a buyer has sent funds to participate in the
828
+ // swap.
829
+ //
830
+ // Only in lifecycle state `open`.
831
+ message RefreshBuyerTokensRequest {
832
+ // If not specified, the caller is used.
833
+ string buyer = 1;
834
+
835
+ // To accept the swap participation confirmation, a participant should send
836
+ // the confirmation text via refresh_buyer_tokens, matching the text set
837
+ // during SNS initialization.
838
+ optional string confirmation_text = 2;
839
+ }
840
+ message RefreshBuyerTokensResponse {
841
+ uint64 icp_accepted_participation_e8s = 1;
842
+ uint64 icp_ledger_account_balance_e8s = 2;
843
+ }
844
+
845
+ // Once a swap is committed or aborted, the tokens need to be
846
+ // distributed, and, if the swap was committed, neurons created.
847
+ message FinalizeSwapRequest {}
848
+
849
+ // Response from the `finalize_swap` canister API.
850
+ message FinalizeSwapResponse {
851
+ SweepResult sweep_icp_result = 1;
852
+
853
+ SweepResult sweep_sns_result = 2;
854
+
855
+ SweepResult claim_neuron_result = 3;
856
+
857
+ SetModeCallResult set_mode_call_result = 4;
858
+
859
+ SetDappControllersCallResult set_dapp_controllers_call_result = 5;
860
+
861
+ SettleCommunityFundParticipationResult settle_community_fund_participation_result = 6;
862
+
863
+ SweepResult create_sns_neuron_recipes_result = 8;
864
+
865
+ SettleNeuronsFundParticipationResult settle_neurons_fund_participation_result = 9;
866
+
867
+ // Explains what (if anything) went wrong.
868
+ optional string error_message = 7;
869
+ }
870
+
871
+ message SweepResult {
872
+ // Success means that on this call to finalize, the item in the
873
+ // sweep succeeded.
874
+ uint32 success = 1;
875
+
876
+ // Failure means that on this call to finalize, the item in the
877
+ // sweep failed but may be successful in the future.
878
+ uint32 failure = 2;
879
+
880
+ // Skipped means that on a previous call to finalize, the item
881
+ // in the sweep was successful.
882
+ uint32 skipped = 3;
883
+
884
+ // Invalid means that on this call and all future calls to finalize,
885
+ // this item will not be successful, and will need intervention to
886
+ // succeed.
887
+ uint32 invalid = 4;
888
+
889
+ // Global_failures does not map to individual items in the sweep, but
890
+ // number of global failures encountered in the sweep.
891
+ uint32 global_failures = 5;
892
+ }
893
+
894
+ // Analogous to Rust type Result<SetModeResponse, CanisterCallError>.
895
+ message SetModeCallResult {
896
+ oneof possibility {
897
+ SetModeResult ok = 1;
898
+ CanisterCallError err = 2;
899
+ }
900
+
901
+ message SetModeResult {}
902
+ }
903
+
904
+ // Request struct for the method restore_dapp_controllers.
905
+ message RestoreDappControllersRequest {}
906
+
907
+ // Response of the method restore_dapp_controllers.
908
+ // Analogous to Rust type Result<SetDappControllersResponse, CanisterCallError>.
909
+ message RestoreDappControllersResponse {
910
+ oneof possibility {
911
+ // TODO(NNS1-1589): Uncomment.
912
+ // ic_sns_root.pb.v1.
913
+ SetDappControllersResponse ok = 1;
914
+ CanisterCallError err = 2;
915
+ }
916
+ }
917
+
918
+ // Analogous to Rust type Result<SetDappControllersResponse, CanisterCallError>.
919
+ message SetDappControllersCallResult {
920
+ oneof possibility {
921
+ // TODO(NNS1-1589): Uncomment.
922
+ // ic_sns_root.pb.v1.
923
+ SetDappControllersResponse ok = 1;
924
+ CanisterCallError err = 2;
925
+ }
926
+ }
927
+
928
+ message SettleCommunityFundParticipationResult {
929
+ message Response {
930
+ // Can be blank.
931
+ GovernanceError governance_error = 1;
932
+ }
933
+
934
+ oneof possibility {
935
+ Response ok = 1;
936
+ CanisterCallError err = 2;
937
+ }
938
+ }
939
+
940
+ // The result from settling the neurons' fund participation in finalization.
941
+ message SettleNeuronsFundParticipationResult {
942
+ // The successful branch of the result. On subsequent attempts to settle
943
+ // neurons fund participation (for example: due to some later stage of
944
+ // finalization failing and a manual retry is invoked), this branch
945
+ // will be set with the results of the original successful attempt.
946
+ message Ok {
947
+ optional uint64 neurons_fund_participation_icp_e8s = 1;
948
+ optional uint64 neurons_fund_neurons_count = 2;
949
+ }
950
+
951
+ // The failure branch of the result. This message can be set for a
952
+ // number of reasons not limited to
953
+ // - invalid state
954
+ // - replica errors
955
+ // - canister errors
956
+ //
957
+ // While some of these errors are transient and can immediately retried,
958
+ // others require manual intervention. The error messages and logs of the
959
+ // canister should provide enough context to debug.
960
+ message Error {
961
+ optional string message = 1;
962
+ }
963
+
964
+ oneof possibility {
965
+ Ok ok = 1;
966
+ Error err = 2;
967
+ }
968
+ }
969
+
970
+ // TODO(NNS1-1589): Delete these copied definitions.
971
+
972
+ // BEGIN NNS1-1589 HACKS
973
+
974
+ // Change control of the listed canisters to the listed principal id.
975
+ // Copy of the type in root.proto. TODO(NNS1-1589)
976
+ message SetDappControllersRequest {
977
+ message CanisterIds {
978
+ repeated ic_base_types.pb.v1.PrincipalId canister_ids = 1;
979
+ }
980
+ optional CanisterIds canister_ids = 1;
981
+
982
+ repeated ic_base_types.pb.v1.PrincipalId controller_principal_ids = 2;
983
+ }
984
+
985
+ message SetDappControllersResponse {
986
+ message FailedUpdate {
987
+ ic_base_types.pb.v1.PrincipalId dapp_canister_id = 1;
988
+ CanisterCallError err = 2;
989
+ }
990
+ repeated FailedUpdate failed_updates = 1;
991
+ }
992
+
993
+ // Copied from nns governance.proto.
994
+ message GovernanceError {
995
+ enum ErrorType {
996
+ ERROR_TYPE_UNSPECIFIED = 0;
997
+ // The operation was successfully completed.
998
+ ERROR_TYPE_OK = 1;
999
+ // This operation is not available, e.g., not implemented.
1000
+ ERROR_TYPE_UNAVAILABLE = 2;
1001
+ // The caller is not authorized to perform this operation.
1002
+ ERROR_TYPE_NOT_AUTHORIZED = 3;
1003
+ // Some entity required for the operation (for example, a neuron) was
1004
+ // not found.
1005
+ ERROR_TYPE_NOT_FOUND = 4;
1006
+ // The command was missing or invalid. This is a permanent error.
1007
+ ERROR_TYPE_INVALID_COMMAND = 5;
1008
+ // The neuron is dissolving or dissolved and the operation requires it to
1009
+ // be not dissolving (that is, having a non-zero dissolve delay that is
1010
+ // accumulating age).
1011
+ ERROR_TYPE_REQUIRES_NOT_DISSOLVING = 6;
1012
+ // The neuron is not dissolving or dissolved and the operation requires
1013
+ // it to be dissolving (that is, having a non-zero dissolve delay with
1014
+ // zero age that is not accumulating).
1015
+ ERROR_TYPE_REQUIRES_DISSOLVING = 7;
1016
+ // The neuron is not dissolving and not dissolved and the operation
1017
+ // requires it to be dissolved (that is, having a dissolve delay of zero
1018
+ // and an age of zero).
1019
+ ERROR_TYPE_REQUIRES_DISSOLVED = 8;
1020
+ // When adding or removing a hot key: the key to add was already
1021
+ // present or the key to remove was not present or the key to add
1022
+ // was invalid or adding another hot key would bring the total
1023
+ // number of the maximum number of allowed hot keys per neuron.
1024
+ ERROR_TYPE_HOT_KEY = 9;
1025
+ // Some canister side resource is exhausted, so this operation cannot be
1026
+ // performed.
1027
+ ERROR_TYPE_RESOURCE_EXHAUSTED = 10;
1028
+ // Some precondition for executing this method was not met (e.g. the
1029
+ // neuron's dissolve time is too short). There could be a change in the
1030
+ // state of the system such that the operation becomes allowed (e.g. the
1031
+ // owner of the neuron increases its dissolve delay).
1032
+ ERROR_TYPE_PRECONDITION_FAILED = 11;
1033
+ // Executing this method failed for some reason external to the
1034
+ // governance canister.
1035
+ ERROR_TYPE_EXTERNAL = 12;
1036
+ // A neuron has an ongoing ledger update and thus can't be
1037
+ // changed.
1038
+ ERROR_TYPE_LEDGER_UPDATE_ONGOING = 13;
1039
+ // There wasn't enough funds to perform the operation.
1040
+ ERROR_TYPE_INSUFFICIENT_FUNDS = 14;
1041
+ // The principal provided was invalid.
1042
+ ERROR_TYPE_INVALID_PRINCIPAL = 15;
1043
+ // The proposal is defective in some way (e.g. title is too long). If the
1044
+ // same proposal is submitted again without modification, it will be
1045
+ // rejected regardless of changes in the system's state (e.g. increasing
1046
+ // the neuron's dissolve delay will not make the proposal acceptable).
1047
+ ERROR_TYPE_INVALID_PROPOSAL = 16;
1048
+ // The neuron attempted to join the Neurons' Fund while already
1049
+ // a member.
1050
+ ERROR_TYPE_ALREADY_JOINED_COMMUNITY_FUND = 17;
1051
+ // The neuron attempted to leave the Neurons' Fund but is not a member.
1052
+ ERROR_TYPE_NOT_IN_THE_COMMUNITY_FUND = 18;
1053
+ }
1054
+
1055
+ ErrorType error_type = 1;
1056
+ string error_message = 2;
1057
+ }
1058
+
1059
+ // Copied from nns governance.proto.
1060
+ message SettleCommunityFundParticipation {
1061
+ // The caller's principal ID must match the value in the
1062
+ // target_swap_canister_id field in the proposal (more precisely, in the
1063
+ // OpenSnsTokenSwap).
1064
+ optional uint64 open_sns_token_swap_proposal_id = 1;
1065
+
1066
+ // Each of the possibilities here corresponds to one of two ways that a swap
1067
+ // can terminate. See also sns_swap_pb::Lifecycle::is_terminal.
1068
+ oneof result {
1069
+ Committed committed = 2;
1070
+ Aborted aborted = 3;
1071
+ }
1072
+
1073
+ // When this happens, ICP needs to be minted, and sent to the SNS governance
1074
+ // canister's main account on the ICP Ledger. As with Aborted, the amount of
1075
+ // ICP that needs to be minted can be deduced from the ProposalData's
1076
+ // cf_participants field.
1077
+ message Committed {
1078
+ // This is where the minted ICP will be sent. In principal, this could be
1079
+ // fetched using the swap canister's get_state method.
1080
+ ic_base_types.pb.v1.PrincipalId sns_governance_canister_id = 1;
1081
+ // Total amount of contributions from direct swap participants.
1082
+ optional uint64 total_direct_contribution_icp_e8s = 2;
1083
+ // Total amount of contributions from the Neuron's Fund.
1084
+ // TODO[NNS1-2570]: Ensure this field is set.
1085
+ optional uint64 total_neurons_fund_contribution_icp_e8s = 3;
1086
+ }
1087
+
1088
+ // When this happens, maturity needs to be restored to Neurons' Fund neurons.
1089
+ // The amounts to be refunded can be found in the ProposalData's
1090
+ // `cf_participants` field.
1091
+ message Aborted {}
1092
+ }
1093
+
1094
+ // Request to settle the Neurons' Fund participation in this SNS Swap.
1095
+ //
1096
+ // When a swap ends, the Swap canister notifies the Neurons' Fund of the swap's ultimate result,
1097
+ // which can be either `Committed` or `Aborted`. Note that currently, the Neurons' Fund is managed
1098
+ // by the NNS Governance canister.
1099
+ // * If the result is `Committed`:
1100
+ // - Neurons' Fund computes the "effective" participation amount for each of its neurons (as per
1101
+ // the Matched Funding rules). This computation is based on the total direct participation
1102
+ // amount, which is thus a field of `Committed`.
1103
+ // - Neurons' Fund converts the "effective" amount of maturity into ICP by:
1104
+ // - Requesting the ICP Ledger to mint an appropriate amount of ICP tokens and sending them
1105
+ // to the SNS treasury.
1106
+ // - Refunding whatever maturity is left over (the maximum possible maturity is reserved by
1107
+ // the Neurons' Fund before the swap begins).
1108
+ // - Neurons' Fund returns the Neurons' Fund participants back to the Swap canister
1109
+ // (see SettleNeuronsFundParticipationResponse).
1110
+ // - The Swap canister then creates SNS neurons for the Neurons' Fund participants.
1111
+ // * If the result is Aborted, the Neurons' Fund is refunded for all maturity reserved for this SNS.
1112
+ //
1113
+ // This design assumes trust between the Neurons' Fund and the SNS Swap canisters. In the one hand,
1114
+ // the Swap trusts that the Neurons' Fund sends the correct amount of ICP to the SNS treasury,
1115
+ // and that the Neurons' Fund allocates its participants following the Matched Funding rules. On the
1116
+ // other hand, the Neurons' Fund trusts that the Swap will indeed create appropriate SNS neurons
1117
+ // for the Neurons' Fund participants.
1118
+ //
1119
+ // The justification for this trust assumption is as follows. The Neurons' Fund can be trusted as
1120
+ // it is controlled by the NNS. The SNS Swap can be trusted as it is (1) deployed by SNS-W, which is
1121
+ // also part of the NNS and (2) upgraded via an NNS proposal (unlike all other SNS canisters).
1122
+ //
1123
+ // This request may be submitted only by the Swap canister of an SNS instance created by
1124
+ // a CreateServiceNervousSystem proposal.
1125
+ //
1126
+ // TODO(NNS1-1589): Until the Jira ticket gets solved, changes here need to be
1127
+ // manually propagated to (sns) swap.proto.
1128
+ message SettleNeuronsFundParticipationRequest {
1129
+ // Proposal ID of the CreateServiceNervousSystem proposal that created this SNS instance.
1130
+ optional uint64 nns_proposal_id = 1;
1131
+
1132
+ // Each of the possibilities here corresponds to one of two ways that a swap can terminate.
1133
+ // See also sns_swap_pb::Lifecycle::is_terminal.
1134
+ oneof result {
1135
+ Committed committed = 2;
1136
+ Aborted aborted = 3;
1137
+ }
1138
+
1139
+ // When this happens, the NNS Governance needs to do several things:
1140
+ // (1) Compute the effective amount of ICP per neuron of the Neurons' Fund as a function of
1141
+ // `total_direct_participation_icp_e8s`. The overall Neurons' Fund participation should
1142
+ // equal `total_neurons_fund_contribution_icp_e8s`.
1143
+ // (2) Mint (via the ICP Ledger) and sent to the SNS governance the amount of
1144
+ // `total_neurons_fund_contribution_icp_e8s`.
1145
+ // (3) Respond to this request with `SettleNeuronsFundParticipationResponse`, providing
1146
+ // the set of `NeuronsFundParticipant`s with the effective amount of ICP per neuron,
1147
+ // as computed in step (1).
1148
+ // (4) Refund each neuron of the Neurons' Fund with (reserved - effective) amount of ICP.
1149
+ // Effective amounts depend on `total_direct_participation_icp_e8s` and the participation limits
1150
+ // of a particular SNS instance, namely, each participation must be between
1151
+ // `min_participant_icp_e8s` and `max_participant_icp_e8s`.
1152
+ // - If a neuron of the Neurons' Fund has less than `min_participant_icp_e8s` worth of maturity,
1153
+ // then it is ineligible to participate.
1154
+ // - If a neuron of the Neurons' Fund has more than `max_participant_icp_e8s` worth of maturity,
1155
+ // then its participation amount is limited to `max_participant_icp_e8s`.
1156
+ // Reserved amounts are computed as the minimal upper bound on the effective amounts, i.e., when
1157
+ // the value `total_direct_participation_icp_e8s` reaches its theoretical maximum.
1158
+ message Committed {
1159
+ // This is where the minted ICP will be sent.
1160
+ ic_base_types.pb.v1.PrincipalId sns_governance_canister_id = 1;
1161
+ // Total amount of participation from direct swap participants.
1162
+ optional uint64 total_direct_participation_icp_e8s = 2;
1163
+ // Total amount of participation from the Neurons' Fund.
1164
+ // TODO[NNS1-2570]: Ensure this field is set.
1165
+ optional uint64 total_neurons_fund_participation_icp_e8s = 3;
1166
+ }
1167
+
1168
+ // When this happens, all priorly reserved maturity for this SNS instance needs to be restored to
1169
+ // the Neurons' Fund neurons.
1170
+ message Aborted {}
1171
+ }
1172
+
1173
+ // Handling the Neurons' Fund and transferring some of its maturity to an SNS treasury is
1174
+ // thus the responsibility of the NNS Governance. When a swap succeeds, a Swap canister should send
1175
+ // a `settle_neurons_fund_participation` request to the NNS Governance, specifying its `result`
1176
+ // field as `committed`. The NNS Governance then computes the ultimate distribution of maturity in
1177
+ // the Neurons' Fund. However, this distribution also needs to be made available to the SNS Swap
1178
+ // that will use this information to create SNS neurons of an appropriate size for each
1179
+ // Neurons' Fund (as well as direct) participant. That is why in the `committed` case,
1180
+ // the NNS Governance should populate the `neurons_fund_participants` field, while in the `aborted`
1181
+ // case it should be empty.
1182
+ //
1183
+ // TODO(NNS1-1589): Until the Jira ticket gets solved, changes here need to be
1184
+ // manually propagated to (sns) swap.proto.
1185
+ message SettleNeuronsFundParticipationResponse {
1186
+ // Represents one NNS neuron from the Neurons' Fund participating in this swap.
1187
+ message NeuronsFundNeuron {
1188
+ // The NNS neuron ID of the participating neuron.
1189
+ optional uint64 nns_neuron_id = 1;
1190
+ // The amount of Neurons' Fund participation associated with this neuron.
1191
+ optional uint64 amount_icp_e8s = 2;
1192
+ // The principal that can vote on behalf of this neuron.
1193
+ optional string hotkey_principal = 3;
1194
+ // Whether the amount maturity amount of Neurons' Fund participation associated with this neuron
1195
+ // has been capped to reflect the maximum participation amount for this SNS swap.
1196
+ optional bool is_capped = 4;
1197
+ }
1198
+
1199
+ // Request was completed successfully.
1200
+ message Ok {
1201
+ repeated NeuronsFundNeuron neurons_fund_neuron_portions = 1;
1202
+ }
1203
+
1204
+ oneof result {
1205
+ GovernanceError err = 1;
1206
+ Ok ok = 2;
1207
+ }
1208
+ }
1209
+
1210
+ // The id of a specific neuron, which equals the neuron's subaccount on
1211
+ // the ledger canister (the account that holds the neuron's staked tokens).
1212
+ message NeuronId {
1213
+ bytes id = 1;
1214
+ }
1215
+
1216
+ // END NNS1-1589 HACKS
1217
+
1218
+ message CanisterCallError {
1219
+ optional int32 code = 1;
1220
+ string description = 2;
1221
+ }
1222
+
1223
+ // Request a refund of tokens that were sent to the canister in
1224
+ // error. The refund is always on the ICP ledger, from this canister's
1225
+ // subaccount of the caller to the account of the caller.
1226
+ message ErrorRefundIcpRequest {
1227
+ // Principal who originally sent the funds to us, and is now asking for any
1228
+ // unaccepted balance to be returned.
1229
+ ic_base_types.pb.v1.PrincipalId source_principal_id = 1;
1230
+ }
1231
+
1232
+ message ErrorRefundIcpResponse {
1233
+ // Request was completed successfully.
1234
+ message Ok {
1235
+ // The ledger transfer went through at this block height.
1236
+ optional uint64 block_height = 1;
1237
+ }
1238
+
1239
+ // Request was not successful, and no funds were transferred.
1240
+ message Err {
1241
+ enum Type {
1242
+ TYPE_UNSPECIFIED = 0;
1243
+
1244
+ // There is something wrong with the request. If repeated, the request
1245
+ // will always be rejected.
1246
+ TYPE_INVALID_REQUEST = 1;
1247
+
1248
+ // Most likely, the canister is in the wrong Lifecycle. More generally,
1249
+ // the system is not yet in a state where the request can be fulfilled,
1250
+ // but it might enter a suitable state later. In this case, the same
1251
+ // request might be accepted later.
1252
+ TYPE_PRECONDITION = 2;
1253
+
1254
+ // Most likely, a request to the ledger failed, in which case, it can be
1255
+ // assumed that no funds were transferred. In general, this is caused by
1256
+ // something outside this canister, which usually means some other
1257
+ // canister (such as ledger).
1258
+ TYPE_EXTERNAL = 3;
1259
+ }
1260
+
1261
+ optional Type error_type = 1;
1262
+ optional string description = 2;
1263
+ }
1264
+
1265
+ oneof result {
1266
+ Ok ok = 1;
1267
+ Err err = 2;
1268
+ }
1269
+ }
1270
+
1271
+ // Request struct for the method `get_lifecycle`
1272
+ message GetLifecycleRequest {}
1273
+
1274
+ // Response struct for the method `get_lifecycle`
1275
+ message GetLifecycleResponse {
1276
+ optional Lifecycle lifecycle = 1;
1277
+ optional uint64 decentralization_sale_open_timestamp_seconds = 2;
1278
+ optional uint64 decentralization_swap_termination_timestamp_seconds = 3;
1279
+ }
1280
+
1281
+ message GetAutoFinalizationStatusRequest {}
1282
+
1283
+ message GetAutoFinalizationStatusResponse {
1284
+ // Reflects whether auto-finalization has been enabled via in the init
1285
+ // parameters (`should_auto_finalize`).
1286
+ optional bool is_auto_finalize_enabled = 1;
1287
+
1288
+ // True if and only if auto-finalization has been started.
1289
+ optional bool has_auto_finalize_been_attempted = 2;
1290
+
1291
+ // Will be populated with the FinalizeSwapResponse once auto-finalization has
1292
+ // completed.
1293
+ optional FinalizeSwapResponse auto_finalize_swap_response = 3;
1294
+ }
1295
+
1296
+ // Request struct for the method `get_init`
1297
+ message GetInitRequest {}
1298
+
1299
+ // Response struct for the method `get_init`
1300
+ message GetInitResponse {
1301
+ optional Init init = 1;
1302
+ }
1303
+
1304
+ // Request struct for the method `get_derived_state`
1305
+ message GetDerivedStateRequest {}
1306
+
1307
+ // Response struct for the method `get_derived_state`
1308
+ message GetDerivedStateResponse {
1309
+ optional uint64 buyer_total_icp_e8s = 1;
1310
+ // Current number of non-Neurons' Fund swap participants
1311
+ optional uint64 direct_participant_count = 3;
1312
+ // Current number of Neurons' Fund swap participants. In particular, it's the
1313
+ // number of unique controllers of the neurons participating
1314
+ // in the Neurons' Fund.
1315
+ optional uint64 cf_participant_count = 4;
1316
+ // Current number of Neurons' Fund neurons participating in the swap
1317
+ // May be greater than cf_participant_count if multiple neurons in
1318
+ // the Neurons' Fund have the same controller.
1319
+ optional uint64 cf_neuron_count = 5;
1320
+ optional double sns_tokens_per_icp = 2;
1321
+ // Current amount of contributions from direct swap participants.
1322
+ optional uint64 direct_participation_icp_e8s = 6;
1323
+ // Current amount of contributions from the Neurons' Fund.
1324
+ optional uint64 neurons_fund_participation_icp_e8s = 7;
1325
+ }
1326
+
1327
+ // ICRC-1 Account. See https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1
1328
+ message ICRC1Account {
1329
+ ic_base_types.pb.v1.PrincipalId owner = 1;
1330
+ optional bytes subaccount = 2;
1331
+ }
1332
+
1333
+ // A device for ensuring that retrying (direct) participation does not result
1334
+ // in multiple participation. Basically, this records a user's intent to
1335
+ // participate BEFORE moving any funds.
1336
+ //
1337
+ // How this is used: before any money is sent, a user's agent must first look
1338
+ // for an existing ticket. If one does not exist, then, a new one is created
1339
+ // for the current participation that is now being attempted (for
1340
+ // the first time).
1341
+ //
1342
+ // If there is already a ticket, then the new participation must be aborted.
1343
+ // The surprise existence of the ticket indicates that there is a pending
1344
+ // participation. In this case the user's agent must attempt to perform the same
1345
+ // participation as stated in the ticket before doing anything else.
1346
+ message Ticket {
1347
+ // Unique ID of the ticket
1348
+ uint64 ticket_id = 1;
1349
+
1350
+ // The account of the ticket.
1351
+ //
1352
+ // account.owner is the owner of this ticket.
1353
+ ICRC1Account account = 2;
1354
+
1355
+ // The user-set amount of the ticket in ICP e8s
1356
+ uint64 amount_icp_e8s = 3;
1357
+
1358
+ // The timestamp of creation of this ticket
1359
+ uint64 creation_time = 4;
1360
+ }
1361
+
1362
+ // Request struct for the method `get_open_ticket`
1363
+ message GetOpenTicketRequest {}
1364
+
1365
+ // Response struct for the method `get_open_ticket`
1366
+ message GetOpenTicketResponse {
1367
+ // Request was completed successfully.
1368
+ message Ok {
1369
+ // If there is an open swap ticket for the caller then this field
1370
+ // contains it.
1371
+ optional Ticket ticket = 1;
1372
+ }
1373
+
1374
+ // Request was not successful, and no ticket was created.
1375
+ message Err {
1376
+ enum Type {
1377
+ TYPE_UNSPECIFIED = 0;
1378
+
1379
+ TYPE_SALE_NOT_OPEN = 1;
1380
+
1381
+ TYPE_SALE_CLOSED = 2;
1382
+ }
1383
+
1384
+ optional Type error_type = 1;
1385
+ }
1386
+
1387
+ oneof result {
1388
+ Ok ok = 1;
1389
+ Err err = 2;
1390
+ }
1391
+ }
1392
+
1393
+ // Request struct for the method `new_sale_ticket`
1394
+ message NewSaleTicketRequest {
1395
+ // The user-set amount of the ticket in ICP e8s
1396
+ uint64 amount_icp_e8s = 1;
1397
+
1398
+ // The subaccount of the caller to be used for the ticket
1399
+ optional bytes subaccount = 2;
1400
+ }
1401
+
1402
+ // Response struct for the method `new_sale_ticket`
1403
+ message NewSaleTicketResponse {
1404
+ // Request was completed successfully.
1405
+ message Ok {
1406
+ // The created ticket.
1407
+ Ticket ticket = 1;
1408
+ }
1409
+
1410
+ // Request was not successful, and no ticket was created.
1411
+ message Err {
1412
+ message InvalidUserAmount {
1413
+ uint64 min_amount_icp_e8s_included = 1;
1414
+ uint64 max_amount_icp_e8s_included = 2;
1415
+ }
1416
+
1417
+ enum Type {
1418
+ TYPE_UNSPECIFIED = 0;
1419
+
1420
+ TYPE_SALE_NOT_OPEN = 1;
1421
+
1422
+ TYPE_SALE_CLOSED = 2;
1423
+
1424
+ // There is already an open ticket associated with the caller.
1425
+ //
1426
+ // When this is the `error_type`, then the field existing_ticket
1427
+ // is set and contains the ticket itself.
1428
+ TYPE_TICKET_EXISTS = 3;
1429
+
1430
+ // The amount sent by the user is not within the Swap parameters.
1431
+ //
1432
+ // When this is the `error_type`, then the field invalid_user_amount
1433
+ // is set and describes minimum and maximum amounts.
1434
+ TYPE_INVALID_USER_AMOUNT = 4;
1435
+
1436
+ // The specified subaccount is not a valid subaccount
1437
+ // (length != 32 bytes).
1438
+ TYPE_INVALID_SUBACCOUNT = 5;
1439
+
1440
+ // The specified principal is forbidden from creating tickets.
1441
+ TYPE_INVALID_PRINCIPAL = 6;
1442
+ }
1443
+
1444
+ Type error_type = 1;
1445
+
1446
+ // When `error_type` is `INVALID_USER_AMOUNT` then this field
1447
+ // describes the minimum and maximum amounts.
1448
+ optional InvalidUserAmount invalid_user_amount = 2;
1449
+
1450
+ // When `error_type` is `TICKET_EXISTS` then this field
1451
+ // contains the ticket that already exists.
1452
+ optional Ticket existing_ticket = 3;
1453
+ }
1454
+
1455
+ oneof result {
1456
+ Ok ok = 1;
1457
+ Err err = 2;
1458
+ }
1459
+ }
1460
+
1461
+ // Request struct for the method `list_direct_participants`. This method
1462
+ // paginates over all direct participants in the decentralization swap.
1463
+ // Direct participants are participants who did not participate via the
1464
+ // Neurons' Fund.
1465
+ message ListDirectParticipantsRequest {
1466
+ // The limit of the number of Participants returned in each page, in range
1467
+ // [0, 30,000].
1468
+ // If no value, or a value outside of this range is requested, 30,000 will be
1469
+ // used.
1470
+ optional uint32 limit = 1;
1471
+
1472
+ // Skip the first `offset` elements when constructing the response.
1473
+ optional uint32 offset = 2;
1474
+ }
1475
+
1476
+ // Response struct for the method `list_direct_participants`.
1477
+ message ListDirectParticipantsResponse {
1478
+ // The list of Participants returned from the invocation of
1479
+ // `list_direct_participants`.
1480
+ // The list is a page of all the buyers in the Swap canister at the time of
1481
+ // the method call. The size of the page is equal to either:
1482
+ // - the max page size (30,000),
1483
+ // - the corresponding `ListDirectParticipantsRequest.limit`,
1484
+ // - the remaining Participants, if there are fewer than `limit` participants
1485
+ // left.
1486
+ //
1487
+ // Pagination through the entire list of participants is complete if
1488
+ // len(participants) < `ListDirectParticipantsRequest.limit`.
1489
+ repeated Participant participants = 1;
1490
+ }
1491
+
1492
+ // A direct Participant in the decentralization swap.
1493
+ message Participant {
1494
+ // The PrincipalId of the participant.
1495
+ ic_base_types.pb.v1.PrincipalId participant_id = 1;
1496
+
1497
+ // The BuyerState of the participant, which includes the
1498
+ // amount of participation in e8s of a Token, and the transfer
1499
+ // status of those tokens.
1500
+ BuyerState participation = 2;
1501
+ }
1502
+
1503
+ // Request struct for the method `get_sale_parameters`.
1504
+ message GetSaleParametersRequest {}
1505
+
1506
+ // Response struct for the method `get_sale_parameters`.
1507
+ message GetSaleParametersResponse {
1508
+ Params params = 1;
1509
+ }
1510
+
1511
+ // Request struct for the method `list_community_fund_participants`.
1512
+ message ListCommunityFundParticipantsRequest {
1513
+ // The maximum number of elements that will be in the response.
1514
+ // This is capped at 10_000.
1515
+ optional uint32 limit = 1;
1516
+ // Skip the first `offset` elements when constructing the response
1517
+ optional uint64 offset = 2;
1518
+ }
1519
+
1520
+ // Response struct for the method `list_community_fund_participants`.
1521
+ message ListCommunityFundParticipantsResponse {
1522
+ repeated CfParticipant cf_participants = 1;
1523
+ }
1524
+
1525
+ // Request for the method `list_sns_neuron_recipes`
1526
+ message ListSnsNeuronRecipesRequest {
1527
+ // The maximum number of elements that will be in the response.
1528
+ // This is capped at 10_000.
1529
+ optional uint32 limit = 1;
1530
+ // Skip the first `offset` elements when constructing the response
1531
+ optional uint64 offset = 2;
1532
+ }
1533
+
1534
+ // Response for the method `list_sns_neuron_recipes`
1535
+ message ListSnsNeuronRecipesResponse {
1536
+ repeated SnsNeuronRecipe sns_neuron_recipes = 1;
1537
+ }
1538
+
1539
+ // Request struct for the method `notify_payment_failure`
1540
+ message NotifyPaymentFailureRequest {}
1541
+
1542
+ // Response for the method `notify_payment_failure`
1543
+ // Returns the ticket if a ticket was found for the caller and the ticket
1544
+ // was removed successfully. Returns None if no ticket was found for the caller.
1545
+ message NotifyPaymentFailureResponse {
1546
+ optional Ticket ticket = 1;
1547
+ }