Skip to content

Commit 15eae36

Browse files
emmazzzDmitri Perelman
andauthored
[sui-system] minimum staking threshold of 1 SUI (#9961)
## Description Took over the work @dmitri-perelman started (and almost finished) in #9825 because I can't push to his fork of the repo. ## Test Plan Added some tests. --- If your changes are not user-facing and not a breaking change, you can skip the following section. Otherwise, please indicate what changed, and then add to the Release Notes section as highlighted during the release process. ### Type of Change (Check all that apply) - [x] user-visible impact - [ ] breaking change for a client SDKs - [ ] breaking change for FNs (FN binary must upgrade) - [ ] breaking change for validators or node operators (must upgrade binaries) - [ ] breaking change for on-chain data layout - [ ] necessitate either a data wipe or data migration ### Release notes --------- Co-authored-by: Dmitri Perelman <[email protected]>
1 parent 976d3e1 commit 15eae36

File tree

18 files changed

+359
-213
lines changed

18 files changed

+359
-213
lines changed

crates/sui-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -240,56 +240,56 @@ validators:
240240
next_epoch_worker_address: ~
241241
extra_fields:
242242
id:
243-
id: "0xf64a2f2782984f7b7a36a99c9d77a83b3b6b5ebecf6ee0eaf118a37d74925c00"
243+
id: "0x5e311ba6292362f225da9c71cf52097ca86b95fe6f4863a88c8e34a262a01193"
244244
size: 0
245245
voting_power: 10000
246-
operation_cap_id: "0x62a99cd4412cb992da5a1035d6021db91e041704625a4010af68d1b0100a3177"
246+
operation_cap_id: "0x669f5c0b0ca9066ace330aba5135ea3d5bbfdd4da5733574d0c596c8787d92de"
247247
gas_price: 1
248248
staking_pool:
249-
id: "0xa110faecebe16b6d167929f7ee40acb2c8c1341eb80c5a24e92fe8dc1cd85015"
249+
id: "0x2a6e996bd69665ad4b2e880c60ec497fb38ca17cc282264222f5ba00ea5d25c8"
250250
activation_epoch: 0
251251
deactivation_epoch: ~
252252
sui_balance: 20000000000000000
253253
rewards_pool:
254254
value: 0
255255
pool_token_balance: 20000000000000000
256256
exchange_rates:
257-
id: "0x77dbaaae503d2664c5e6dfdd01d0bc46b1edf85fc927be9ece5cfd5f0c7a48ef"
257+
id: "0x34c71362a7bfbbd2f2e1ddc116e22834185f8e84635dd819e5432b35bca560aa"
258258
size: 1
259259
pending_stake: 0
260260
pending_total_sui_withdraw: 0
261261
pending_pool_token_withdraw: 0
262262
extra_fields:
263263
id:
264-
id: "0x4898c94bc523d09495c8f9f0947785a3d27e6fbc904c7aa13f48bd90b3032ac7"
264+
id: "0x9540bb52b6eb64682154ab9f190069b2f65408326ca1cde6879bcd22f41ea436"
265265
size: 0
266266
commission_rate: 0
267267
next_epoch_stake: 20000000000000000
268268
next_epoch_gas_price: 1
269269
next_epoch_commission_rate: 0
270270
extra_fields:
271271
id:
272-
id: "0x6daf7eb399ff05a5c2567b7bbb125c9451433af8b425904ab54debbdd28b38df"
272+
id: "0x32da93ecef6352085e4a98e8fdc6ef53f58a87867735e5963e2862da4ca7ebf4"
273273
size: 0
274274
pending_active_validators:
275275
contents:
276-
id: "0x66748b0af8b5eb3e9414d0fd7b424031ec634dc036b8a7a16d17b59e48c514f4"
276+
id: "0x2c9d0fce0e485143215ddf9b3bad49a90faaa08abbe603d6268064e0fa11de63"
277277
size: 0
278278
pending_removals: []
279279
staking_pool_mappings:
280-
id: "0x8091ccc40b81fafe63c18b385bef35ca34f86dc8ce03b14a5c5bd2ecc764dfcc"
280+
id: "0x0e8b457d90c99717d5deafde80d2e6c2c930ededf359d0dde79488fae9d2c70d"
281281
size: 1
282282
inactive_validators:
283-
id: "0xd2427484e7572464744d5ac7805d36e083ab262fef2f8c8cb16e4bf80eeba28e"
283+
id: "0xe5e4e20e15059e13633058858aa474a3d50e647ba500b6ab2eab85a1ffd31ac2"
284284
size: 0
285285
validator_candidates:
286-
id: "0x575efc4d1aa2dac9276de776fe85bab09f1dcde06481d19309094b460d4666d5"
286+
id: "0xc7bb461afa1ee2ca919443255525031ccf3dfafd6aabca790053afffaef11258"
287287
size: 0
288288
at_risk_validators:
289289
contents: []
290290
extra_fields:
291291
id:
292-
id: "0x341985f35823807fb3acf0a34c0b8bf152b355c3638dca24a3222701324eafe7"
292+
id: "0x89b6475302daabe7d012b2f70b706551cbe84e6c4f23ae4713e472af36bcb239"
293293
size: 0
294294
storage_fund:
295295
total_object_storage_rebates:
@@ -306,7 +306,7 @@ parameters:
306306
validator_low_stake_grace_period: 7
307307
extra_fields:
308308
id:
309-
id: "0xfcfc2dd968cefbf2e4d4679d06ac311ad5eb58fc4ea7d2e988eec89b8e1ec85f"
309+
id: "0xa95d9ca79ae9361d0fa0086c09d61818a4c95926d4ff8873af54fa7c3d033288"
310310
size: 0
311311
reference_gas_price: 1
312312
validator_report_records:
@@ -320,7 +320,7 @@ stake_subsidy:
320320
stake_subsidy_decrease_rate: 10000
321321
extra_fields:
322322
id:
323-
id: "0x08d8bb5dcbae6e7e0cff0ba0aa23ec8998794cb8e465dd0db6c45cf84752b25b"
323+
id: "0x7590e21a4280f3e5b567da3338e491df195f724b7f908e6cf543717dbbaf3040"
324324
size: 0
325325
safe_mode: false
326326
safe_mode_storage_rewards:
@@ -332,6 +332,6 @@ safe_mode_non_refundable_storage_fee: 0
332332
epoch_start_timestamp_ms: 10
333333
extra_fields:
334334
id:
335-
id: "0x1f79bdc6d1399738cc06184895e2bc41c76b3c06ddb3d3ece4549f45bf0de724"
335+
id: "0x139f148cc3a7ad72f792389ed8d63875c594fbd59bd7c65a6c4bb01c1a28be49"
336336
size: 0
337337

crates/sui-framework/docs/staking_pool.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,15 @@ A self-custodial object holding the staked SUI tokens.
343343

344344

345345

346+
<a name="0x3_staking_pool_EStakedSuiBelowThreshold"></a>
347+
348+
349+
350+
<pre><code><b>const</b> <a href="staking_pool.md#0x3_staking_pool_EStakedSuiBelowThreshold">EStakedSuiBelowThreshold</a>: u64 = 18;
351+
</code></pre>
352+
353+
354+
346355
<a name="0x3_staking_pool_ETokenBalancesDoNotMatchExchangeRate"></a>
347356

348357

@@ -397,6 +406,16 @@ A self-custodial object holding the staked SUI tokens.
397406

398407

399408

409+
<a name="0x3_staking_pool_MIN_STAKING_THRESHOLD"></a>
410+
411+
StakedSui objects cannot be split to below this amount.
412+
413+
414+
<pre><code><b>const</b> <a href="staking_pool.md#0x3_staking_pool_MIN_STAKING_THRESHOLD">MIN_STAKING_THRESHOLD</a>: u64 = 1000000000;
415+
</code></pre>
416+
417+
418+
400419
<a name="0x3_staking_pool_new"></a>
401420

402421
## Function `new`
@@ -1049,7 +1068,7 @@ Split the given StakedSui to the two parts, one with principal <code>split_amoun
10491068
transfer the newly split part to the sender address.
10501069

10511070

1052-
<pre><code><b>public</b> entry <b>fun</b> <a href="staking_pool.md#0x3_staking_pool_split_staked_sui">split_staked_sui</a>(c: &<b>mut</b> <a href="staking_pool.md#0x3_staking_pool_StakedSui">staking_pool::StakedSui</a>, split_amount: u64, ctx: &<b>mut</b> <a href="_TxContext">tx_context::TxContext</a>)
1071+
<pre><code><b>public</b> entry <b>fun</b> <a href="staking_pool.md#0x3_staking_pool_split_staked_sui">split_staked_sui</a>(stake: &<b>mut</b> <a href="staking_pool.md#0x3_staking_pool_StakedSui">staking_pool::StakedSui</a>, split_amount: u64, ctx: &<b>mut</b> <a href="_TxContext">tx_context::TxContext</a>)
10531072
</code></pre>
10541073

10551074

@@ -1058,8 +1077,14 @@ transfer the newly split part to the sender address.
10581077
<summary>Implementation</summary>
10591078

10601079

1061-
<pre><code><b>public</b> entry <b>fun</b> <a href="staking_pool.md#0x3_staking_pool_split_staked_sui">split_staked_sui</a>(c: &<b>mut</b> <a href="staking_pool.md#0x3_staking_pool_StakedSui">StakedSui</a>, split_amount: u64, ctx: &<b>mut</b> TxContext) {
1062-
<a href="_transfer">transfer::transfer</a>(<a href="staking_pool.md#0x3_staking_pool_split">split</a>(c, split_amount, ctx), <a href="_sender">tx_context::sender</a>(ctx));
1080+
<pre><code><b>public</b> entry <b>fun</b> <a href="staking_pool.md#0x3_staking_pool_split_staked_sui">split_staked_sui</a>(stake: &<b>mut</b> <a href="staking_pool.md#0x3_staking_pool_StakedSui">StakedSui</a>, split_amount: u64, ctx: &<b>mut</b> TxContext) {
1081+
<b>let</b> original_amount = <a href="_value">balance::value</a>(&stake.principal);
1082+
<b>assert</b>!(split_amount &lt;= original_amount, <a href="staking_pool.md#0x3_staking_pool_EInsufficientSuiTokenBalance">EInsufficientSuiTokenBalance</a>);
1083+
<b>let</b> remaining_amount = original_amount - split_amount;
1084+
// Both resulting parts should have at least <a href="staking_pool.md#0x3_staking_pool_MIN_STAKING_THRESHOLD">MIN_STAKING_THRESHOLD</a>.
1085+
<b>assert</b>!(remaining_amount &gt;= <a href="staking_pool.md#0x3_staking_pool_MIN_STAKING_THRESHOLD">MIN_STAKING_THRESHOLD</a>, <a href="staking_pool.md#0x3_staking_pool_EStakedSuiBelowThreshold">EStakedSuiBelowThreshold</a>);
1086+
<b>assert</b>!(split_amount &gt;= <a href="staking_pool.md#0x3_staking_pool_MIN_STAKING_THRESHOLD">MIN_STAKING_THRESHOLD</a>, <a href="staking_pool.md#0x3_staking_pool_EStakedSuiBelowThreshold">EStakedSuiBelowThreshold</a>);
1087+
<a href="_transfer">transfer::transfer</a>(<a href="staking_pool.md#0x3_staking_pool_split">split</a>(stake, split_amount, ctx), <a href="_sender">tx_context::sender</a>(ctx));
10631088
}
10641089
</code></pre>
10651090

crates/sui-framework/docs/validator_set.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,15 @@ The epoch value corresponds to the first epoch this change takes place.
351351
## Constants
352352

353353

354+
<a name="0x3_validator_set_MIN_STAKING_THRESHOLD"></a>
355+
356+
357+
358+
<pre><code><b>const</b> <a href="validator_set.md#0x3_validator_set_MIN_STAKING_THRESHOLD">MIN_STAKING_THRESHOLD</a>: u64 = 1000000000;
359+
</code></pre>
360+
361+
362+
354363
<a name="0x3_validator_set_EInvalidCap"></a>
355364

356365

@@ -477,6 +486,15 @@ The epoch value corresponds to the first epoch this change takes place.
477486

478487

479488

489+
<a name="0x3_validator_set_EStakingBelowThreshold"></a>
490+
491+
492+
493+
<pre><code><b>const</b> <a href="validator_set.md#0x3_validator_set_EStakingBelowThreshold">EStakingBelowThreshold</a>: u64 = 10;
494+
</code></pre>
495+
496+
497+
480498
<a name="0x3_validator_set_EValidatorNotCandidate"></a>
481499

482500

@@ -743,6 +761,7 @@ Only an active validator can request to be removed.
743761
Called by <code><a href="sui_system.md#0x3_sui_system">sui_system</a></code>, to add a new stake to the validator.
744762
This request is added to the validator's staking pool's pending stake entries, processed at the end
745763
of the epoch.
764+
Aborts in case the staking amount is smaller than MIN_STAKING_THRESHOLD
746765

747766

748767
<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="validator_set.md#0x3_validator_set_request_add_stake">request_add_stake</a>(self: &<b>mut</b> <a href="validator_set.md#0x3_validator_set_ValidatorSet">validator_set::ValidatorSet</a>, validator_address: <b>address</b>, stake: <a href="_Balance">balance::Balance</a>&lt;<a href="_SUI">sui::SUI</a>&gt;, ctx: &<b>mut</b> <a href="_TxContext">tx_context::TxContext</a>)
@@ -760,6 +779,8 @@ of the epoch.
760779
stake: Balance&lt;SUI&gt;,
761780
ctx: &<b>mut</b> TxContext,
762781
) {
782+
<b>let</b> sui_amount = <a href="_value">balance::value</a>(&stake);
783+
<b>assert</b>!(sui_amount &gt;= <a href="validator_set.md#0x3_validator_set_MIN_STAKING_THRESHOLD">MIN_STAKING_THRESHOLD</a>, <a href="validator_set.md#0x3_validator_set_EStakingBelowThreshold">EStakingBelowThreshold</a>);
763784
<b>let</b> <a href="validator.md#0x3_validator">validator</a> = <a href="validator_set.md#0x3_validator_set_get_candidate_or_active_validator_mut">get_candidate_or_active_validator_mut</a>(self, validator_address);
764785
<a href="validator.md#0x3_validator_request_add_stake">validator::request_add_stake</a>(<a href="validator.md#0x3_validator">validator</a>, stake, <a href="_sender">tx_context::sender</a>(ctx), ctx);
765786
}

crates/sui-framework/packages/sui-system/sources/staking_pool.move

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ module sui_system::staking_pool {
1717
friend sui_system::validator;
1818
friend sui_system::validator_set;
1919

20+
/// StakedSui objects cannot be split to below this amount.
21+
const MIN_STAKING_THRESHOLD: u64 = 1_000_000_000; // 1 SUI
22+
2023
const EInsufficientPoolTokenBalance: u64 = 0;
2124
const EWrongPool: u64 = 1;
2225
const EWithdrawAmountCannotBeZero: u64 = 2;
@@ -35,6 +38,7 @@ module sui_system::staking_pool {
3538
const EPoolNotPreactive: u64 = 15;
3639
const EActivationOfInactivePool: u64 = 16;
3740
const EDelegationOfZeroSui: u64 = 17;
41+
const EStakedSuiBelowThreshold: u64 = 18;
3842

3943
/// A staking pool embedded in each validator struct in the system state object.
4044
struct StakingPool has key, store {
@@ -342,8 +346,14 @@ module sui_system::staking_pool {
342346

343347
/// Split the given StakedSui to the two parts, one with principal `split_amount`,
344348
/// transfer the newly split part to the sender address.
345-
public entry fun split_staked_sui(c: &mut StakedSui, split_amount: u64, ctx: &mut TxContext) {
346-
transfer::transfer(split(c, split_amount, ctx), tx_context::sender(ctx));
349+
public entry fun split_staked_sui(stake: &mut StakedSui, split_amount: u64, ctx: &mut TxContext) {
350+
let original_amount = balance::value(&stake.principal);
351+
assert!(split_amount <= original_amount, EInsufficientSuiTokenBalance);
352+
let remaining_amount = original_amount - split_amount;
353+
// Both resulting parts should have at least MIN_STAKING_THRESHOLD.
354+
assert!(remaining_amount >= MIN_STAKING_THRESHOLD, EStakedSuiBelowThreshold);
355+
assert!(split_amount >= MIN_STAKING_THRESHOLD, EStakedSuiBelowThreshold);
356+
transfer::transfer(split(stake, split_amount, ctx), tx_context::sender(ctx));
347357
}
348358

349359
/// Consume the staked sui `other` and add its value to `self`.

crates/sui-framework/packages/sui-system/sources/validator_set.move

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ module sui_system::validator_set {
108108
const ANY_VALIDATOR: u8 = 3;
109109

110110
const BASIS_POINT_DENOMINATOR: u128 = 10000;
111+
const MIN_STAKING_THRESHOLD: u64 = 1_000_000_000; // 1 SUI
111112

112113
// Errors
113114
const ENonValidatorInReportRecords: u64 = 0;
@@ -120,7 +121,7 @@ module sui_system::validator_set {
120121
const EValidatorNotCandidate: u64 = 7;
121122
const ENotValidatorCandidate: u64 = 8;
122123
const ENotActiveOrPendingValidator: u64 = 9;
123-
124+
const EStakingBelowThreshold: u64 = 10;
124125
const EInvalidCap: u64 = 101;
125126

126127

@@ -265,12 +266,15 @@ module sui_system::validator_set {
265266
/// Called by `sui_system`, to add a new stake to the validator.
266267
/// This request is added to the validator's staking pool's pending stake entries, processed at the end
267268
/// of the epoch.
269+
/// Aborts in case the staking amount is smaller than MIN_STAKING_THRESHOLD
268270
public(friend) fun request_add_stake(
269271
self: &mut ValidatorSet,
270272
validator_address: address,
271273
stake: Balance<SUI>,
272274
ctx: &mut TxContext,
273275
) {
276+
let sui_amount = balance::value(&stake);
277+
assert!(sui_amount >= MIN_STAKING_THRESHOLD, EStakingBelowThreshold);
274278
let validator = get_candidate_or_active_validator_mut(self, validator_address);
275279
validator::request_add_stake(validator, stake, tx_context::sender(ctx), ctx);
276280
}

0 commit comments

Comments
 (0)