diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 2497aad..4f2096c 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -3,13 +3,13 @@ name: Pull Request on: pull_request: push: - branches: [mainnet, devel, add_ci] + branches: [mainnet] jobs: move-test: runs-on: ubuntu-latest container: - image: mysten/sui-tools:mainnet-v1.62.1 + image: mysten/sui-tools:mainnet ports: - 80 steps: diff --git a/contracts/Move.lock b/contracts/Move.lock index a3de63f..d5b43ea 100644 --- a/contracts/Move.lock +++ b/contracts/Move.lock @@ -1,48 +1,29 @@ -# @generated by Move, please check-in and do not edit manually. +# Generated by move; do not edit +# This file should be checked in. [move] -version = 3 -manifest_digest = "B6D6C734105D94569A4952D271B434F37A5816AB5E868FF6AF09C757B103B56A" -deps_digest = "F9B494B64F0615AED0E98FC12A85B85ECD2BC5185C22D30E7F67786BB52E507C" -dependencies = [ - { id = "Bridge", name = "Bridge" }, - { id = "MoveStdlib", name = "MoveStdlib" }, - { id = "Sui", name = "Sui" }, - { id = "SuiSystem", name = "SuiSystem" }, -] - -[[move.package]] -id = "Bridge" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "ad937889f2f99bc666b05aaeadccb0d3d7d1f863", subdir = "crates/sui-framework/packages/bridge" } - -dependencies = [ - { id = "MoveStdlib", name = "MoveStdlib" }, - { id = "Sui", name = "Sui" }, - { id = "SuiSystem", name = "SuiSystem" }, -] - -[[move.package]] -id = "MoveStdlib" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "ad937889f2f99bc666b05aaeadccb0d3d7d1f863", subdir = "crates/sui-framework/packages/move-stdlib" } - -[[move.package]] -id = "Sui" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "ad937889f2f99bc666b05aaeadccb0d3d7d1f863", subdir = "crates/sui-framework/packages/sui-framework" } - -dependencies = [ - { id = "MoveStdlib", name = "MoveStdlib" }, -] - -[[move.package]] -id = "SuiSystem" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "ad937889f2f99bc666b05aaeadccb0d3d7d1f863", subdir = "crates/sui-framework/packages/sui-system" } - -dependencies = [ - { id = "MoveStdlib", name = "MoveStdlib" }, - { id = "Sui", name = "Sui" }, -] - -[move.toolchain-version] -compiler-version = "1.62.1" -edition = "2024" -flavor = "sui" +version = 4 + +[pinned.mainnet.MoveStdlib] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "868c226359ef914f1f3b080518f27eb13d8967f5" } +use_environment = "mainnet" +manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363" +deps = {} + +[pinned.mainnet.Sui] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "868c226359ef914f1f3b080518f27eb13d8967f5" } +use_environment = "mainnet" +manifest_digest = "CD547CB1ACCE0880C835DAED2D8FFCB91D56C833AE5240D3AA5B918398263195" +deps = { MoveStdlib = "MoveStdlib" } + +[pinned.mainnet.SuiSystem] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-system", rev = "868c226359ef914f1f3b080518f27eb13d8967f5" } +use_environment = "mainnet" +manifest_digest = "CCBB2DE961DACC94644C65D54E808DEF86BC7E9C42A494D5250CC5F99392E069" +deps = { MoveStdlib = "MoveStdlib", Sui = "Sui" } + +[pinned.mainnet.liquid_staking] +source = { root = true } +use_environment = "mainnet" +manifest_digest = "5088FADDD8E68B81993124941C625140D78354C8DCD372C440055B66B7604874" +deps = { std = "MoveStdlib", sui = "Sui", sui_system = "SuiSystem" } diff --git a/contracts/Move.lock.legacy b/contracts/Move.lock.legacy new file mode 100644 index 0000000..a3de63f --- /dev/null +++ b/contracts/Move.lock.legacy @@ -0,0 +1,48 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 3 +manifest_digest = "B6D6C734105D94569A4952D271B434F37A5816AB5E868FF6AF09C757B103B56A" +deps_digest = "F9B494B64F0615AED0E98FC12A85B85ECD2BC5185C22D30E7F67786BB52E507C" +dependencies = [ + { id = "Bridge", name = "Bridge" }, + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "Bridge" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "ad937889f2f99bc666b05aaeadccb0d3d7d1f863", subdir = "crates/sui-framework/packages/bridge" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "ad937889f2f99bc666b05aaeadccb0d3d7d1f863", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +id = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "ad937889f2f99bc666b05aaeadccb0d3d7d1f863", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, +] + +[[move.package]] +id = "SuiSystem" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "ad937889f2f99bc666b05aaeadccb0d3d7d1f863", subdir = "crates/sui-framework/packages/sui-system" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, +] + +[move.toolchain-version] +compiler-version = "1.62.1" +edition = "2024" +flavor = "sui" diff --git a/contracts/Move.toml b/contracts/Move.toml index fe33228..0a3bb1b 100644 --- a/contracts/Move.toml +++ b/contracts/Move.toml @@ -1,39 +1,6 @@ [package] name = "liquid_staking" -edition = "2024" # edition = "legacy" to use legacy (pre-2024) Move -published-at = "0xf89509a8b32205a362921d58a5a0b0bfb13981122a5a0dc49eef7947d4dd21a2" -# license = "" # e.g., "MIT", "GPL", "Apache 2.0" -# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] +edition = "2024" [dependencies] - - -# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. -# Revision can be a branch, a tag, and a commit hash. -# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } - -# For local dependencies use `local = path`. Path is relative to the package root -# Local = { local = "../path/to" } - -# To resolve a version conflict and force a specific version for dependency -# override use `override = true` -# Override = { local = "../conflicting/version", override = true } - -[addresses] -# liquid_staking = "0x0" -liquid_staking = "0xb49e62ea76f58b8e02e7c31cbb939e9d019a0c2be1c30f3bc58a6249a58f9333" - -# Named addresses will be accessible in Move as `@name`. They're also exported: -# for example, `std = "0x1"` is exported by the Standard Library. -# alice = "0xA11CE" - -[dev-dependencies] -# The dev-dependencies section allows overriding dependencies for `--test` and -# `--dev` modes. You can introduce test-only dependencies here. -# Local = { local = "../path/to/dev-build" } - -[dev-addresses] -# The dev-addresses section allows overwriting named addresses for the `--test` -# and `--dev` modes. -# alice = "0xB0B" - +sui_system = { system = "sui_system" } diff --git a/contracts/Published.toml b/contracts/Published.toml new file mode 100644 index 0000000..d86fa97 --- /dev/null +++ b/contracts/Published.toml @@ -0,0 +1,12 @@ +# Generated by Move +# This file contains metadata about published versions of this package in different environments +# This file SHOULD be committed to source control + +[published.mainnet] +chain-id = "35834a8a" +published-at = "0xb49e62ea76f58b8e02e7c31cbb939e9d019a0c2be1c30f3bc58a6249a58f9333" +original-id = "0xb0575765166030556a6eafd3b1b970eba8183ff748860680245b9edd41c716e7" +version = 9 +toolchain-version = "1.65.2" +build-config = { flavor = "sui", edition = "2024" } +upgrade-capability = "0x4dc657b6c0fe896f4b94fee1ceac96312dde0a36b94e799caaec30deb53dcd67" diff --git a/contracts/sources/cell.move b/contracts/sources/cell.move index a6b3cb6..6923532 100644 --- a/contracts/sources/cell.move +++ b/contracts/sources/cell.move @@ -1,6 +1,6 @@ module liquid_staking::cell { public struct Cell has store { - element: Option + element: Option, } public fun new(element: Element): Cell { diff --git a/contracts/sources/events.move b/contracts/sources/events.move index 406f516..2c2843d 100644 --- a/contracts/sources/events.move +++ b/contracts/sources/events.move @@ -10,4 +10,4 @@ module liquid_staking::events { public(package) fun emit_event(event: T) { event::emit(Event { event }); } -} \ No newline at end of file +} diff --git a/contracts/sources/fees.move b/contracts/sources/fees.move index ae1cdd8..33df151 100644 --- a/contracts/sources/fees.move +++ b/contracts/sources/fees.move @@ -15,11 +15,11 @@ module liquid_staking::fees { staked_sui_redeem_fee_bps: u64, // unused spread_fee_bps: u64, custom_redeem_fee_bps: u64, // unused - extra_fields: Bag // in case we add other fees later + extra_fields: Bag, // in case we add other fees later } public struct FeeConfigBuilder { - fields: Bag + fields: Bag, } public fun sui_mint_fee_bps(fees: &FeeConfig): u64 { @@ -65,7 +65,10 @@ module liquid_staking::fees { self } - public fun set_staked_sui_redeem_fee_bps(mut self: FeeConfigBuilder, fee: u64): FeeConfigBuilder { + public fun set_staked_sui_redeem_fee_bps( + mut self: FeeConfigBuilder, + fee: u64, + ): FeeConfigBuilder { bag::add(&mut self.fields, b"staked_sui_redeem_fee_bps", fee); self } @@ -84,7 +87,7 @@ module liquid_staking::fees { let FeeConfigBuilder { mut fields } = builder; let fees = FeeConfig { - sui_mint_fee_bps:if (bag::contains(&fields, b"sui_mint_fee_bps")) { + sui_mint_fee_bps: if (bag::contains(&fields, b"sui_mint_fee_bps")) { bag::remove(&mut fields, b"sui_mint_fee_bps") } else { 0 @@ -114,7 +117,7 @@ module liquid_staking::fees { } else { 0 }, - extra_fields: fields + extra_fields: fields, }; validate_fees(&fees); @@ -128,7 +131,7 @@ module liquid_staking::fees { } // Note that while it's technically exploitable, we allow lsts to be created with 0 mint/redeem fees. - // This is because having a 0 fee LST is useful in certain cases where mint/redemption can only be done by + // This is because having a 0 fee LST is useful in certain cases where mint/redemption can only be done by // a single party. It is up to the pool creator to ensure that the fees are set correctly. fun validate_fees(fees: &FeeConfig) { assert!(fees.sui_mint_fee_bps <= MAX_BPS, EInvalidFeeConfig); @@ -166,7 +169,8 @@ module liquid_staking::fees { (((sui_amount as u128) * (self.custom_redeem_fee_bps as u128) + 9999) / 10_000) as u64 } - #[test_only] use sui::test_scenario::{Self, Scenario}; + #[test_only] + use sui::test_scenario; #[test] public fun test_validate_fees_happy() { @@ -178,12 +182,12 @@ module liquid_staking::fees { staked_sui_redeem_fee_bps: 10_000, spread_fee_bps: 10_000, custom_redeem_fee_bps: 10_000, - extra_fields: bag::new(scenario.ctx()) + extra_fields: bag::new(scenario.ctx()), }; validate_fees(&fees); - sui::test_utils::destroy(fees); + std::unit_test::destroy(fees); scenario.end(); } @@ -198,12 +202,12 @@ module liquid_staking::fees { staked_sui_redeem_fee_bps: 10_000, spread_fee_bps: 10_000, custom_redeem_fee_bps: 10_000, - extra_fields: bag::new(scenario.ctx()) + extra_fields: bag::new(scenario.ctx()), }; validate_fees(&fees); - sui::test_utils::destroy(fees); + std::unit_test::destroy(fees); scenario.end(); } @@ -217,15 +221,15 @@ module liquid_staking::fees { staked_sui_redeem_fee_bps: 10_000, spread_fee_bps: 10_000, custom_redeem_fee_bps: 10_000, - extra_fields: bag::new(scenario.ctx()) + extra_fields: bag::new(scenario.ctx()), }; - assert!(calculate_mint_fee(&fees, 1) == 1, 0); - assert!(calculate_mint_fee(&fees, 99) == 1, 0); - assert!(calculate_mint_fee(&fees, 100) == 1, 0); - assert!(calculate_mint_fee(&fees, 101) == 2, 0); + assert!(calculate_mint_fee(&fees, 1) == 1); + assert!(calculate_mint_fee(&fees, 99) == 1); + assert!(calculate_mint_fee(&fees, 100) == 1); + assert!(calculate_mint_fee(&fees, 101) == 2); - sui::test_utils::destroy(fees); + std::unit_test::destroy(fees); scenario.end(); } @@ -239,15 +243,15 @@ module liquid_staking::fees { staked_sui_redeem_fee_bps: 10_000, spread_fee_bps: 10_000, custom_redeem_fee_bps: 10_000, - extra_fields: bag::new(scenario.ctx()) + extra_fields: bag::new(scenario.ctx()), }; - assert!(calculate_redeem_fee(&fees, 1) == 1, 0); - assert!(calculate_redeem_fee(&fees, 99) == 1, 0); - assert!(calculate_redeem_fee(&fees, 100) == 1, 0); - assert!(calculate_redeem_fee(&fees, 101) == 2, 0); + assert!(calculate_redeem_fee(&fees, 1) == 1); + assert!(calculate_redeem_fee(&fees, 99) == 1); + assert!(calculate_redeem_fee(&fees, 100) == 1); + assert!(calculate_redeem_fee(&fees, 101) == 2); - sui::test_utils::destroy(fees); + std::unit_test::destroy(fees); scenario.end(); } } diff --git a/contracts/sources/hooks/weight.move b/contracts/sources/hooks/weight.move index 7f3134a..68651ec 100644 --- a/contracts/sources/hooks/weight.move +++ b/contracts/sources/hooks/weight.move @@ -1,21 +1,16 @@ -/// This module allows for the dynamic rebalancing of validator stakes based +/// This module allows for the dynamic rebalancing of validator stakes based /// on a set of validator addresses and weights. Rebalance can be called permissionlessly module liquid_staking::weight { - use sui_system::sui_system::{SuiSystemState}; - use liquid_staking::liquid_staking::{LiquidStakingInfo, AdminCap}; - use liquid_staking::fees::{FeeConfig}; - use sui::vec_map::{Self, VecMap}; - use sui::bag::{Self, Bag}; - use liquid_staking::liquid_staking::{CustomRedeemRequest}; - use liquid_staking::version::{Self, Version}; - use sui::package; - use sui::coin::Coin; - use sui::sui::SUI; - use liquid_staking::registry::{Registry}; - use std::type_name::{Self, TypeName}; - use liquid_staking::events::{emit_event}; - use std::string::String; - use std::ascii; + use liquid_staking::{ + events::emit_event, + fees::FeeConfig, + liquid_staking::{LiquidStakingInfo, AdminCap, CustomRedeemRequest}, + registry::Registry, + version::{Self, Version} + }; + use std::{ascii, string::String, type_name::{Self, TypeName}}; + use sui::{bag::{Self, Bag}, coin::Coin, package, sui::SUI, vec_map::{Self, VecMap}}; + use sui_system::sui_system::SuiSystemState; /* Constants */ const CURRENT_VERSION: u16 = 1; @@ -26,11 +21,11 @@ module liquid_staking::weight { total_weight: u64, admin_cap: AdminCap

, version: Version, - extra_fields: Bag + extra_fields: Bag, } public struct WeightHookAdminCap has key, store { - id: UID + id: UID, } public struct WEIGHT has drop {} @@ -43,7 +38,7 @@ module liquid_staking::weight { public struct CreateEvent has copy, drop { typename: TypeName, weight_hook_id: ID, - weight_hook_admin_cap_id: ID + weight_hook_admin_cap_id: ID, } fun init(otw: WEIGHT, ctx: &mut TxContext) { @@ -52,20 +47,20 @@ module liquid_staking::weight { public fun new

( admin_cap: AdminCap

, - ctx: &mut TxContext + ctx: &mut TxContext, ): (WeightHook

, WeightHookAdminCap

) { let weight_hook = WeightHook { - id: object::new(ctx), - validator_addresses_and_weights: vec_map::empty(), - total_weight: 0, - admin_cap, - version: version::new(CURRENT_VERSION), - extra_fields: bag::new(ctx) + id: object::new(ctx), + validator_addresses_and_weights: vec_map::empty(), + total_weight: 0, + admin_cap, + version: version::new(CURRENT_VERSION), + extra_fields: bag::new(ctx), }; let weight_hook_admin_cap = WeightHookAdminCap { id: object::new(ctx) }; emit_event(CreateEvent { - typename: type_name::get

(), + typename: type_name::with_defining_ids

(), weight_hook_id: *weight_hook.id.as_inner(), weight_hook_admin_cap_id: *weight_hook_admin_cap.id.as_inner(), }); @@ -83,7 +78,7 @@ module liquid_staking::weight { liquid_staking_info, RegistryInfo { weight_hook_id: *self.id.as_inner(), - } + }, ); } @@ -131,7 +126,7 @@ module liquid_staking::weight { self: &mut WeightHook

, system_state: &mut SuiSystemState, liquid_staking_info: &mut LiquidStakingInfo

, - ctx: &mut TxContext + ctx: &mut TxContext, ) { liquid_staking_info.refresh(system_state, ctx); let total_sui_supply = liquid_staking_info.storage().total_sui_supply(); // we want to allocate the unaccrued spread fees as well @@ -144,12 +139,14 @@ module liquid_staking::weight { system_state: &mut SuiSystemState, liquid_staking_info: &mut LiquidStakingInfo

, request: &mut CustomRedeemRequest

, - ctx: &mut TxContext + ctx: &mut TxContext, ) { liquid_staking_info.refresh(system_state, ctx); let total_sui_supply = liquid_staking_info.storage().total_sui_supply(); // we want to allocate the unaccrued spread fees as well - let sui_unstake_amount = liquid_staking_info.lst_amount_to_sui_amount(request.lst().value()); + let sui_unstake_amount = liquid_staking_info.lst_amount_to_sui_amount(request + .lst() + .value()); let total_sui_to_allocate = total_sui_supply - sui_unstake_amount; self.rebalance_internal(system_state, liquid_staking_info, total_sui_to_allocate, ctx); @@ -169,17 +166,21 @@ module liquid_staking::weight { let admin_cap = &self.admin_cap; liquid_staking_info.update_metadata( - admin_cap, metadata, name, symbol, description, icon_url + admin_cap, + metadata, + name, + symbol, + description, + icon_url, ); } - fun rebalance_internal

( self: &mut WeightHook

, system_state: &mut SuiSystemState, liquid_staking_info: &mut LiquidStakingInfo

, total_sui_to_allocate: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ) { self.version.assert_version_and_upgrade(CURRENT_VERSION); if (self.total_weight == 0) { @@ -197,14 +198,21 @@ module liquid_staking::weight { }); // 2. calculate current and target amounts of sui for each validator - let (validator_addresses, validator_weights) = validator_addresses_and_weights.into_keys_values(); - - let validator_target_amounts = validator_weights.map!(|weight| { - ((total_sui_to_allocate as u128) * (weight as u128) / (self.total_weight as u128)) as u64 + let ( + validator_addresses, + validator_weights, + ) = validator_addresses_and_weights.into_keys_values(); + + let validator_target_amounts = validator_weights.map!(|weight| { + ( + (total_sui_to_allocate as u128) * (weight as u128) / (self.total_weight as u128), + ) as u64 }); let validator_current_amounts = validator_addresses.map_ref!(|validator_address| { - let validator_index = liquid_staking_info.storage().find_validator_index_by_address(*validator_address); + let validator_index = liquid_staking_info + .storage() + .find_validator_index_by_address(*validator_address); if (validator_index >= liquid_staking_info.storage().validators().length()) { return 0 }; @@ -221,7 +229,7 @@ module liquid_staking::weight { system_state, validator_addresses[i], validator_current_amounts[i] - validator_target_amounts[i], - ctx + ctx, ); }; }); @@ -234,16 +242,13 @@ module liquid_staking::weight { system_state, validator_addresses[i], validator_target_amounts[i] - validator_current_amounts[i], - ctx + ctx, ); }; }); } - public fun eject

( - mut self: WeightHook

, - admin_cap: WeightHookAdminCap

, - ): AdminCap

{ + public fun eject

(mut self: WeightHook

, admin_cap: WeightHookAdminCap

): AdminCap

{ self.version.assert_version_and_upgrade(CURRENT_VERSION); let WeightHookAdminCap { id } = admin_cap; @@ -256,10 +261,7 @@ module liquid_staking::weight { admin_cap } - public fun admin_cap

( - self: &WeightHook

, - _: &WeightHookAdminCap

- ): &AdminCap

{ + public fun admin_cap

(self: &WeightHook

, _: &WeightHookAdminCap

): &AdminCap

{ self.version.assert_version(CURRENT_VERSION); &self.admin_cap diff --git a/contracts/sources/liquid_staking.move b/contracts/sources/liquid_staking.move index b4bc1b3..9afc738 100644 --- a/contracts/sources/liquid_staking.move +++ b/contracts/sources/liquid_staking.move @@ -1,21 +1,21 @@ /// Module: liquid_staking module liquid_staking::liquid_staking { - use sui::balance::{Self, Balance}; - use sui_system::sui_system::{SuiSystemState}; - use sui::coin::{Self, Coin}; - use sui::sui::SUI; - use liquid_staking::storage::{Self, Storage}; - use sui::bag::{Self, Bag}; - use liquid_staking::fees::{FeeConfig}; - use liquid_staking::cell::{Self, Cell}; - use sui::coin::{TreasuryCap, CoinMetadata}; - use liquid_staking::version::{Self, Version}; - use liquid_staking::events::{emit_event}; - use sui_system::staking_pool::{FungibleStakedSui}; - use std::type_name::{Self, TypeName}; - use sui::package; - use std::string::String; - use std::ascii; + use liquid_staking::{ + cell::{Self, Cell}, + events::emit_event, + fees::FeeConfig, + storage::{Self, Storage}, + version::{Self, Version} + }; + use std::{ascii, string::String, type_name::{Self, TypeName}}; + use sui::{ + bag::{Self, Bag}, + balance::{Self, Balance}, + coin::{Self, Coin, TreasuryCap, CoinMetadata}, + package, + sui::SUI + }; + use sui_system::{staking_pool::FungibleStakedSui, sui_system::SuiSystemState}; /* Errors */ const EInvalidLstCreation: u64 = 0; @@ -40,55 +40,55 @@ module liquid_staking::liquid_staking { accrued_spread_fees: u64, storage: Storage, version: Version, - extra_fields: Bag + extra_fields: Bag, } - public struct AdminCap has key, store { - id: UID + public struct AdminCap has key, store { + id: UID, } /// hot potato that indicates a custom redeem request #[allow(lint(coin_field))] public struct CustomRedeemRequest { lst: Coin

, - request_processed: bool // the hook will mark this as true + request_processed: bool, // the hook will mark this as true } /* Events */ public struct CreateEvent has copy, drop { typename: TypeName, - liquid_staking_info_id: ID + liquid_staking_info_id: ID, } public struct MintEvent has copy, drop { typename: TypeName, sui_amount_in: u64, lst_amount_out: u64, - fee_amount: u64 + fee_amount: u64, } public struct RedeemEvent has copy, drop { typename: TypeName, lst_amount_in: u64, sui_amount_out: u64, - fee_amount: u64 + fee_amount: u64, } public struct DecreaseValidatorStakeEvent has copy, drop { typename: TypeName, staking_pool_id: ID, - amount: u64 + amount: u64, } public struct IncreaseValidatorStakeEvent has copy, drop { typename: TypeName, staking_pool_id: ID, - amount: u64 + amount: u64, } public struct CollectFeesEvent has copy, drop { typename: TypeName, - amount: u64 + amount: u64, } public struct EpochChangedEvent has copy, drop { @@ -96,12 +96,12 @@ module liquid_staking::liquid_staking { old_sui_supply: u64, new_sui_supply: u64, lst_supply: u64, - spread_fee: u64 + spread_fee: u64, } /* Public View Functions */ - // returns total sui managed by the LST. Note that this value might be out of date if the + // returns total sui managed by the LST. Note that this value might be out of date if the // LiquidStakingInfo object is out of date. public fun total_sui_supply

(self: &LiquidStakingInfo

): u64 { self.storage.total_sui_supply() - self.accrued_spread_fees @@ -162,9 +162,9 @@ module liquid_staking::liquid_staking { } public fun create_lst( - fee_config: FeeConfig, + fee_config: FeeConfig, lst_treasury_cap: TreasuryCap

, - ctx: &mut TxContext + ctx: &mut TxContext, ): (AdminCap

, LiquidStakingInfo

) { assert!(lst_treasury_cap.total_supply() == 0, EInvalidLstCreation); @@ -173,17 +173,17 @@ module liquid_staking::liquid_staking { fee_config, lst_treasury_cap, storage, - ctx + ctx, ) } public fun create_lst_with_stake( - system_state: &mut SuiSystemState, - fee_config: FeeConfig, + system_state: &mut SuiSystemState, + fee_config: FeeConfig, lst_treasury_cap: TreasuryCap

, mut fungible_staked_suis: vector, sui: Coin, - ctx: &mut TxContext + ctx: &mut TxContext, ): (AdminCap

, LiquidStakingInfo

) { let mut storage = storage::new(ctx); while (!fungible_staked_suis.is_empty()) { @@ -191,14 +191,17 @@ module liquid_staking::liquid_staking { storage.join_fungible_stake( system_state, fungible_staked_sui, - ctx + ctx, ); }; vector::destroy_empty(fungible_staked_suis); storage.join_to_sui_pool(sui.into_balance()); - assert!(lst_treasury_cap.total_supply() > 0 && storage.total_sui_supply() > 0, EInvalidLstCreation); + assert!( + lst_treasury_cap.total_supply() > 0 && storage.total_sui_supply() > 0, + EInvalidLstCreation, + ); // make sure the lst ratio is in a sane range: let total_sui_supply = (storage.total_sui_supply() as u128); @@ -206,28 +209,28 @@ module liquid_staking::liquid_staking { assert!( (total_sui_supply >= total_lst_supply) && (total_sui_supply <= 2 * total_lst_supply), // total_sui_supply / total_lst_supply <= 2 - EInvalidLstCreation + EInvalidLstCreation, ); create_lst_with_storage( fee_config, lst_treasury_cap, storage, - ctx + ctx, ) } fun create_lst_with_storage( - fee_config: FeeConfig, + fee_config: FeeConfig, lst_treasury_cap: TreasuryCap

, storage: Storage, - ctx: &mut TxContext + ctx: &mut TxContext, ): (AdminCap

, LiquidStakingInfo

) { let uid = object::new(ctx); emit_event(CreateEvent { - typename: type_name::get

(), - liquid_staking_info_id: uid.to_inner() + typename: type_name::with_defining_ids

(), + liquid_staking_info_id: uid.to_inner(), }); ( @@ -240,17 +243,17 @@ module liquid_staking::liquid_staking { accrued_spread_fees: 0, storage, version: version::new(CURRENT_VERSION), - extra_fields: bag::new(ctx) - } + extra_fields: bag::new(ctx), + }, ) } // User operations public fun mint( - self: &mut LiquidStakingInfo

, - system_state: &mut SuiSystemState, - sui: Coin, - ctx: &mut TxContext + self: &mut LiquidStakingInfo

, + system_state: &mut SuiSystemState, + sui: Coin, + ctx: &mut TxContext, ): Coin

{ self.refresh(system_state, ctx); @@ -263,15 +266,15 @@ module liquid_staking::liquid_staking { // deduct fees let mint_fee_amount = self.fee_config.get().calculate_mint_fee(sui_balance.value()); self.fees.join(sui_balance.split(mint_fee_amount)); - + let lst_mint_amount = self.sui_amount_to_lst_amount(sui_balance.value()); assert!(lst_mint_amount > 0, EZeroLstMinted); emit_event(MintEvent { - typename: type_name::get

(), + typename: type_name::with_defining_ids

(), sui_amount_in, lst_amount_out: lst_mint_amount, - fee_amount: mint_fee_amount + fee_amount: mint_fee_amount, }); let lst = self.lst_treasury_cap.mint(lst_mint_amount, ctx); @@ -281,7 +284,7 @@ module liquid_staking::liquid_staking { assert!( ((lst.value() as u128) * old_sui_supply <= (sui_balance.value() as u128) * old_lst_supply) || (old_sui_supply > 0 && old_lst_supply == 0), // special case - EMintInvariantViolated + EMintInvariantViolated, ); self.storage.join_to_sui_pool(sui_balance); @@ -292,8 +295,8 @@ module liquid_staking::liquid_staking { public fun redeem( self: &mut LiquidStakingInfo

, lst: Coin

, - system_state: &mut SuiSystemState, - ctx: &mut TxContext + system_state: &mut SuiSystemState, + ctx: &mut TxContext, ): Coin { self.redeem_internal(lst, system_state, false, ctx) } @@ -301,9 +304,9 @@ module liquid_staking::liquid_staking { fun redeem_internal( self: &mut LiquidStakingInfo

, lst: Coin

, - system_state: &mut SuiSystemState, + system_state: &mut SuiSystemState, is_custom_redeem: bool, - ctx: &mut TxContext + ctx: &mut TxContext, ): Coin { self.refresh(system_state, ctx); @@ -331,17 +334,17 @@ module liquid_staking::liquid_staking { assert!(sui.value() > 0, ERedeemInvariantViolated); emit_event(RedeemEvent { - typename: type_name::get

(), + typename: type_name::with_defining_ids

(), lst_amount_in: lst.value(), sui_amount_out: sui.value(), - fee_amount: redeem_fee_amount + fee_amount: redeem_fee_amount, }); // invariant: sui_out / lst_in <= old_sui_supply / old_lst_supply // -> sui_out * old_lst_supply <= lst_in * old_sui_supply assert!( (sui.value() as u128 + (redeem_fee_amount as u128)) * old_lst_supply <= (lst.value() as u128) * old_sui_supply, - ERedeemInvariantViolated + ERedeemInvariantViolated, ); self.lst_treasury_cap.burn(lst); @@ -353,7 +356,7 @@ module liquid_staking::liquid_staking { self: &mut LiquidStakingInfo

, lst: Coin

, system_state: &mut SuiSystemState, - ctx: &mut TxContext + ctx: &mut TxContext, ): CustomRedeemRequest

{ self.version.assert_version_and_upgrade(CURRENT_VERSION); self.refresh(system_state, ctx); @@ -364,8 +367,8 @@ module liquid_staking::liquid_staking { public fun custom_redeem( self: &mut LiquidStakingInfo

, request: CustomRedeemRequest

, - system_state: &mut SuiSystemState, - ctx: &mut TxContext + system_state: &mut SuiSystemState, + ctx: &mut TxContext, ): Coin { let CustomRedeemRequest { lst, request_processed } = request; assert!(request_processed, ERequestNotProcessed); @@ -378,18 +381,18 @@ module liquid_staking::liquid_staking { self: &mut LiquidStakingInfo

, _: &AdminCap

, validator_index: u64, - new_validator_index: u64 + new_validator_index: u64, ) { self.storage.change_validator_priority(validator_index, new_validator_index); } - + public fun increase_validator_stake

( self: &mut LiquidStakingInfo

, _: &AdminCap

, system_state: &mut SuiSystemState, validator_address: address, sui_amount: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ): u64 { self.refresh(system_state, ctx); @@ -402,45 +405,47 @@ module liquid_staking::liquid_staking { let staked_sui = system_state.request_add_stake_non_entry( coin::from_balance(sui, ctx), validator_address, - ctx + ctx, ); let staked_sui_amount = staked_sui.staked_sui_amount(); emit_event(IncreaseValidatorStakeEvent { - typename: type_name::get

(), + typename: type_name::with_defining_ids

(), staking_pool_id: staked_sui.pool_id(), - amount: staked_sui.staked_sui_amount() + amount: staked_sui.staked_sui_amount(), }); self.storage.join_stake(system_state, staked_sui, ctx); staked_sui_amount } - + public fun decrease_validator_stake

( self: &mut LiquidStakingInfo

, _: &AdminCap

, system_state: &mut SuiSystemState, validator_address: address, target_unstake_sui_amount: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ): u64 { self.refresh(system_state, ctx); let validator_index = self.storage.find_validator_index_by_address(validator_address); assert!(validator_index < self.storage.validators().length(), EValidatorNotFound); - let sui_amount = self.storage.unstake_approx_n_sui_from_validator( - system_state, - validator_index, - target_unstake_sui_amount, - ctx - ); + let sui_amount = self + .storage + .unstake_approx_n_sui_from_validator( + system_state, + validator_index, + target_unstake_sui_amount, + ctx, + ); emit_event(DecreaseValidatorStakeEvent { - typename: type_name::get

(), + typename: type_name::with_defining_ids

(), staking_pool_id: self.storage.validators()[validator_index].staking_pool_id(), - amount: sui_amount + amount: sui_amount, }); sui_amount @@ -450,7 +455,7 @@ module liquid_staking::liquid_staking { self: &mut LiquidStakingInfo

, system_state: &mut SuiSystemState, _admin_cap: &AdminCap

, - ctx: &mut TxContext + ctx: &mut TxContext, ): Coin { self.refresh(system_state, ctx); @@ -461,8 +466,8 @@ module liquid_staking::liquid_staking { fees.join(spread_fees); emit_event(CollectFeesEvent { - typename: type_name::get

(), - amount: fees.value() + typename: type_name::with_defining_ids

(), + amount: fees.value(), }); coin::from_balance(fees, ctx) @@ -481,25 +486,28 @@ module liquid_staking::liquid_staking { // returns true if the object was refreshed public fun refresh

( - self: &mut LiquidStakingInfo

, - system_state: &mut SuiSystemState, - ctx: &mut TxContext + self: &mut LiquidStakingInfo

, + system_state: &mut SuiSystemState, + ctx: &mut TxContext, ): bool { self.version.assert_version_and_upgrade(CURRENT_VERSION); let old_total_supply = self.total_sui_supply(); - if (self.storage.refresh(system_state, ctx)) { // epoch rolled over + let epoch_rolled_over = self.storage.refresh(system_state, ctx); + if (epoch_rolled_over) { let new_total_supply = self.total_sui_supply(); // don't think we need to keep track of this in fixed point. - // If there's 1 SUI staked, and the yearly rewards is 1%, then + // If there's 1 SUI staked, and the yearly rewards is 1%, then // the spread fee in 1 epoch is 1 * 0.01 / 365 = 0.0000274 SUI => 27400 MIST // ie very unlikely to round spread fees to 0. let spread_fee = if (new_total_supply > old_total_supply) { - (((new_total_supply - old_total_supply) as u128) + ( + ((new_total_supply - old_total_supply) as u128) * (self.fee_config.get().spread_fee_bps() as u128) - / (10_000 as u128)) as u64 + / (10_000u128), + ) as u64 } else { 0 }; @@ -507,11 +515,11 @@ module liquid_staking::liquid_staking { self.accrued_spread_fees = self.accrued_spread_fees + spread_fee; emit_event(EpochChangedEvent { - typename: type_name::get

(), + typename: type_name::with_defining_ids

(), old_sui_supply: old_total_supply, new_sui_supply: new_total_supply, lst_supply: self.total_lst_supply(), - spread_fee + spread_fee, }); return true @@ -550,17 +558,14 @@ module liquid_staking::liquid_staking { public(package) fun mark_redeem_request_as_processed

( _: &AdminCap

, - request: &mut CustomRedeemRequest

+ request: &mut CustomRedeemRequest

, ) { request.request_processed = true; } /* Private Functions */ - fun sui_amount_to_lst_amount

( - self: &LiquidStakingInfo

, - sui_amount: u64 - ): u64 { + fun sui_amount_to_lst_amount

(self: &LiquidStakingInfo

, sui_amount: u64): u64 { let total_sui_supply = self.total_sui_supply(); let total_lst_supply = self.total_lst_supply(); @@ -568,7 +573,8 @@ module liquid_staking::liquid_staking { return sui_amount }; - let lst_amount = (total_lst_supply as u128) + let lst_amount = + (total_lst_supply as u128) * (sui_amount as u128) / (total_sui_supply as u128); @@ -576,15 +582,16 @@ module liquid_staking::liquid_staking { } public(package) fun lst_amount_to_sui_amount

( - self: &LiquidStakingInfo

, - lst_amount: u64 + self: &LiquidStakingInfo

, + lst_amount: u64, ): u64 { let total_sui_supply = self.total_sui_supply(); let total_lst_supply = self.total_lst_supply(); assert!(total_lst_supply > 0, EZeroLstSupply); - let sui_amount = (total_sui_supply as u128) + let sui_amount = + (total_sui_supply as u128) * (lst_amount as u128) / (total_lst_supply as u128); diff --git a/contracts/sources/registry.move b/contracts/sources/registry.move index c05880d..5623d5c 100644 --- a/contracts/sources/registry.move +++ b/contracts/sources/registry.move @@ -1,15 +1,14 @@ module liquid_staking::registry { + use liquid_staking::{liquid_staking::{AdminCap, LiquidStakingInfo}, version::{Self, Version}}; + use std::type_name; use sui::bag::{Self, Bag}; - use liquid_staking::liquid_staking::{AdminCap, LiquidStakingInfo}; - use std::type_name::{Self}; - use liquid_staking::version::{Self, Version}; const CURRENT_VERSION: u16 = 1; public struct Registry has key, store { id: UID, version: Version, - table: Bag, + table: Bag, } public struct Entry has copy, store { @@ -43,25 +42,27 @@ module liquid_staking::registry { public(package) fun add_to_registry( self: &mut Registry, - admin_cap: &AdminCap, + admin_cap: &AdminCap, liquid_staking_info: &LiquidStakingInfo, extra_info: ExtraInfoType, ) { self.version.assert_version_and_upgrade(CURRENT_VERSION); - self.table.add( - type_name::get(), - Entry { - admin_cap_id: object::id(admin_cap), - liquid_staking_info_id: object::id(liquid_staking_info), - extra_info, - } - ); + self + .table + .add( + type_name::with_defining_ids(), + Entry { + admin_cap_id: object::id(admin_cap), + liquid_staking_info_id: object::id(liquid_staking_info), + extra_info, + }, + ); } public(package) fun get_entry( self: &Registry, ): &Entry { - self.table.borrow(type_name::get()) + self.table.borrow(type_name::with_defining_ids()) } } diff --git a/contracts/sources/storage.move b/contracts/sources/storage.move index 25f1678..e729823 100644 --- a/contracts/sources/storage.move +++ b/contracts/sources/storage.move @@ -1,10 +1,10 @@ module liquid_staking::storage { - use sui_system::staking_pool::{StakedSui, FungibleStakedSui, PoolTokenExchangeRate}; - use sui::sui::SUI; - use sui::balance::{Self, Balance}; - use sui_system::sui_system::{SuiSystemState}; use std::u64::{min, max}; - use sui::bag::{Self, Bag}; + use sui::{bag::{Self, Bag}, balance::{Self, Balance}, sui::SUI}; + use sui_system::{ + staking_pool::{StakedSui, FungibleStakedSui, PoolTokenExchangeRate}, + sui_system::SuiSystemState + }; /* Errors */ const ENotEnoughSuiInSuiPool: u64 = 0; @@ -20,17 +20,17 @@ module liquid_staking::storage { /// The Storage struct holds all stake for the LST. public struct Storage has store { - /// Sui balance. Unstake operations deposit SUI here. + /// Sui balance. Unstake operations deposit SUI here. sui_pool: Balance, /// Validators that have stake in the LST. validator_infos: vector, - /// Total Sui managed by the LST. This is the sum of all active + /// Total Sui managed by the LST. This is the sum of all active /// stake, inactive stake, and SUI in the sui_pool. total_sui_supply: u64, /// The epoch at which the storage was last refreshed. last_refresh_epoch: u64, /// Extra fields for future-proofing. - extra_fields: Bag + extra_fields: Bag, } /// ValidatorInfo holds all stake for a single validator. @@ -48,7 +48,7 @@ module liquid_staking::storage { /// The total Sui staked to the validator (active stake + inactive stake). total_sui_amount: u64, /// Extra fields for future-proofing. - extra_fields: Bag + extra_fields: Bag, } public(package) fun new(ctx: &mut TxContext): Storage { @@ -57,7 +57,7 @@ module liquid_staking::storage { validator_infos: vector::empty(), total_sui_supply: 0, last_refresh_epoch: ctx.epoch(), - extra_fields: bag::new(ctx) + extra_fields: bag::new(ctx), } } @@ -102,7 +102,10 @@ module liquid_staking::storage { self.total_sui_amount } - public(package) fun find_validator_index_by_address(self: &Storage, validator_address: address): u64 { + public(package) fun find_validator_index_by_address( + self: &Storage, + validator_address: address, + ): u64 { let mut i = 0; while (i < self.validator_infos.length()) { if (self.validator_infos[i].validator_address == validator_address) { @@ -127,11 +130,10 @@ module liquid_staking::storage { /// - Removes validators that have no stake. /// Returns true if the storage was updated. public(package) fun refresh( - self: &mut Storage, - system_state: &mut SuiSystemState, - ctx: &mut TxContext + self: &mut Storage, + system_state: &mut SuiSystemState, + ctx: &mut TxContext, ): bool { - if (self.last_refresh_epoch == ctx.epoch()) { return false }; @@ -153,7 +155,9 @@ module liquid_staking::storage { }; if (self.validator_infos[i].is_empty()) { - let ValidatorInfo { active_stake, inactive_stake, extra_fields, .. } = self.validator_infos.remove(i); + let ValidatorInfo { active_stake, inactive_stake, extra_fields, .. } = self + .validator_infos + .remove(i); active_stake.destroy_none(); inactive_stake.destroy_none(); extra_fields.destroy_empty(); @@ -165,7 +169,7 @@ module liquid_staking::storage { let latest_exchange_rate_opt = self.get_latest_exchange_rate( &self.validator_infos[i].staking_pool_id, system_state, - ctx + ctx, ); if (latest_exchange_rate_opt.is_some()) { @@ -176,26 +180,28 @@ module liquid_staking::storage { if (self.validator_infos[i].inactive_stake.is_some()) { let inactive_stake = self.take_from_inactive_stake(i); - let fungible_staked_sui = system_state.convert_to_fungible_staked_sui(inactive_stake, ctx); + let fungible_staked_sui = system_state.convert_to_fungible_staked_sui( + inactive_stake, + ctx, + ); self.join_fungible_staked_sui_to_validator(i, fungible_staked_sui); }; - }; self.last_refresh_epoch = ctx.epoch(); true } - // finds the latest exchange rate by searching backwards from current epoch + // finds the latest exchange rate by searching backwards from current epoch // to the storage's last refresh epoch. - // this may return none in the case where the staking pool is inactive or + // this may return none in the case where the staking pool is inactive or // if sui system is currently in safe mode. In both these cases, the storage // object has the latest exchange rate already. fun get_latest_exchange_rate( self: &Storage, staking_pool_id: &ID, system_state: &mut SuiSystemState, - ctx: &TxContext + ctx: &TxContext, ): Option { let exchange_rates = system_state.pool_exchange_rates(staking_pool_id); @@ -211,7 +217,7 @@ module liquid_staking::storage { option::none() } - /// Update the total sui amount for the validator and modify the + /// Update the total sui amount for the validator and modify the /// storage sui supply accordingly assumes the exchange rate is up to date fun refresh_validator_info(self: &mut Storage, i: u64) { let validator_info = &mut self.validator_infos[i]; @@ -221,8 +227,8 @@ module liquid_staking::storage { if (validator_info.active_stake.is_some()) { let active_stake = validator_info.active_stake.borrow(); let active_sui_amount = get_sui_amount( - &validator_info.exchange_rate, - active_stake.value() + &validator_info.exchange_rate, + active_stake.value(), ); total_sui_amount = total_sui_amount + active_sui_amount; @@ -242,9 +248,9 @@ module liquid_staking::storage { // the higher the validator index, the lower the priority. In this case, low priority means it'll // process unstake requests first public(package) fun change_validator_priority( - self: &mut Storage, - validator_index: u64, - new_validator_index: u64 + self: &mut Storage, + validator_index: u64, + new_validator_index: u64, ) { if (validator_index == new_validator_index) { return @@ -254,7 +260,6 @@ module liquid_staking::storage { self.validator_infos.insert(validator_info, new_validator_index); } - /* Join Functions */ public(package) fun join_to_sui_pool(self: &mut Storage, sui: Balance) { self.total_sui_supply = self.total_sui_supply + sui.value(); @@ -262,15 +267,15 @@ module liquid_staking::storage { } public(package) fun join_stake( - self: &mut Storage, + self: &mut Storage, system_state: &mut SuiSystemState, - stake: StakedSui, - ctx: &mut TxContext + stake: StakedSui, + ctx: &mut TxContext, ) { let validator_index = self.get_or_add_validator_index_by_staking_pool_id_mut( - system_state, - stake.pool_id(), - ctx + system_state, + stake.pool_id(), + ctx, ); if (stake.stake_activation_epoch() <= ctx.epoch()) { @@ -282,22 +287,22 @@ module liquid_staking::storage { } public(package) fun join_fungible_stake( - self: &mut Storage, + self: &mut Storage, system_state: &mut SuiSystemState, fungible_staked_sui: FungibleStakedSui, - ctx: &mut TxContext + ctx: &mut TxContext, ) { let validator_index = self.get_or_add_validator_index_by_staking_pool_id_mut( - system_state, - fungible_staked_sui.pool_id(), - ctx + system_state, + fungible_staked_sui.pool_id(), + ctx, ); self.join_fungible_staked_sui_to_validator(validator_index, fungible_staked_sui); } fun join_inactive_stake_to_validator( - self: &mut Storage, + self: &mut Storage, validator_index: u64, stake: StakedSui, ) { @@ -313,14 +318,13 @@ module liquid_staking::storage { } fun join_fungible_staked_sui_to_validator( - self: &mut Storage, + self: &mut Storage, validator_index: u64, fungible_staked_sui: FungibleStakedSui, ) { let validator_info = &mut self.validator_infos[validator_index]; if (validator_info.active_stake.is_some()) { validator_info.active_stake.borrow_mut().join_fungible_staked_sui(fungible_staked_sui); - } else { validator_info.active_stake.fill(fungible_staked_sui); }; @@ -330,8 +334,8 @@ module liquid_staking::storage { /* Split/Take Functions */ public(package) fun split_up_to_n_sui_from_sui_pool( - self: &mut Storage, - max_sui_amount_out: u64 + self: &mut Storage, + max_sui_amount_out: u64, ): Balance { let sui_amount_out = min(self.sui_pool.value(), max_sui_amount_out); self.split_from_sui_pool(sui_amount_out) @@ -343,28 +347,34 @@ module liquid_staking::storage { } public(package) fun unstake_approx_n_sui_from_validator( - self: &mut Storage, + self: &mut Storage, system_state: &mut SuiSystemState, - validator_index: u64, + validator_index: u64, unstake_sui_amount: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ): u64 { - let mut amount = self.unstake_approx_n_sui_from_inactive_stake(system_state, validator_index, unstake_sui_amount, ctx); + let mut amount = self.unstake_approx_n_sui_from_inactive_stake( + system_state, + validator_index, + unstake_sui_amount, + ctx, + ); if (unstake_sui_amount > amount) { - amount = amount + self.unstake_approx_n_sui_from_active_stake(system_state, validator_index, unstake_sui_amount - amount, ctx); + amount = + amount + self.unstake_approx_n_sui_from_active_stake(system_state, validator_index, unstake_sui_amount - amount, ctx); }; amount } - // This function tries to unstake approximately n SUI. + // This function tries to unstake approximately n SUI. // the output amount should be bounded from [0, n + 1 * MIST_PER_SUI * pool_token_ratio) public(package) fun unstake_approx_n_sui_from_active_stake( - self: &mut Storage, + self: &mut Storage, system_state: &mut SuiSystemState, - validator_index: u64, + validator_index: u64, target_unstake_sui_amount: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ): u64 { if (target_unstake_sui_amount == 0) { return 0 @@ -377,22 +387,22 @@ module liquid_staking::storage { let fungible_staked_sui_amount = validator_info.active_stake.borrow().value(); let total_sui_amount = get_sui_amount( - &validator_info.exchange_rate, - fungible_staked_sui_amount + &validator_info.exchange_rate, + fungible_staked_sui_amount, ); let unstaked_sui = if (total_sui_amount <= target_unstake_sui_amount) { self.take_active_stake(system_state, validator_index, ctx) - } - else { + } else { // ceil(target_unstake_sui_amount * fungible_staked_sui_amount / total_sui_amount) - let split_amount = ( - ((target_unstake_sui_amount as u128) + let split_amount = + ( + ((target_unstake_sui_amount as u128) * (fungible_staked_sui_amount as u128) + (total_sui_amount as u128) - 1) - / (total_sui_amount as u128) - ) as u64; + / (total_sui_amount as u128), + ) as u64; self.split_from_active_stake(system_state, validator_index, split_amount as u64, ctx) }; @@ -405,11 +415,11 @@ module liquid_staking::storage { // The output should be bounded from [0, n + 1 * MIST_PER_SUI) Sui public(package) fun unstake_approx_n_sui_from_inactive_stake( - self: &mut Storage, + self: &mut Storage, system_state: &mut SuiSystemState, - validator_index: u64, + validator_index: u64, target_unstake_sui_amount: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ): u64 { if (target_unstake_sui_amount == 0) { return 0 @@ -425,8 +435,7 @@ module liquid_staking::storage { let staked_sui_amount = validator_info.inactive_stake.borrow().staked_sui_amount(); let staked_sui = if (staked_sui_amount < target_unstake_sui_amount + MIN_STAKE_THRESHOLD) { self.take_from_inactive_stake(validator_index) - } - else { + } else { self.split_from_inactive_stake(validator_index, target_unstake_sui_amount, ctx) }; @@ -442,7 +451,7 @@ module liquid_staking::storage { self: &mut Storage, system_state: &mut SuiSystemState, max_sui_amount_out: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ): Balance { { let mut i = self.validators().length(); @@ -454,7 +463,7 @@ module liquid_staking::storage { system_state, i, max_sui_amount_out - sui_pool_value, - ctx + ctx, ); }; }; @@ -468,11 +477,11 @@ module liquid_staking::storage { self.unstake_approx_n_sui_from_active_stake( system_state, i, - // unstake a bit more than required. + // unstake a bit more than required. // this is to account for the fact that redeeming active stake // will sometimes result in less sui than expected (roughly 2 mist) max_sui_amount_out - sui_pool_value + ACTIVE_STAKE_REDEEM_OFFSET, - ctx + ctx, ); }; }; @@ -483,15 +492,16 @@ module liquid_staking::storage { /* all split/unstake/take functions are built using the following 4 functions */ fun split_from_active_stake( - self: &mut Storage, + self: &mut Storage, system_state: &mut SuiSystemState, - validator_index: u64, + validator_index: u64, fungible_staked_sui_amount: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ): Balance { let validator_info = &mut self.validator_infos[validator_index]; - let stake = validator_info.active_stake + let stake = validator_info + .active_stake .borrow_mut() .split_fungible_staked_sui(fungible_staked_sui_amount, ctx); @@ -499,10 +509,10 @@ module liquid_staking::storage { } fun take_active_stake( - self: &mut Storage, + self: &mut Storage, system_state: &mut SuiSystemState, - validator_index: u64, - ctx: &TxContext + validator_index: u64, + ctx: &TxContext, ): Balance { let validator_info = &mut self.validator_infos[validator_index]; let fungible_staked_sui = validator_info.active_stake.extract(); @@ -511,25 +521,20 @@ module liquid_staking::storage { } fun split_from_inactive_stake( - self: &mut Storage, - validator_index: u64, + self: &mut Storage, + validator_index: u64, sui_amount_out: u64, - ctx: &mut TxContext + ctx: &mut TxContext, ): StakedSui { let validator_info = &mut self.validator_infos[validator_index]; - let stake = validator_info.inactive_stake - .borrow_mut() - .split(sui_amount_out, ctx); + let stake = validator_info.inactive_stake.borrow_mut().split(sui_amount_out, ctx); self.refresh_validator_info(validator_index); stake } - fun take_from_inactive_stake( - self: &mut Storage, - validator_index: u64, - ): StakedSui { + fun take_from_inactive_stake(self: &mut Storage, validator_index: u64): StakedSui { let validator_info = &mut self.validator_infos[validator_index]; let stake = validator_info.inactive_stake.extract(); @@ -544,7 +549,7 @@ module liquid_staking::storage { system_state: &mut SuiSystemState, validator_index: u64, fungible_staked_sui: FungibleStakedSui, - ctx: &TxContext + ctx: &TxContext, ): Balance { let redeemed_sui = system_state.redeem_fungible_staked_sui(fungible_staked_sui, ctx); let redeemed_amount = redeemed_sui.value(); @@ -564,10 +569,10 @@ module liquid_staking::storage { /* Private functions */ fun get_or_add_validator_index_by_staking_pool_id_mut( - self: &mut Storage, + self: &mut Storage, system_state: &mut SuiSystemState, staking_pool_id: ID, - ctx: &mut TxContext + ctx: &mut TxContext, ): u64 { let mut current_validator_addresses = vector[]; @@ -583,16 +588,10 @@ module liquid_staking::storage { let validator_address = system_state.validator_address_by_pool_id(&staking_pool_id); - assert!( - !current_validator_addresses.contains(&validator_address), - EValidatorAlreadyExists - ); + assert!(!current_validator_addresses.contains(&validator_address), EValidatorAlreadyExists); let active_validator_addresses = system_state.active_validator_addresses(); - assert!( - active_validator_addresses.contains(&validator_address), - ENotActiveValidator - ); + assert!(active_validator_addresses.contains(&validator_address), ENotActiveValidator); let exchange_rates = system_state.pool_exchange_rates(&staking_pool_id); // Search backwards to handle newly activated validators that may only @@ -603,15 +602,17 @@ module liquid_staking::storage { }; let latest_exchange_rate = exchange_rates.borrow(cur_epoch); - self.validator_infos.push_back(ValidatorInfo { - staking_pool_id: copy staking_pool_id, - validator_address, - active_stake: option::none(), - inactive_stake: option::none(), - exchange_rate: *latest_exchange_rate, - total_sui_amount: 0, - extra_fields: bag::new(ctx) - }); + self + .validator_infos + .push_back(ValidatorInfo { + staking_pool_id: copy staking_pool_id, + validator_address, + active_stake: option::none(), + inactive_stake: option::none(), + exchange_rate: *latest_exchange_rate, + total_sui_amount: 0, + extra_fields: bag::new(ctx), + }); assert!(self.validator_infos.length() <= MAX_VALIDATORS, ETooManyValidators); @@ -625,10 +626,10 @@ module liquid_staking::storage { if (exchange_rate.sui_amount() == 0 || exchange_rate.pool_token_amount() == 0) { return token_amount }; - let res = (exchange_rate.sui_amount() as u128) + let res = + (exchange_rate.sui_amount() as u128) * (token_amount as u128) / (exchange_rate.pool_token_amount() as u128); res as u64 } - } diff --git a/contracts/sources/version.move b/contracts/sources/version.move index 00defdd..3b0e42c 100644 --- a/contracts/sources/version.move +++ b/contracts/sources/version.move @@ -1,41 +1,29 @@ module liquid_staking::version { - // ===== Errors ===== // When the package called has an outdated version const EIncorrectVersion: u64 = 0; /// Capability object given to the pool creator - public struct Version has store, drop (u16) + public struct Version has drop, store (u16) - public(package) fun new( - version: u16, - ): Version { + public(package) fun new(version: u16): Version { Version(version) } - - public(package) fun migrate_( - version: &mut Version, - current_version: u16, - ) { + + public(package) fun migrate_(version: &mut Version, current_version: u16) { assert!(version.0 < current_version, EIncorrectVersion); version.0 = current_version; } - public(package) fun assert_version( - version: &Version, - current_version: u16, - ) { + public(package) fun assert_version(version: &Version, current_version: u16) { assert!(version.0 == current_version, EIncorrectVersion); } - public(package) fun assert_version_and_upgrade( - version: &mut Version, - current_version: u16, - ) { + public(package) fun assert_version_and_upgrade(version: &mut Version, current_version: u16) { if (version.0 < current_version) { version.0 = current_version; }; assert_version(version, current_version); } -} \ No newline at end of file +} diff --git a/contracts/tests/liquid_staking_tests.move b/contracts/tests/liquid_staking_tests.move index d277045..2c09f2d 100644 --- a/contracts/tests/liquid_staking_tests.move +++ b/contracts/tests/liquid_staking_tests.move @@ -1,656 +1,596 @@ -#[allow(deprecated_usage)] #[test_only] module liquid_staking::liquid_staking_tests { - // uncomment this line to import the module - use sui::address; - use sui_system::staking_pool::StakedSui; - use sui::test_scenario::{Self, Scenario}; - use sui_system::sui_system::SuiSystemState; - use sui::coin::{Self}; - use sui::sui::SUI; - use liquid_staking::liquid_staking::{create_lst, create_lst_with_stake}; - use liquid_staking::fees::{Self}; - use sui_system::governance_test_utils::{ - advance_epoch_with_reward_amounts, - create_sui_system_state_for_testing, + use liquid_staking::{ + fees, + liquid_staking::{create_lst, create_lst_with_stake}, + test_utils::{advance_epoch_no_rewards, advance_epoch_with_rewards, setup_runner} }; - use liquid_staking::test_utils::create_validators_with_stakes; + use sui::{coin, sui::SUI, test_scenario}; + use sui_system::sui_system::SuiSystemState; /* Constants */ const MIST_PER_SUI: u64 = 1_000_000_000; - #[test_only] - public fun stake_with(validator_index: u64, amount: u64, scenario: &mut Scenario): StakedSui { - scenario.next_tx(@0x0); - - let mut system_state = scenario.take_shared(); - - let ctx = scenario.ctx(); - - let staked_sui = system_state.request_add_stake_non_entry( - coin::mint_for_testing(amount * MIST_PER_SUI, ctx), - address::from_u256(validator_index as u256), - ctx - ); - - test_scenario::return_shared(system_state); - scenario.next_tx(@0x0); - - staked_sui - } - - fun setup_sui_system(scenario: &mut Scenario, stakes: vector) { - let validators = create_validators_with_stakes(stakes, scenario.ctx()); - create_sui_system_state_for_testing(validators, 0, 0, scenario.ctx()); - - advance_epoch_with_reward_amounts(0, 0, scenario); - } - - public struct TEST has drop {} - - #[test] - fun test_create_lst() { - let mut scenario = test_scenario::begin(@0x0); + public struct TEST has drop {} - setup_sui_system(&mut scenario, vector[100, 100]); + #[test] + fun test_create_lst() { + let mut runner = setup_runner(vector[100, 100]); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let system_state = scenario.take_shared(); + let system_state = runner.scenario_mut().take_shared(); let (admin_cap, lst_info) = create_lst( - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - assert!(lst_info.total_lst_supply() == 0, 0); - assert!(lst_info.storage().total_sui_supply() == 0, 0); + assert!(lst_info.total_lst_supply() == 0); + assert!(lst_info.storage().total_sui_supply() == 0); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); - } + runner.finish(); + } #[test] fun test_create_lst_with_stake_happy() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); - let staked_sui = stake_with(0, 100, &mut scenario); + advance_epoch_no_rewards(&mut runner); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - - let mut system_state = scenario.take_shared(); - let fungible_staked_sui = system_state.convert_to_fungible_staked_sui(staked_sui, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let fungible_staked_sui = system_state.convert_to_fungible_staked_sui( + staked_sui, + runner.ctx(), + ); // Create a treasury cap with non-zero coins - let mut treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); - let coins = treasury_cap.mint(200 * MIST_PER_SUI, scenario.ctx()); + let mut treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); + let coins = treasury_cap.mint(200 * MIST_PER_SUI, runner.ctx()); let (admin_cap, lst_info) = create_lst_with_stake( &mut system_state, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), treasury_cap, vector[fungible_staked_sui], - coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()), - scenario.ctx() + coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()), + runner.ctx(), ); - assert!(lst_info.total_lst_supply() == 200 * MIST_PER_SUI, 0); - assert!(lst_info.storage().total_sui_supply() == 200 * MIST_PER_SUI, 0); + assert!(lst_info.total_lst_supply() == 200 * MIST_PER_SUI); + assert!(lst_info.storage().total_sui_supply() == 200 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); - sui::test_utils::destroy( coins); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); + std::unit_test::destroy(coins); - scenario.end(); + runner.finish(); } #[test] #[expected_failure(abort_code = 0, location = liquid_staking::liquid_staking)] fun test_create_lst_fail() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); - setup_sui_system(&mut scenario, vector[100, 100]); + let system_state = runner.scenario_mut().take_shared(); - let system_state = scenario.take_shared(); - - let mut treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); - let coins = treasury_cap.mint(1000 * MIST_PER_SUI, scenario.ctx()); + let mut treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); + let coins = treasury_cap.mint(1000 * MIST_PER_SUI, runner.ctx()); let (admin_cap, lst_info) = create_lst( - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), treasury_cap, - scenario.ctx() + runner.ctx(), ); test_scenario::return_shared(system_state); - sui::test_utils::destroy(coins); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(coins); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[test] #[expected_failure(abort_code = 0, location = liquid_staking::liquid_staking)] fun test_create_lst_with_stake_fail_1() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); - let staked_sui = stake_with(0, 100, &mut scenario); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); - let mut system_state = scenario.take_shared(); - let fungible_staked_sui = system_state.convert_to_fungible_staked_sui(staked_sui, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let fungible_staked_sui = system_state.convert_to_fungible_staked_sui( + staked_sui, + runner.ctx(), + ); // Create an empty treasury cap - let treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); + let treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); let (admin_cap, lst_info) = create_lst_with_stake( &mut system_state, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), treasury_cap, vector[fungible_staked_sui], - coin::zero(scenario.ctx()), - scenario.ctx() + coin::zero(runner.ctx()), + runner.ctx(), ); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[test] #[expected_failure(abort_code = 0, location = liquid_staking::liquid_staking)] fun test_create_lst_with_stake_fail_2() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); - setup_sui_system(&mut scenario, vector[100, 100]); + let mut system_state = runner.scenario_mut().take_shared(); - let mut system_state = scenario.take_shared(); - - let mut treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); - let coins = treasury_cap.mint(1000 * MIST_PER_SUI, scenario.ctx()); + let mut treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); + let coins = treasury_cap.mint(1000 * MIST_PER_SUI, runner.ctx()); let (admin_cap, lst_info) = create_lst_with_stake( &mut system_state, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), treasury_cap, vector::empty(), - coin::zero(scenario.ctx()), - scenario.ctx() + coin::zero(runner.ctx()), + runner.ctx(), ); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); - sui::test_utils::destroy(coins); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); + std::unit_test::destroy(coins); - scenario.end(); + runner.finish(); } #[test] #[expected_failure(abort_code = 0, location = liquid_staking::liquid_staking)] fun test_create_lst_with_stake_fail_3() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - let mut treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); - let coins = treasury_cap.mint(1000 * MIST_PER_SUI, scenario.ctx()); + let mut treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); + let coins = treasury_cap.mint(1000 * MIST_PER_SUI, runner.ctx()); let (admin_cap, lst_info) = create_lst_with_stake( &mut system_state, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), treasury_cap, vector::empty(), - coin::mint_for_testing(1000 * MIST_PER_SUI - 1, scenario.ctx()), - scenario.ctx() + coin::mint_for_testing(1000 * MIST_PER_SUI - 1, runner.ctx()), + runner.ctx(), ); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); - sui::test_utils::destroy(coins); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); + std::unit_test::destroy(coins); - scenario.end(); + runner.finish(); } #[test] #[expected_failure(abort_code = 0, location = liquid_staking::liquid_staking)] fun test_create_lst_with_stake_fail_4() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); - setup_sui_system(&mut scenario, vector[100, 100]); + let mut system_state = runner.scenario_mut().take_shared(); - let mut system_state = scenario.take_shared(); - - let mut treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); - let coins = treasury_cap.mint(1000 * MIST_PER_SUI, scenario.ctx()); + let mut treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); + let coins = treasury_cap.mint(1000 * MIST_PER_SUI, runner.ctx()); let (admin_cap, lst_info) = create_lst_with_stake( &mut system_state, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), treasury_cap, vector::empty(), - coin::mint_for_testing(2000 * MIST_PER_SUI + 1, scenario.ctx()), - scenario.ctx() + coin::mint_for_testing(2000 * MIST_PER_SUI + 1, runner.ctx()), + runner.ctx(), ); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); - sui::test_utils::destroy(coins); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); + std::unit_test::destroy(coins); - scenario.end(); + runner.finish(); } #[test] fun test_mint_and_redeem() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - let sui = coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let sui = coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - let lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let lst = lst_info.mint(&mut system_state, sui, runner.ctx()); - assert!(lst.value() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.fees() == 1 * MIST_PER_SUI, 0); - sui::test_utils::destroy(lst); + assert!(lst.value() == 99 * MIST_PER_SUI); + assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.fees() == MIST_PER_SUI); + std::unit_test::destroy(lst); - let sui = coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()); - let mut lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); - - assert!(lst.value() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_lst_supply() == 198 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 198 * MIST_PER_SUI, 0); - assert!(lst_info.fees() == 2 * MIST_PER_SUI, 0); + let sui = coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()); + let mut lst = lst_info.mint(&mut system_state, sui, runner.ctx()); + assert!(lst.value() == 99 * MIST_PER_SUI); + assert!(lst_info.total_lst_supply() == 198 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 198 * MIST_PER_SUI); + assert!(lst_info.fees() == 2 * MIST_PER_SUI); let sui = lst_info.redeem( - lst.split(10 * MIST_PER_SUI, scenario.ctx()), - &mut system_state, - scenario.ctx() + lst.split(10 * MIST_PER_SUI, runner.ctx()), + &mut system_state, + runner.ctx(), ); - assert!(sui.value() == 9_900_000_000, 0); - assert!(lst_info.total_lst_supply() == 188 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 188 * MIST_PER_SUI, 0); - assert!(lst_info.fees() == 2 * MIST_PER_SUI + 100_000_000, 0); + assert!(sui.value() == 9_900_000_000); + assert!(lst_info.total_lst_supply() == 188 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 188 * MIST_PER_SUI); + assert!(lst_info.fees() == 2 * MIST_PER_SUI + 100_000_000); - sui::test_utils::destroy(sui); - sui::test_utils::destroy(lst); + std::unit_test::destroy(sui); + std::unit_test::destroy(lst); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[test] fun test_increase_and_decrease_validator_stake() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[10, 10]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[10, 10]); + runner.scenario_mut().next_tx(@0x0); - scenario.next_tx(@0x0); - - let mut system_state = scenario.take_shared(); - let sui = coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let sui = coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - let lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let lst = lst_info.mint(&mut system_state, sui, runner.ctx()); - assert!(lst.value() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.fees() == 1 * MIST_PER_SUI, 0); + assert!(lst.value() == 99 * MIST_PER_SUI); + assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.fees() == MIST_PER_SUI); lst_info.increase_validator_stake( - &admin_cap, - &mut system_state, - @0x0, - 20 * MIST_PER_SUI, - scenario.ctx() + &admin_cap, + &mut system_state, + validator_addresses[0], + 20 * MIST_PER_SUI, + runner.ctx(), ); - assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI, 0); + assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI); assert!( - lst_info.storage().validators()[0].inactive_stake().borrow().staked_sui_amount() == 20 * MIST_PER_SUI, - 0 + lst_info.storage().validators()[0].inactive_stake().borrow().staked_sui_amount() == 20 * MIST_PER_SUI, ); lst_info.increase_validator_stake( - &admin_cap, - &mut system_state, - @0x1, - 20 * MIST_PER_SUI, - scenario.ctx() + &admin_cap, + &mut system_state, + validator_addresses[1], + 20 * MIST_PER_SUI, + runner.ctx(), ); - assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI, 0); + assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI); assert!( - lst_info.storage().validators()[1].inactive_stake().borrow().staked_sui_amount() == 20 * MIST_PER_SUI, - 0 + lst_info.storage().validators()[1].inactive_stake().borrow().staked_sui_amount() == 20 * MIST_PER_SUI, ); test_scenario::return_shared(system_state); - scenario.next_tx(@0x0); - advance_epoch_with_reward_amounts(0, 20, &mut scenario); + advance_epoch_with_rewards(&mut runner, 20); - - scenario.next_tx(@0x0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); lst_info.increase_validator_stake( - &admin_cap, - &mut system_state, - @0x1, - 20 * MIST_PER_SUI, - scenario.ctx() + &admin_cap, + &mut system_state, + validator_addresses[1], + 20 * MIST_PER_SUI, + runner.ctx(), ); - assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI, 0); + assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI); assert!( - lst_info.storage().validators()[1].inactive_stake().borrow().staked_sui_amount() == 20 * MIST_PER_SUI, - 0 + lst_info.storage().validators()[1].inactive_stake().borrow().staked_sui_amount() == 20 * MIST_PER_SUI, ); assert!( - lst_info.storage().validators()[1].active_stake().borrow().value() == 10 * MIST_PER_SUI, - 0 + lst_info.storage().validators()[1].active_stake().borrow().value() == 10 * MIST_PER_SUI, ); lst_info.decrease_validator_stake( - &admin_cap, - &mut system_state, - @0x1, - 40 * MIST_PER_SUI, - scenario.ctx() + &admin_cap, + &mut system_state, + validator_addresses[1], + 40 * MIST_PER_SUI, + runner.ctx(), ); - assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI, 0); - assert!( - lst_info.storage().validators()[1].inactive_stake().is_none(), - 0 - ); - assert!( - lst_info.storage().validators()[1].active_stake().is_none(), - 0 - ); + assert!(lst_info.total_lst_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 99 * MIST_PER_SUI); + assert!(lst_info.storage().validators()[1].inactive_stake().is_none()); + assert!(lst_info.storage().validators()[1].active_stake().is_none()); - sui::test_utils::destroy(lst); + std::unit_test::destroy(lst); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[test] fun test_spread_fee() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[90, 90]); + let mut runner = setup_runner(vector[90, 90]); + let validator_addresses = runner.genesis_validator_addresses(); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_spread_fee_bps(5000) // 50% .set_sui_mint_fee_bps(1000) // 10% .to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - let sui = coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()); - let lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let sui = coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()); + let lst = lst_info.mint(&mut system_state, sui, runner.ctx()); - assert!(lst.value() == 90 * MIST_PER_SUI, 0); + assert!(lst.value() == 90 * MIST_PER_SUI); lst_info.increase_validator_stake( - &admin_cap, - &mut system_state, - @0x0, - 45 * MIST_PER_SUI, - scenario.ctx() + &admin_cap, + &mut system_state, + validator_addresses[0], + 45 * MIST_PER_SUI, + runner.ctx(), ); lst_info.increase_validator_stake( - &admin_cap, - &mut system_state, - @0x1, - 45 * MIST_PER_SUI, - scenario.ctx() + &admin_cap, + &mut system_state, + validator_addresses[1], + 45 * MIST_PER_SUI, + runner.ctx(), ); test_scenario::return_shared(system_state); - scenario.next_tx(@0x0); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // got 90 SUI of rewards, 45 of that should be spread fee - advance_epoch_with_reward_amounts(0, 270, &mut scenario); + advance_epoch_with_rewards(&mut runner, 270); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); let sui = lst_info.redeem( lst, - &mut system_state, - scenario.ctx() + &mut system_state, + runner.ctx(), ); - assert!(sui.value() == 135 * MIST_PER_SUI, 0); - assert!(lst_info.storage().total_sui_supply() == 45 * MIST_PER_SUI, 0); - assert!(lst_info.total_sui_supply() == 0, 0); - assert!(lst_info.accrued_spread_fees() == 45 * MIST_PER_SUI, 0); + assert!(sui.value() == 135 * MIST_PER_SUI); + assert!(lst_info.storage().total_sui_supply() == 45 * MIST_PER_SUI); + assert!(lst_info.total_sui_supply() == 0); + assert!(lst_info.accrued_spread_fees() == 45 * MIST_PER_SUI); - let fees = lst_info.collect_fees(&mut system_state, &admin_cap, scenario.ctx()); - assert!(fees.value() == 55 * MIST_PER_SUI, 0); // 45 in spread, 10 in mint - assert!(lst_info.accrued_spread_fees() == 0, 0); - assert!(lst_info.storage().total_sui_supply() == 0, 0); + let fees = lst_info.collect_fees(&mut system_state, &admin_cap, runner.ctx()); + assert!(fees.value() == 55 * MIST_PER_SUI); // 45 in spread, 10 in mint + assert!(lst_info.accrued_spread_fees() == 0); + assert!(lst_info.storage().total_sui_supply() == 0); - sui::test_utils::destroy(sui); - sui::test_utils::destroy(fees); + std::unit_test::destroy(sui); + std::unit_test::destroy(fees); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[test] fun test_update_fees() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[90, 90]); - setup_sui_system(&mut scenario, vector[90, 90]); + runner.scenario_mut().next_tx(@0x0); - scenario.next_tx(@0x0); - - let system_state = scenario.take_shared(); + let system_state = runner.scenario_mut().take_shared(); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_spread_fee_bps(5000) // 50% .set_sui_mint_fee_bps(1000) // 10% .to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); lst_info.update_fees( &admin_cap, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_spread_fee_bps(1000) // 10% .set_sui_mint_fee_bps(100) // 10% - .to_fee_config() + .to_fee_config(), ); - assert!(lst_info.fee_config().spread_fee_bps() == 1000, 0); - assert!(lst_info.fee_config().sui_mint_fee_bps() == 100, 0); + assert!(lst_info.fee_config().spread_fee_bps() == 1000); + assert!(lst_info.fee_config().sui_mint_fee_bps() == 100); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[test] fun test_increase_validator_stake_by_dust_amount() { - let mut scenario = test_scenario::begin(@0x0); - setup_sui_system(&mut scenario, vector[100, 100]); - scenario.next_tx(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let mut treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); - let lst = treasury_cap.mint(100 * MIST_PER_SUI, scenario.ctx()); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); + let mut treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); + let lst = treasury_cap.mint(100 * MIST_PER_SUI, runner.ctx()); + + let mut system_state = runner.scenario_mut().take_shared(); let (admin_cap, mut lst_info) = create_lst_with_stake( &mut system_state, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_spread_fee_bps(5000) // 50% .set_sui_mint_fee_bps(1000) // 10% .to_fee_config(), treasury_cap, vector::empty(), - coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()), - scenario.ctx() + coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()), + runner.ctx(), ); let increased_amount = lst_info.increase_validator_stake( &admin_cap, &mut system_state, - @0x0, + validator_addresses[0], MIST_PER_SUI - 1, - scenario.ctx() + runner.ctx(), ); - assert!(increased_amount == 0, 0); + assert!(increased_amount == 0); - sui::test_utils::destroy(lst); + std::unit_test::destroy(lst); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[test] fun test_change_validator_priority() { - let mut scenario = test_scenario::begin(@0x0); - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); - let lst = treasury_cap.mint(100 * MIST_PER_SUI, scenario.ctx()); + let mut treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); + let lst = treasury_cap.mint(100 * MIST_PER_SUI, runner.ctx()); - let mut system_state = scenario.take_shared(); - let pool_id_1 = system_state.validator_staking_pool_id(@0x0); - let pool_id_2 = system_state.validator_staking_pool_id(@0x1); + let mut system_state = runner.scenario_mut().take_shared(); + let pool_id_1 = system_state.validator_staking_pool_id(validator_addresses[0]); + let pool_id_2 = system_state.validator_staking_pool_id(validator_addresses[1]); let (admin_cap, mut lst_info) = create_lst_with_stake( &mut system_state, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_spread_fee_bps(5000) // 50% .set_sui_mint_fee_bps(1000) // 10% .to_fee_config(), treasury_cap, vector::empty(), - coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()), - scenario.ctx() + coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()), + runner.ctx(), ); lst_info.increase_validator_stake( &admin_cap, &mut system_state, - @0x0, + validator_addresses[0], MIST_PER_SUI, - scenario.ctx() + runner.ctx(), ); lst_info.increase_validator_stake( &admin_cap, &mut system_state, - @0x1, + validator_addresses[1], MIST_PER_SUI, - scenario.ctx() + runner.ctx(), ); assert!(lst_info.storage().validators()[0].staking_pool_id() == pool_id_1); @@ -659,7 +599,7 @@ module liquid_staking::liquid_staking_tests { lst_info.change_validator_priority( &admin_cap, 0, - 1 + 1, ); assert!(lst_info.storage().validators()[0].staking_pool_id() == pool_id_2); @@ -668,90 +608,96 @@ module liquid_staking::liquid_staking_tests { lst_info.change_validator_priority( &admin_cap, 0, - 0 + 0, ); assert!(lst_info.storage().validators()[0].staking_pool_id() == pool_id_2); assert!(lst_info.storage().validators()[1].staking_pool_id() == pool_id_1); - sui::test_utils::destroy(lst); + std::unit_test::destroy(lst); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } /* randomized testing */ #[random_test] fun test_random_increase_validator_stake(mint_amount: u64, stake_amount: u64) { - let mut scenario = test_scenario::begin(@0x0); - setup_sui_system(&mut scenario, vector[100, 100]); - scenario.next_tx(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); + + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_spread_fee_bps(5000) // 50% .set_sui_mint_fee_bps(1000) // 10% .to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - let sui = coin::mint_for_testing(mint_amount, scenario.ctx()); - let lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let sui = coin::mint_for_testing(mint_amount, runner.ctx()); + let lst = lst_info.mint(&mut system_state, sui, runner.ctx()); let total_sui_supply = lst_info.total_sui_supply(); let increased_amount = lst_info.increase_validator_stake( &admin_cap, &mut system_state, - @0x0, + validator_addresses[0], stake_amount, - scenario.ctx() + runner.ctx(), ); - assert!(increased_amount == std::u64::min(total_sui_supply, stake_amount), 0); + assert!(increased_amount == std::u64::min(total_sui_supply, stake_amount)); - sui::test_utils::destroy(lst); + std::unit_test::destroy(lst); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[random_test] fun test_random_decrease_validator_stake(mint_amount: u64, unstake_amount: u64) { - let mut scenario = test_scenario::begin(@0x0); - setup_sui_system(&mut scenario, vector[100, 100]); - scenario.next_tx(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui = stake_with(0, std::u64::max(mint_amount / MIST_PER_SUI, 1), &mut scenario); - let mut treasury_cap = coin::create_treasury_cap_for_testing(scenario.ctx()); - let lst = treasury_cap.mint(mint_amount / MIST_PER_SUI * MIST_PER_SUI, scenario.ctx()); + let staked_sui = runner.stake_with_and_take( + validator_addresses[0], + std::u64::max(mint_amount / MIST_PER_SUI, 1), + ); + let mut treasury_cap = coin::create_treasury_cap_for_testing(runner.ctx()); + let lst = treasury_cap.mint(mint_amount / MIST_PER_SUI * MIST_PER_SUI, runner.ctx()); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); - let mut system_state = scenario.take_shared(); - let fungible_staked_sui = system_state.convert_to_fungible_staked_sui(staked_sui, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let fungible_staked_sui = system_state.convert_to_fungible_staked_sui( + staked_sui, + runner.ctx(), + ); let (admin_cap, mut lst_info) = create_lst_with_stake( &mut system_state, - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_spread_fee_bps(5000) // 50% .set_sui_mint_fee_bps(1000) // 10% .to_fee_config(), treasury_cap, vector[fungible_staked_sui], - coin::zero(scenario.ctx()), - scenario.ctx() + coin::zero(runner.ctx()), + runner.ctx(), ); let total_sui_supply = lst_info.total_sui_supply(); @@ -759,57 +705,60 @@ module liquid_staking::liquid_staking_tests { let unstaked_amount = lst_info.decrease_validator_stake( &admin_cap, &mut system_state, - @0x0, + validator_addresses[0], unstake_amount, - scenario.ctx() + runner.ctx(), ); - assert!(unstaked_amount <= std::u64::min(total_sui_supply, unstake_amount + MIST_PER_SUI), 0); + assert!(unstaked_amount <= std::u64::min(total_sui_supply, unstake_amount + MIST_PER_SUI)); - sui::test_utils::destroy(lst); + std::unit_test::destroy(lst); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } #[test] #[expected_failure(abort_code = 6, location = liquid_staking::liquid_staking)] fun test_custom_redeem_request_fail_not_processed() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100, 100]); - setup_sui_system(&mut scenario, vector[100, 100, 100]); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()).set_custom_redeem_fee_bps(100).to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + fees::new_builder(runner.ctx()).set_custom_redeem_fee_bps(100).to_fee_config(), + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - let mut system_state = scenario.take_shared(); - let sui = coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()); - let mut lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let sui = coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()); + let mut lst = lst_info.mint(&mut system_state, sui, runner.ctx()); - assert!(lst_info.total_lst_supply() == 100 * MIST_PER_SUI, 0); - assert!(lst_info.storage().total_sui_supply() == 100 * MIST_PER_SUI, 0); + assert!(lst_info.total_lst_supply() == 100 * MIST_PER_SUI); + assert!(lst_info.storage().total_sui_supply() == 100 * MIST_PER_SUI); - let lst_to_unstake = lst.split(10 * MIST_PER_SUI, scenario.ctx()); - let custom_redeem_request = lst_info.custom_redeem_request(lst_to_unstake,&mut system_state, scenario.ctx()); + let lst_to_unstake = lst.split(10 * MIST_PER_SUI, runner.ctx()); + let custom_redeem_request = lst_info.custom_redeem_request( + lst_to_unstake, + &mut system_state, + runner.ctx(), + ); - let sui = lst_info.custom_redeem(custom_redeem_request, &mut system_state, scenario.ctx()); + let sui = lst_info.custom_redeem(custom_redeem_request, &mut system_state, runner.ctx()); test_scenario::return_shared(system_state); - sui::test_utils::destroy(lst_info); - sui::test_utils::destroy(lst); - sui::test_utils::destroy(sui); - sui::test_utils::destroy(admin_cap); + std::unit_test::destroy(lst_info); + std::unit_test::destroy(lst); + std::unit_test::destroy(sui); + std::unit_test::destroy(admin_cap); - scenario.end(); + runner.finish(); } #[test] @@ -817,37 +766,36 @@ module liquid_staking::liquid_staking_tests { use sui_system::sui_system; use std::unit_test; - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100]); + let validator_addresses = runner.genesis_validator_addresses(); // activate validator - setup_sui_system(&mut scenario, vector[100]); - - scenario.next_tx(@0x0); - let mut system_state = scenario.take_shared(); - sui_system::set_epoch_for_testing(&mut system_state, scenario.ctx().epoch() + 1); - scenario.next_epoch(@0x0); + runner.scenario_mut().next_tx(@0x0); + let mut system_state = runner.scenario_mut().take_shared(); + sui_system::set_epoch_for_testing(&mut system_state, runner.ctx().epoch() + 1); + runner.scenario_mut().next_epoch(@0x0); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()) + fees::new_builder(runner.ctx()) .set_sui_mint_fee_bps(100) .set_redeem_fee_bps(100) .to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - assert!(lst_info.total_lst_supply() == 0, 0); - assert!(lst_info.storage().total_sui_supply() == 0, 0); + assert!(lst_info.total_lst_supply() == 0); + assert!(lst_info.storage().total_sui_supply() == 0); - let sui = coin::mint_for_testing(200 * MIST_PER_SUI, scenario.ctx()); - let lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let sui = coin::mint_for_testing(200 * MIST_PER_SUI, runner.ctx()); + let lst = lst_info.mint(&mut system_state, sui, runner.ctx()); lst_info.increase_validator_stake( - &admin_cap, - &mut system_state, - @0x0, - 200 * MIST_PER_SUI, - scenario.ctx() + &admin_cap, + &mut system_state, + validator_addresses[0], + 200 * MIST_PER_SUI, + runner.ctx(), ); test_scenario::return_shared(system_state); @@ -855,6 +803,6 @@ module liquid_staking::liquid_staking_tests { unit_test::destroy(lst); unit_test::destroy(lst_info); - scenario.end(); + runner.finish(); } } diff --git a/contracts/tests/registry_tests.move b/contracts/tests/registry_tests.move index 04b62e5..11c1032 100644 --- a/contracts/tests/registry_tests.move +++ b/contracts/tests/registry_tests.move @@ -1,21 +1,18 @@ #[test_only] module liquid_staking::registry_tests { - use sui::test_scenario::{Self, Scenario}; - use liquid_staking::fees::{Self}; - use liquid_staking::liquid_staking::{Self}; - use sui::coin::{Self}; - use liquid_staking::weight::{Self}; - use liquid_staking::registry::{Self}; + use liquid_staking::{fees, liquid_staking, registry, weight}; + use sui::{coin, test_scenario}; + public struct TEST has drop {} - #[test] - fun test_add_weight_hook_to_registry() { + #[test] + fun test_add_weight_hook_to_registry() { let mut scenario = test_scenario::begin(@0x0); let (admin_cap, lst_info) = liquid_staking::create_lst( fees::new_builder(scenario.ctx()).to_fee_config(), coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + scenario.ctx(), ); let admin_cap_id = object::id(&admin_cap); @@ -31,10 +28,10 @@ module liquid_staking::registry_tests { assert!(entry.liquid_staking_info_id() == lst_info_id); assert!(entry.extra_info().weight_hook_id() == object::id(&weight_hook)); - sui::test_utils::destroy(lst_info); - sui::test_utils::destroy(weight_hook); - sui::test_utils::destroy(weight_hook_admin_cap); - sui::test_utils::destroy(registry); + std::unit_test::destroy(lst_info); + std::unit_test::destroy(weight_hook); + std::unit_test::destroy(weight_hook_admin_cap); + std::unit_test::destroy(registry); scenario.end(); } diff --git a/contracts/tests/storage_tests.move b/contracts/tests/storage_tests.move index 26eab6b..cb7e82c 100644 --- a/contracts/tests/storage_tests.move +++ b/contracts/tests/storage_tests.move @@ -1,473 +1,512 @@ -#[allow(deprecated_usage)] #[test_only] module liquid_staking::storage_tests { - /* Tests */ - use sui::test_scenario::{Self, Scenario}; - use sui_system::governance_test_utils::{ - advance_epoch_with_reward_amounts, - advance_epoch_with_reward_amounts_return_rebate, + use liquid_staking::{ + storage::new, + test_utils::{advance_epoch_no_rewards, advance_epoch_with_rewards, setup_runner} }; - use sui::address; - use sui::coin::{Self}; - use sui_system::staking_pool::{StakedSui}; - use sui_system::sui_system::{SuiSystemState}; - use sui::balance::{Self}; - use liquid_staking::storage::{new}; - use sui::sui::SUI; - use liquid_staking::test_utils::create_validators_with_stakes; - use sui_system::governance_test_utils::create_sui_system_state_for_testing; - - - #[test_only] - fun setup_sui_system(scenario: &mut Scenario, stakes: vector) { - let validators = create_validators_with_stakes(stakes, scenario.ctx()); - create_sui_system_state_for_testing(validators, 0, 0, scenario.ctx()); - - advance_epoch_with_reward_amounts(0, 0, scenario); - } + use sui::{balance, coin, sui::SUI, test_scenario}; + use sui_system::{staking_pool::StakedSui, sui_system::SuiSystemState, test_runner::TestRunner}; const MIST_PER_SUI: u64 = 1_000_000_000; - fun stake_with(validator_index: u64, amount: u64, scenario: &mut Scenario): StakedSui { - stake_with_granular(validator_index, amount * MIST_PER_SUI, scenario) - } - - fun stake_with_granular(validator_index: u64, amount: u64, scenario: &mut Scenario): StakedSui { - scenario.next_tx(@0x0); - - let mut system_state = scenario.take_shared(); - - let ctx = scenario.ctx(); - + /// Stake with MIST-level precision + fun stake_with_granular(runner: &mut TestRunner, validator: address, amount: u64): StakedSui { + runner.scenario_mut().next_tx(@0x0); + let mut system_state = runner.scenario_mut().take_shared(); let staked_sui = system_state.request_add_stake_non_entry( - coin::mint_for_testing(amount, ctx), - address::from_u256(validator_index as u256), - ctx + coin::mint_for_testing(amount, runner.ctx()), + validator, + runner.ctx(), ); - test_scenario::return_shared(system_state); - scenario.next_tx(@0x0); - + runner.scenario_mut().next_tx(@0x0); staked_sui } #[test] public fun test_refresh() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let mut storage = new(runner.ctx()); - let mut storage = new(scenario.ctx()); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let staked_sui_1 = stake_with(0, 100, &mut scenario); - - let mut system_state = scenario.take_shared(); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - storage.refresh(&mut system_state, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + storage.refresh(&mut system_state, runner.ctx()); test_scenario::return_shared(system_state); // check state - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.last_refresh_epoch() == scenario.ctx().epoch(), 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.last_refresh_epoch() == runner.ctx().epoch()); + assert!(storage.validators().length() == 1); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); + assert!( + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, + ); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI); // stake now looks like [200, 100] => [300, 200] - advance_epoch_with_reward_amounts(0, 200, &mut scenario); + advance_epoch_with_rewards(&mut runner, 200); - let mut system_state = scenario.take_shared(); - assert!(storage.refresh(&mut system_state, scenario.ctx()), 0); + let mut system_state = runner.scenario_mut().take_shared(); + assert!(storage.refresh(&mut system_state, runner.ctx())); test_scenario::return_shared(system_state); // inactive stake should have been converted to active stake - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.last_refresh_epoch() == scenario.ctx().epoch(), 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 150 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.last_refresh_epoch() == runner.ctx().epoch()); + assert!(storage.validators().length() == 1); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 300 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 150 * MIST_PER_SUI); // stake now looks like [300, 200] => [450, 300] - advance_epoch_with_reward_amounts(0,300, &mut scenario); + advance_epoch_with_rewards(&mut runner, 300); - let mut system_state = scenario.take_shared(); - assert!(storage.refresh(&mut system_state, scenario.ctx()), 0); + let mut system_state = runner.scenario_mut().take_shared(); + assert!(storage.refresh(&mut system_state, runner.ctx())); - assert!(storage.total_sui_supply() == 150 * MIST_PER_SUI, 0); - assert!(storage.last_refresh_epoch() == scenario.ctx().epoch(), 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.validators()[0].total_sui_amount() == 150 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 450 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 150 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 150 * MIST_PER_SUI); + assert!(storage.last_refresh_epoch() == runner.ctx().epoch()); + assert!(storage.validators().length() == 1); + assert!(storage.validators()[0].total_sui_amount() == 150 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 450 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 150 * MIST_PER_SUI); // check idempotency - assert!(!storage.refresh(&mut system_state, scenario.ctx()), 0); + assert!(!storage.refresh(&mut system_state, runner.ctx())); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - scenario.end(); + std::unit_test::destroy(storage); + runner.finish(); } #[test] fun test_refresh_prune_empty_validator_infos() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui = stake_with(0, 50, &mut scenario); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 50); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 50 * MIST_PER_SUI, 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); + assert!( + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 50 * MIST_PER_SUI, + ); // Withdraw the stake before refresh let unstaked_sui = storage.unstake_approx_n_sui_from_validator( &mut system_state, - 0, - 100 * MIST_PER_SUI, - scenario.ctx() + 0, + 100 * MIST_PER_SUI, + runner.ctx(), ); - assert!(unstaked_sui == 50 * MIST_PER_SUI, 0); - assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 0, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(unstaked_sui == 50 * MIST_PER_SUI); + assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 0); + assert!(storage.validators()[0].active_stake().is_none()); + assert!(storage.validators()[0].inactive_stake().is_none()); test_scenario::return_shared(system_state); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); - let mut system_state = scenario.take_shared(); - assert!(storage.refresh(&mut system_state, scenario.ctx()), 0); + let mut system_state = runner.scenario_mut().take_shared(); + assert!(storage.refresh(&mut system_state, runner.ctx())); - assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators().length() == 0, 0); // Validator should be removed as it's empty + assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 50 * MIST_PER_SUI); + assert!(storage.validators().length() == 0); // Validator should be removed as it's empty test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - scenario.end(); + std::unit_test::destroy(storage); + runner.finish(); } #[test] fun test_refresh_skip_epoch() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let staked_sui = stake_with(0, 100, &mut scenario); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut system_state = runner.scenario_mut().take_shared(); - let mut system_state = scenario.take_shared(); - - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); test_scenario::return_shared(system_state); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); + assert!( + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, + ); // stake now looks like [200, 100] => [300, 200] - advance_epoch_with_reward_amounts(0, 200, &mut scenario); + advance_epoch_with_rewards(&mut runner, 200); // stake now looks like [300, 200] => [450, 300] - advance_epoch_with_reward_amounts(0, 300, &mut scenario); + advance_epoch_with_rewards(&mut runner, 300); - let mut system_state = scenario.take_shared(); - storage.refresh(&mut system_state, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + storage.refresh(&mut system_state, runner.ctx()); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 150 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 150 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 450 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 150 * MIST_PER_SUI, 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 150 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 150 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 450 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 150 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - scenario.end(); + std::unit_test::destroy(storage); + runner.finish(); } #[test] fun test_refresh_safe_mode() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui = stake_with(0, 100, &mut scenario); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); test_scenario::return_shared(system_state); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.last_refresh_epoch() == 1, 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); + assert!( + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, + ); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI); + assert!(storage.last_refresh_epoch() == 1); // safe mode - test_scenario::next_epoch(&mut scenario, @0x0); + runner.advance_epoch_safe_mode(); - let mut system_state = scenario.take_shared(); - storage.refresh(&mut system_state, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + storage.refresh(&mut system_state, runner.ctx()); // storage should use the old exchange rate - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.last_refresh_epoch() == 2, 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI); + assert!(storage.last_refresh_epoch() == 2); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - scenario.end(); + std::unit_test::destroy(storage); + runner.finish(); } - #[test] + #[test] fun test_join_to_sui_pool() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); - let mut storage = new(scenario.ctx()); + let mut runner = setup_runner(vector[100, 100]); + let mut storage = new(runner.ctx()); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - assert!(storage.total_sui_supply() == 0, 0); - assert!(storage.sui_pool().value() == 0, 0); + assert!(storage.total_sui_supply() == 0); + assert!(storage.sui_pool().value() == 0); let sui = balance::create_for_testing(50 * MIST_PER_SUI); storage.join_to_sui_pool(sui); - assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 50 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 50 * MIST_PER_SUI); - sui::test_utils::destroy(storage); - scenario.end(); + std::unit_test::destroy(storage); + runner.finish(); } /* Join Stake tests */ #[test] fun test_join_stake_active() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let active_staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 50); + let active_staked_sui_2 = runner.stake_with_and_take(validator_addresses[0], 50); - let active_staked_sui_1 = stake_with(0, 50, &mut scenario); - let active_staked_sui_2 = stake_with(0, 50, &mut scenario); - - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); - + advance_epoch_with_rewards(&mut runner, 400); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - storage.join_stake(&mut system_state, active_staked_sui_1, scenario.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_1, runner.ctx()); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 400 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 200 * MIST_PER_SUI, 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 400 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 200 * MIST_PER_SUI); - storage.join_stake(&mut system_state, active_staked_sui_2, scenario.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_2, runner.ctx()); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 400 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 200 * MIST_PER_SUI, 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 400 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 200 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] fun test_join_stake_inactive() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let mut staked_sui_1 = stake_with(0, 100, &mut scenario); - let staked_sui_2 = staked_sui_1.split(50 * MIST_PER_SUI, scenario.ctx()); + let mut staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + let staked_sui_2 = staked_sui_1.split(50 * MIST_PER_SUI, runner.ctx()); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); - assert!(storage.last_refresh_epoch() == scenario.ctx().epoch(), 0); - assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.validators()[0].total_sui_amount() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI, 0); + assert!(storage.last_refresh_epoch() == runner.ctx().epoch()); + assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI); + assert!(storage.validators().length() == 1); + assert!(storage.validators()[0].total_sui_amount() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); + assert!( + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 50 * MIST_PER_SUI, + ); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI); - storage.join_stake(&mut system_state, staked_sui_2, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui_2, runner.ctx()); - assert!(storage.last_refresh_epoch() == scenario.ctx().epoch(), 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI, 0); + assert!(storage.last_refresh_epoch() == runner.ctx().epoch()); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.validators().length() == 1); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); + assert!( + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, + ); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 100 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] #[expected_failure(abort_code = 1, location = liquid_staking::storage)] fun test_join_inactive_stake_from_non_active_validator() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); - - let staked_sui_1 = stake_with(1, 100, &mut scenario); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - scenario.next_tx(@0x1); - let mut system_state = scenario.take_shared(); - system_state.request_remove_validator(scenario.ctx()); - test_scenario::return_shared(system_state); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[1], 100); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + runner.set_sender(validator_addresses[1]); + runner.remove_validator(); - let mut system_state = scenario.take_shared(); - assert!(!system_state.active_validator_addresses().contains(&@0x1), 0); + advance_epoch_no_rewards(&mut runner); + let mut system_state = runner.scenario_mut().take_shared(); + assert!(!system_state.active_validator_addresses().contains(&validator_addresses[1])); - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] fun test_refresh_with_inactive_stake() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[1], 100); + runner.scenario_mut().next_tx(@0x0); - let staked_sui_1 = stake_with(1, 100, &mut scenario); - - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); test_scenario::return_shared(system_state); - scenario.next_tx(@0x1); - let mut system_state = scenario.take_shared(); - system_state.request_remove_validator(scenario.ctx()); - test_scenario::return_shared(system_state); + runner.set_sender(validator_addresses[1]); + runner.remove_validator(); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); + advance_epoch_no_rewards(&mut runner); - let mut system_state = scenario.take_shared(); - assert!(!system_state.active_validator_addresses().contains(&@0x1), 0); + let mut system_state = runner.scenario_mut().take_shared(); + assert!(!system_state.active_validator_addresses().contains(&validator_addresses[1])); - storage.refresh(&mut system_state, scenario.ctx()); - assert!(storage.validators().length() == 0, 0); // got removed - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); + storage.refresh(&mut system_state, runner.ctx()); + assert!(storage.validators().length() == 0); // got removed + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] #[expected_failure(abort_code = 15, location = sui_system::staking_pool)] fun test_refresh_inactive_staking_pool_edge_case() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(1, 100, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[1], 100); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); test_scenario::return_shared(system_state); // remove validator - scenario.next_tx(@0x1); - let mut system_state = scenario.take_shared(); - system_state.request_remove_validator(scenario.ctx()); - test_scenario::return_shared(system_state); + runner.set_sender(validator_addresses[1]); + runner.remove_validator(); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); + advance_epoch_no_rewards(&mut runner); - let mut system_state = scenario.take_shared(); - assert!(!system_state.active_validator_addresses().contains(&@0x1), 0); + let mut system_state = runner.scenario_mut().take_shared(); + assert!(!system_state.active_validator_addresses().contains(&validator_addresses[1])); test_scenario::return_shared(system_state); - // readd with same address - scenario.next_tx(@0x1); - let mut system_state = scenario.take_shared(); - let pubkey = x"99f25ef61f8032b914636460982c5cc6f134ef1ddae76657f2cbfec1ebfc8d097374080df6fcf0dcb8bc4b0d8e0af5d80ebbff2b4c599f54f42d6312dfc314276078c1cc347ebbbec5198be258513f386b930d02c2749a803e2330955ebd1a10"; - let pop = x"b01cc86f421beca7ab4cfca87c0799c4d038c199dd399fbec1924d4d4367866dba9e84d514710b91feb65316e4ceef43"; + // read with same address + runner.scenario_mut().next_tx(validator_addresses[1]); + let mut system_state = runner.scenario_mut().take_shared(); + let pubkey = + x"99f25ef61f8032b914636460982c5cc6f134ef1ddae76657f2cbfec1ebfc8d097374080df6fcf0dcb8bc4b0d8e0af5d80ebbff2b4c599f54f42d6312dfc314276078c1cc347ebbbec5198be258513f386b930d02c2749a803e2330955ebd1a10"; + let pop = + x"b01cc86f421beca7ab4cfca87c0799c4d038c199dd399fbec1924d4d4367866dba9e84d514710b91feb65316e4ceef43"; system_state.request_add_validator_candidate_for_testing( pubkey, - vector[215, 64, 85, 185, 231, 116, 69, 151, 97, 79, 4, 183, 20, 70, 84, 51, 211, 162, 115, 221, 73, 241, 240, 171, 192, 25, 232, 106, 175, 162, 176, 43], - vector[148, 117, 212, 171, 44, 104, 167, 11, 177, 100, 4, 55, 17, 235, 117, 45, 117, 84, 159, 49, 14, 159, 239, 246, 237, 21, 83, 166, 112, 53, 62, 199], + vector[ + 215, + 64, + 85, + 185, + 231, + 116, + 69, + 151, + 97, + 79, + 4, + 183, + 20, + 70, + 84, + 51, + 211, + 162, + 115, + 221, + 73, + 241, + 240, + 171, + 192, + 25, + 232, + 106, + 175, + 162, + 176, + 43, + ], + vector[ + 148, + 117, + 212, + 171, + 44, + 104, + 167, + 11, + 177, + 100, + 4, + 55, + 17, + 235, + 117, + 45, + 117, + 84, + 159, + 49, + 14, + 159, + 239, + 246, + 237, + 21, + 83, + 166, + 112, + 53, + 62, + 199, + ], pop, b"ValidatorName2", b"description2", @@ -479,766 +518,744 @@ module liquid_staking::storage_tests { b"/ip4/168.168.168.168/udp/80", 1, 0, - scenario.ctx(), + runner.ctx(), ); test_scenario::return_shared(system_state); - - let staked_sui_2 = stake_with(1, 100, &mut scenario); + + let staked_sui_2 = runner.stake_with_and_take(validator_addresses[1], 100); // 3. mark candidate as pending active validator - scenario.next_tx(@0x1); - let mut system_state = scenario.take_shared(); - system_state.request_add_validator(scenario.ctx()); - test_scenario::return_shared(system_state); + runner.set_sender(validator_addresses[1]); + runner.add_validator(); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); - let mut system_state = scenario.take_shared(); - storage.refresh(&mut system_state, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + storage.refresh(&mut system_state, runner.ctx()); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - sui::test_utils::destroy(staked_sui_2); - - scenario.end(); + std::unit_test::destroy(storage); + std::unit_test::destroy(staked_sui_2); + + runner.finish(); } #[test] #[expected_failure(abort_code = 1, location = liquid_staking::storage)] fun test_join_active_stake_from_non_active_validator() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let active_staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); - let active_staked_sui_1 = stake_with(0, 100, &mut scenario); - - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 300, &mut scenario); + advance_epoch_no_rewards(&mut runner); + advance_epoch_with_rewards(&mut runner, 300); // mark validator as inactive - scenario.next_tx(@0x0); - let mut system_state = scenario.take_shared(); - system_state.request_remove_validator(scenario.ctx()); - test_scenario::return_shared(system_state); + runner.set_sender(validator_addresses[0]); + runner.remove_validator(); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - storage.join_stake(&mut system_state, active_staked_sui_1, scenario.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_1, runner.ctx()); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] fun test_join_stake_multiple_validators() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let active_staked_sui_1 = stake_with(0, 100, &mut scenario); - let active_staked_sui_2 = stake_with(1, 100, &mut scenario); + let active_staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + let active_staked_sui_2 = runner.stake_with_and_take(validator_addresses[1], 100); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let staked_sui = stake_with(0, 100, &mut scenario); - scenario.next_tx(@0x0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut system_state = runner.scenario_mut().take_shared(); - let mut system_state = scenario.take_shared(); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_1, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_2, runner.ctx()); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_1, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_2, scenario.ctx()); + assert!(storage.validators().length() == 2); + assert!(storage.total_sui_supply() == 500 * MIST_PER_SUI); - assert!(storage.validators().length() == 2, 0); - assert!(storage.total_sui_supply() == 500 * MIST_PER_SUI, 0); - - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].validator_address() == @0x0, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().sui_amount() == 400 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 200 * MIST_PER_SUI, 0); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); + assert!(storage.validators()[0].validator_address() == validator_addresses[0]); + assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI); + assert!( + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, + ); + assert!(storage.validators()[0].exchange_rate().sui_amount() == 400 * MIST_PER_SUI); + assert!(storage.validators()[0].exchange_rate().pool_token_amount() == 200 * MIST_PER_SUI); - assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI, 0); - assert!(storage.validators()[1].validator_address() == @0x1, 0); - assert!(storage.validators()[1].active_stake().borrow().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[1].inactive_stake().is_none(), 0); - assert!(storage.validators()[1].exchange_rate().sui_amount() == 400 * MIST_PER_SUI, 0); - assert!(storage.validators()[1].exchange_rate().pool_token_amount() == 200 * MIST_PER_SUI, 0); + assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI); + assert!(storage.validators()[1].validator_address() == validator_addresses[1]); + assert!(storage.validators()[1].active_stake().borrow().value() == 100 * MIST_PER_SUI); + assert!(storage.validators()[1].inactive_stake().is_none()); + assert!(storage.validators()[1].exchange_rate().sui_amount() == 400 * MIST_PER_SUI); + assert!(storage.validators()[1].exchange_rate().pool_token_amount() == 200 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] fun test_split_up_to_n_sui_from_sui_pool() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let mut storage = new(runner.ctx()); - setup_sui_system(&mut scenario, vector[100, 100]); - let mut storage = new(scenario.ctx()); + runner.scenario_mut().next_tx(@0x0); - scenario.next_tx(@0x0); - - assert!(storage.total_sui_supply() == 0, 0); + assert!(storage.total_sui_supply() == 0); let sui = balance::create_for_testing(50 * MIST_PER_SUI); storage.join_to_sui_pool(sui); - assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 50 * MIST_PER_SUI); let sui = storage.split_up_to_n_sui_from_sui_pool(25 * MIST_PER_SUI); - assert!(storage.total_sui_supply() == 25 * MIST_PER_SUI, 0); - assert!(sui.value() == 25 * MIST_PER_SUI, 0); - sui::test_utils::destroy(sui); + assert!(storage.total_sui_supply() == 25 * MIST_PER_SUI); + assert!(sui.value() == 25 * MIST_PER_SUI); + std::unit_test::destroy(sui); let sui = storage.split_up_to_n_sui_from_sui_pool(50 * MIST_PER_SUI); - assert!(storage.total_sui_supply() == 0 * MIST_PER_SUI, 0); - assert!(sui.value() == 25 * MIST_PER_SUI, 0); - sui::test_utils::destroy(sui); + assert!(storage.total_sui_supply() == 0); + assert!(sui.value() == 25 * MIST_PER_SUI); + std::unit_test::destroy(sui); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); - scenario.end(); + runner.finish(); } /* Unstake Approx Inactive Stake Tests */ #[test] fun test_unstake_approx_n_sui_from_inactive_stake_take_nothing() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(0, 100, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_inactive_stake( - &mut system_state, - 0, - 0, - scenario.ctx() + &mut system_state, + 0, + 0, + runner.ctx(), ); - assert!(amount == 0, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 0, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); + assert!(amount == 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 0); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); assert!( - storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, - 0 + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, ); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_inactive_stake_take_all() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(0, 100, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_inactive_stake( - &mut system_state, - 0, - 101 * MIST_PER_SUI, - scenario.ctx() + &mut system_state, + 0, + 101 * MIST_PER_SUI, + runner.ctx(), ); - assert!(amount == 100 * MIST_PER_SUI, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 0, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(amount == 100 * MIST_PER_SUI); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 0); + assert!(storage.validators()[0].active_stake().is_none()); + assert!(storage.validators()[0].inactive_stake().is_none()); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_inactive_stake_take_partial() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(0, 100, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_inactive_stake( - &mut system_state, - 0, - 50 * MIST_PER_SUI, - scenario.ctx() + &mut system_state, + 0, + 50 * MIST_PER_SUI, + runner.ctx(), ); - assert!(amount == 50 * MIST_PER_SUI, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); + assert!(amount == 50 * MIST_PER_SUI); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); assert!( - storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 50 * MIST_PER_SUI, - 0 + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 50 * MIST_PER_SUI, ); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_inactive_stake_take_dust() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let staked_sui_1 = stake_with(0, 100, &mut scenario); + let mut system_state = runner.scenario_mut().take_shared(); - let mut system_state = scenario.take_shared(); - - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_inactive_stake( - &mut system_state, - 0, - 1, - scenario.ctx() + &mut system_state, + 0, + 1, + runner.ctx(), ); - assert!(amount == MIST_PER_SUI, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 99 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); + assert!(amount == MIST_PER_SUI); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 99 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().is_none()); assert!( - storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 99 * MIST_PER_SUI, - 0 + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 99 * MIST_PER_SUI, ); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_inactive_stake_leave_dust() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let staked_sui_1 = stake_with(0, 100, &mut scenario); + let mut system_state = runner.scenario_mut().take_shared(); - let mut system_state = scenario.take_shared(); - - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_inactive_stake( - &mut system_state, - 0, + &mut system_state, + 0, 99 * MIST_PER_SUI + 1, - scenario.ctx() + runner.ctx(), ); - assert!(amount == 100 * MIST_PER_SUI, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 0, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(amount == 100 * MIST_PER_SUI); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 100 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 0); + assert!(storage.validators()[0].active_stake().is_none()); + assert!(storage.validators()[0].inactive_stake().is_none()); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } /* Unstake Approx Active Stake Tests */ #[test] fun test_unstake_approx_n_sui_from_active_stake_take_nothing() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(0, 100, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + advance_epoch_no_rewards(&mut runner); + advance_epoch_with_rewards(&mut runner, 400); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_active_stake( - &mut system_state, - 0, - 0, - scenario.ctx() + &mut system_state, + 0, + 0, + runner.ctx(), ); - assert!(amount == 0, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 0, 0); - assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(amount == 0); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 0); + assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_active_stake_take_all() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(0, 100, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + advance_epoch_no_rewards(&mut runner); + advance_epoch_with_rewards(&mut runner, 400); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); - storage.refresh(&mut system_state, scenario.ctx()); + storage.refresh(&mut system_state, runner.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_active_stake( - &mut system_state, - 0, - 200 * MIST_PER_SUI, - scenario.ctx() + &mut system_state, + 0, + 200 * MIST_PER_SUI, + runner.ctx(), ); - assert!(amount == 200 * MIST_PER_SUI, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 200 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 0, 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(amount == 200 * MIST_PER_SUI); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 200 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 0); + assert!(storage.validators()[0].active_stake().is_none()); + assert!(storage.validators()[0].inactive_stake().is_none()); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_active_stake_take_partial() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(0, 100, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + advance_epoch_no_rewards(&mut runner); + advance_epoch_with_rewards(&mut runner, 400); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); - storage.refresh(&mut system_state, scenario.ctx()); + storage.refresh(&mut system_state, runner.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_active_stake( - &mut system_state, - 0, - 100 * MIST_PER_SUI, - scenario.ctx() + &mut system_state, + 0, + 100 * MIST_PER_SUI, + runner.ctx(), ); - assert!(amount == 100 * MIST_PER_SUI, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(amount == 100 * MIST_PER_SUI); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].active_stake().borrow().value() == 50 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_active_stake_take_dust() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(0, 100, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + advance_epoch_no_rewards(&mut runner); + advance_epoch_with_rewards(&mut runner, 400); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); - storage.refresh(&mut system_state, scenario.ctx()); + storage.refresh(&mut system_state, runner.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_active_stake( - &mut system_state, - 0, - 1, - scenario.ctx() + &mut system_state, + 0, + 1, + runner.ctx(), ); - assert!(amount == 2, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 2, 0); - assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI - 2, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI - 1, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(amount == 2); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 2); + assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI - 2); + assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI - 1); + assert!(storage.validators()[0].inactive_stake().is_none()); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_active_stake_leave_dust() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui_1 = stake_with(0, 100, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + advance_epoch_no_rewards(&mut runner); + advance_epoch_with_rewards(&mut runner, 400); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); - storage.refresh(&mut system_state, scenario.ctx()); + storage.refresh(&mut system_state, runner.ctx()); - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_active_stake( - &mut system_state, - 0, - 199 * MIST_PER_SUI + 1, - scenario.ctx() + &mut system_state, + 0, + 199 * MIST_PER_SUI + 1, + runner.ctx(), ); - assert!(amount == 199 * MIST_PER_SUI + 2, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 199 * MIST_PER_SUI + 2, 0); - assert!(storage.validators()[0].total_sui_amount() == MIST_PER_SUI - 2, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == MIST_PER_SUI / 2 - 1, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(amount == 199 * MIST_PER_SUI + 2); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 199 * MIST_PER_SUI + 2); + assert!(storage.validators()[0].total_sui_amount() == MIST_PER_SUI - 2); + assert!(storage.validators()[0].active_stake().borrow().value() == MIST_PER_SUI / 2 - 1); + assert!(storage.validators()[0].inactive_stake().is_none()); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_active_stake_ceil() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + advance_epoch_no_rewards(&mut runner); + advance_epoch_with_rewards(&mut runner, 400); - let staked_sui_1 = stake_with(0, 100, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + runner.scenario_mut().next_tx(@0x0); - scenario.next_tx(@0x0); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); + storage.refresh(&mut system_state, runner.ctx()); - storage.refresh(&mut system_state, scenario.ctx()); - - storage.join_stake(&mut system_state, staked_sui_1, scenario.ctx()); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); + storage.join_stake(&mut system_state, staked_sui_1, runner.ctx()); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_active_stake( - &mut system_state, - 0, - 2 * MIST_PER_SUI + 1, - scenario.ctx() + &mut system_state, + 0, + 2 * MIST_PER_SUI + 1, + runner.ctx(), ); - assert!(amount == 2 * MIST_PER_SUI + 2, 0); - assert!(storage.validators().length() == 1, 0); - assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 2 * MIST_PER_SUI + 2, 0); - assert!(storage.validators()[0].total_sui_amount() == 198 * MIST_PER_SUI - 2, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 99_000_000_000 - 1, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); + assert!(amount == 2 * MIST_PER_SUI + 2); + assert!(storage.validators().length() == 1); + assert!(storage.total_sui_supply() == 200 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 2 * MIST_PER_SUI + 2); + assert!(storage.validators()[0].total_sui_amount() == 198 * MIST_PER_SUI - 2); + assert!(storage.validators()[0].active_stake().borrow().value() == 99_000_000_000 - 1); + assert!(storage.validators()[0].inactive_stake().is_none()); - sui::test_utils::destroy(storage); + std::unit_test::destroy(storage); test_scenario::return_shared(system_state); - scenario.end(); + runner.finish(); } /* split up to n sui tests */ #[test] fun test_split_up_to_n_sui_only_from_sui_pool() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let active_staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + let active_staked_sui_2 = runner.stake_with_and_take(validator_addresses[1], 100); - let active_staked_sui_1 = stake_with(0, 100, &mut scenario); - let active_staked_sui_2 = stake_with(1, 100, &mut scenario); - - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); - + advance_epoch_with_rewards(&mut runner, 400); - let staked_sui = stake_with(0, 100, &mut scenario); - scenario.next_tx(@0x0); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); storage.join_to_sui_pool(balance::create_for_testing(100 * MIST_PER_SUI)); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_1, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_2, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_1, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_2, runner.ctx()); - assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); + assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI); // start of test let sui = storage.split_n_sui( &mut system_state, 100 * MIST_PER_SUI, - scenario.ctx() + runner.ctx(), ); - assert!(sui.value() == 100 * MIST_PER_SUI, 0); - assert!(storage.total_sui_supply() == 500 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 0, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI, 0); + assert!(sui.value() == 100 * MIST_PER_SUI); + assert!(storage.total_sui_supply() == 500 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 0); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); + assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - sui::test_utils::destroy(sui); - - scenario.end(); + std::unit_test::destroy(storage); + std::unit_test::destroy(sui); + + runner.finish(); } #[test] fun test_split_up_to_n_sui_take_from_inactive_stake() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let active_staked_sui_1 = stake_with(0, 100, &mut scenario); - let active_staked_sui_2 = stake_with(1, 100, &mut scenario); + let active_staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + let active_staked_sui_2 = runner.stake_with_and_take(validator_addresses[1], 100); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let staked_sui = stake_with(0, 100, &mut scenario); - scenario.next_tx(@0x0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); - - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); storage.join_to_sui_pool(balance::create_for_testing(100 * MIST_PER_SUI)); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_1, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_2, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_1, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_2, runner.ctx()); - assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); + assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI); // start of test let sui = storage.split_n_sui( &mut system_state, 200 * MIST_PER_SUI, - scenario.ctx() + runner.ctx(), ); - assert!(sui.value() == 200 * MIST_PER_SUI, 0); - assert!(storage.total_sui_supply() == 400 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 0, 0); - assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI, 0); + assert!(sui.value() == 200 * MIST_PER_SUI); + assert!(storage.total_sui_supply() == 400 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 0); + assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - sui::test_utils::destroy(sui); - - scenario.end(); + std::unit_test::destroy(storage); + std::unit_test::destroy(sui); + + runner.finish(); } #[test] fun test_split_up_to_n_sui_take_all() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let active_staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + let active_staked_sui_2 = runner.stake_with_and_take(validator_addresses[1], 100); - let active_staked_sui_1 = stake_with(0, 100, &mut scenario); - let active_staked_sui_2 = stake_with(1, 100, &mut scenario); - - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); - let staked_sui = stake_with(0, 100, &mut scenario); - scenario.next_tx(@0x0); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); storage.join_to_sui_pool(balance::create_for_testing(100 * MIST_PER_SUI)); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_1, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_2, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_1, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_2, runner.ctx()); - assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); + assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI); // start of test let sui = storage.split_n_sui( &mut system_state, 600 * MIST_PER_SUI, - scenario.ctx() + runner.ctx(), ); - assert!(sui.value() == 600 * MIST_PER_SUI, 0); - assert!(storage.total_sui_supply() == 0, 0); - assert!(storage.sui_pool().value() == 0, 0); - assert!(storage.validators()[0].total_sui_amount() == 0, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); - assert!(storage.validators()[1].inactive_stake().is_none(), 0); - assert!(storage.validators()[1].active_stake().is_none(), 0); + assert!(sui.value() == 600 * MIST_PER_SUI); + assert!(storage.total_sui_supply() == 0); + assert!(storage.sui_pool().value() == 0); + assert!(storage.validators()[0].total_sui_amount() == 0); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].active_stake().is_none()); + assert!(storage.validators()[1].inactive_stake().is_none()); + assert!(storage.validators()[1].active_stake().is_none()); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - sui::test_utils::destroy(sui); - - scenario.end(); + std::unit_test::destroy(storage); + std::unit_test::destroy(sui); + + runner.finish(); } #[test] fun test_split_up_to_n_sui_take_nothing() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let active_staked_sui_1 = runner.stake_with_and_take(validator_addresses[0], 100); + let active_staked_sui_2 = runner.stake_with_and_take(validator_addresses[1], 100); - let active_staked_sui_1 = stake_with(0, 100, &mut scenario); - let active_staked_sui_2 = stake_with(1, 100, &mut scenario); - - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); - let staked_sui = stake_with(0, 100, &mut scenario); - scenario.next_tx(@0x0); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); storage.join_to_sui_pool(balance::create_for_testing(100 * MIST_PER_SUI)); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_1, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui_2, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_1, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui_2, runner.ctx()); - assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); + assert!(storage.validators()[1].total_sui_amount() == 200 * MIST_PER_SUI); // start of test let sui = storage.split_n_sui( &mut system_state, 0, - scenario.ctx() + runner.ctx(), ); - assert!(sui.value() == 0, 0); - assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI, 0); + assert!(sui.value() == 0); + assert!(storage.total_sui_supply() == 600 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - sui::test_utils::destroy(sui); - - scenario.end(); + std::unit_test::destroy(storage); + std::unit_test::destroy(sui); + + runner.finish(); } #[random_test] @@ -1246,237 +1263,228 @@ module liquid_staking::storage_tests { initial_validator_stake_amount: u64, initial_stake_amount: u64, reward_amount: u64, - split_amount: u64 + split_amount: u64, ) { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[initial_validator_stake_amount % 10_000_000]); + let mut runner = setup_runner(vector[initial_validator_stake_amount % 10_000_000]); + let validator_addresses = runner.genesis_validator_addresses(); let active_staked_sui = stake_with_granular( - 0, + &mut runner, + validator_addresses[0], initial_stake_amount % (1_000_000 * MIST_PER_SUI) + MIST_PER_SUI, - &mut scenario ); - advance_epoch_with_reward_amounts(0, 1, &mut scenario); - advance_epoch_with_reward_amounts(0, 1, &mut scenario); - advance_epoch_with_reward_amounts(0, 1, &mut scenario); + advance_epoch_with_rewards(&mut runner, 1); + advance_epoch_with_rewards(&mut runner, 1); + advance_epoch_with_rewards(&mut runner, 1); - let storage_rebate = advance_epoch_with_reward_amounts_return_rebate( - 0, - reward_amount % (100_000 * MIST_PER_SUI), - 0, - 0, - &mut scenario - ); - sui::test_utils::destroy(storage_rebate); + advance_epoch_with_rewards(&mut runner, reward_amount % 100_000); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui, runner.ctx()); let total_sui_supply = storage.total_sui_supply(); let sui = storage.split_n_sui( &mut system_state, (split_amount % total_sui_supply) % 4001, - scenario.ctx() + runner.ctx(), ); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - sui::test_utils::destroy(sui); - scenario.end(); + std::unit_test::destroy(storage); + std::unit_test::destroy(sui); + runner.finish(); } /* unstake approx n sui from validator tests */ #[test] fun test_unstake_approx_n_sui_from_validator_take_from_inactive_stake() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let active_staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); - let active_staked_sui = stake_with(0, 100, &mut scenario); - - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); - let staked_sui = stake_with(0, 100, &mut scenario); - scenario.next_tx(@0x0); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui, runner.ctx()); - assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_validator( &mut system_state, 0, 100 * MIST_PER_SUI, - scenario.ctx() + runner.ctx(), ); - assert!(amount == 100 * MIST_PER_SUI, 0); - assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI, 0); + assert!(amount == 100 * MIST_PER_SUI); + assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 100 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 200 * MIST_PER_SUI); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_validator_take_all() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let active_staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); - let active_staked_sui = stake_with(0, 100, &mut scenario); - - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); - let staked_sui = stake_with(0, 100, &mut scenario); - scenario.next_tx(@0x0); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui, runner.ctx()); - assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_validator( &mut system_state, 0, 300 * MIST_PER_SUI, - scenario.ctx() + runner.ctx(), ); - assert!(amount == 300 * MIST_PER_SUI, 0); - assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 0, 0); - assert!(storage.validators()[0].inactive_stake().is_none(), 0); - assert!(storage.validators()[0].active_stake().is_none(), 0); + assert!(amount == 300 * MIST_PER_SUI); + assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 300 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 0); + assert!(storage.validators()[0].inactive_stake().is_none()); + assert!(storage.validators()[0].active_stake().is_none()); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] fun test_unstake_approx_n_sui_from_validator_take_nothing() { - let mut scenario = test_scenario::begin(@0x0); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100]); + let active_staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); - let active_staked_sui = stake_with(0, 100, &mut scenario); - - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // stake now looks like [200, 200] => [400, 400] - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); - let staked_sui = stake_with(0, 100, &mut scenario); - scenario.next_tx(@0x0); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 100); + runner.scenario_mut().next_tx(@0x0); - let mut storage = new(scenario.ctx()); - assert!(storage.total_sui_supply() == 0, 0); + let mut storage = new(runner.ctx()); + assert!(storage.total_sui_supply() == 0); - let mut system_state = scenario.take_shared(); + let mut system_state = runner.scenario_mut().take_shared(); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.join_stake(&mut system_state, active_staked_sui, scenario.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.join_stake(&mut system_state, active_staked_sui, runner.ctx()); - assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); + assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); let amount = storage.unstake_approx_n_sui_from_validator( &mut system_state, 0, 0, - scenario.ctx() + runner.ctx(), ); - assert!(amount == 0, 0); - assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI, 0); - assert!(storage.sui_pool().value() == 0, 0); - assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, 0); - assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI, 0); + assert!(amount == 0); + assert!(storage.total_sui_supply() == 300 * MIST_PER_SUI); + assert!(storage.sui_pool().value() == 0); + assert!(storage.validators()[0].total_sui_amount() == 300 * MIST_PER_SUI); + assert!( + storage.validators()[0].inactive_stake().borrow().staked_sui_amount() == 100 * MIST_PER_SUI, + ); + assert!(storage.validators()[0].active_stake().borrow().value() == 100 * MIST_PER_SUI); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - - scenario.end(); + std::unit_test::destroy(storage); + + runner.finish(); } #[test] #[expected_failure(abort_code = 2, location = liquid_staking::storage)] fun test_too_many_validators() { - let mut scenario = test_scenario::begin(@0x0); - - let mut storage = new(scenario.ctx()); - let mut validator_initial_stakes = vector::empty(); 51u64.do!(|_| { validator_initial_stakes.push_back(100); }); - setup_sui_system(&mut scenario, validator_initial_stakes); - scenario.next_tx(@0x0); + let mut runner = setup_runner(validator_initial_stakes); + let validator_addresses = runner.genesis_validator_addresses(); + + let mut storage = new(runner.ctx()); + + runner.scenario_mut().next_tx(@0x0); 51u64.do!(|i| { - let stake = stake_with(i, 100, &mut scenario); + let stake = runner.stake_with_and_take(validator_addresses[i], 100); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - storage.join_stake(&mut system_state, stake, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + storage.join_stake(&mut system_state, stake, runner.ctx()); test_scenario::return_shared(system_state); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); }); - sui::test_utils::destroy(storage); - scenario.end(); + std::unit_test::destroy(storage); + runner.finish(); } #[test] fun test_unstake_rounding_loss_regression() { - let mut scenario = test_scenario::begin(@0x0); - // Single validator with 500 SUI - setup_sui_system(&mut scenario, vector[500]); + let mut runner = setup_runner(vector[500]); + let validator_addresses = runner.genesis_validator_addresses(); // User stakes 200 SUI → validator has 700 tokens total - let staked_sui = stake_with(0, 200, &mut scenario); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + let staked_sui = runner.stake_with_and_take(validator_addresses[0], 200); + advance_epoch_no_rewards(&mut runner); // Add 400 SUI rewards → rate becomes 1100:700 = 11:7 - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); - storage.refresh(&mut system_state, scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); + storage.refresh(&mut system_state, runner.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); let initial_total = storage.total_sui_supply(); let initial_validator_total = storage.validators()[0].total_sui_amount(); @@ -1487,7 +1495,7 @@ module liquid_staking::storage_tests { &mut system_state, 0, MIST_PER_SUI, - scenario.ctx() + runner.ctx(), ); let final_total = storage.total_sui_supply(); @@ -1508,51 +1516,49 @@ module liquid_staking::storage_tests { assert!(final_total == final_sui_pool + final_validator_total); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - scenario.end(); + std::unit_test::destroy(storage); + runner.finish(); } #[test] fun test_refresh_inactive_validator_with_rewards() { - let mut scenario = test_scenario::begin(@0x0); - - setup_sui_system(&mut scenario, vector[100, 100]); + let mut runner = setup_runner(vector[100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - let staked_sui = stake_with(1, 100, &mut scenario); + let staked_sui = runner.stake_with_and_take(validator_addresses[1], 100); // Activate the stake - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); - let mut system_state = scenario.take_shared(); - let mut storage = new(scenario.ctx()); - storage.join_stake(&mut system_state, staked_sui, scenario.ctx()); - storage.refresh(&mut system_state, scenario.ctx()); + runner.scenario_mut().next_tx(@0x0); + let mut system_state = runner.scenario_mut().take_shared(); + let mut storage = new(runner.ctx()); + storage.join_stake(&mut system_state, staked_sui, runner.ctx()); + storage.refresh(&mut system_state, runner.ctx()); test_scenario::return_shared(system_state); // Earn rewards, then remove validator. // Storage's exchange rate is now stale — it doesn't include these rewards. - advance_epoch_with_reward_amounts(0, 400, &mut scenario); + advance_epoch_with_rewards(&mut runner, 400); - scenario.next_tx(@0x1); - let mut system_state = scenario.take_shared(); - system_state.request_remove_validator(scenario.ctx()); - test_scenario::return_shared(system_state); + runner.set_sender(validator_addresses[1]); + runner.remove_validator(); - advance_epoch_with_reward_amounts(0, 0, &mut scenario); + advance_epoch_no_rewards(&mut runner); // refresh() detects the inactive validator and unstakes everything. // redeem_and_update_accounting subtracts the actual redeemed amount // (at the current rate, which includes rewards) from // total_sui_amount (which was computed at the stale rate) - scenario.next_tx(@0x0); - let mut system_state = scenario.take_shared(); - assert!(!system_state.active_validator_addresses().contains(&@0x1)); + runner.scenario_mut().next_tx(@0x0); + let mut system_state = runner.scenario_mut().take_shared(); + assert!(!system_state.active_validator_addresses().contains(&validator_addresses[1])); - storage.refresh(&mut system_state, scenario.ctx()); + storage.refresh(&mut system_state, runner.ctx()); assert!(storage.validators().length() == 0); test_scenario::return_shared(system_state); - sui::test_utils::destroy(storage); - scenario.end(); + std::unit_test::destroy(storage); + runner.finish(); } } diff --git a/contracts/tests/test_utils.move b/contracts/tests/test_utils.move index 4a189e7..3b3806f 100644 --- a/contracts/tests/test_utils.move +++ b/contracts/tests/test_utils.move @@ -1,24 +1,23 @@ -#[allow(deprecated_usage)] #[test_only] module liquid_staking::test_utils { - use sui::address; - use sui_system::governance_test_utils::{ - create_validator_for_testing, - }; - use sui_system::validator::{Validator}; + use sui_system::{test_runner::{Self, TestRunner}, validator_builder}; - /// Create a validator set with the given stake amounts - public fun create_validators_with_stakes( - stakes: vector, - ctx: &mut TxContext, - ): vector { - let mut i = 0; - let mut validators = vector[]; - while (i < stakes.length()) { - let validator = create_validator_for_testing(address::from_u256(i as u256), stakes[i], ctx); - validators.push_back(validator); - i = i + 1 - }; - validators + public fun advance_epoch_no_rewards(runner: &mut TestRunner) { + runner.advance_epoch(option::none()).destroy_for_testing(); } -} \ No newline at end of file + + public fun advance_epoch_with_rewards(runner: &mut TestRunner, computation_charge: u64) { + let opts = runner.advance_epoch_opts().computation_charge(computation_charge); + runner.advance_epoch(option::some(opts)).destroy_for_testing(); + } + + public fun setup_runner(stakes: vector): TestRunner { + let mut runner = test_runner::new() + .validators(stakes.map!(|stake| validator_builder::new().initial_stake(stake))) + .build(); + + advance_epoch_no_rewards(&mut runner); + + runner + } +} diff --git a/contracts/tests/weight_tests.move b/contracts/tests/weight_tests.move index d4d8adf..1279416 100644 --- a/contracts/tests/weight_tests.move +++ b/contracts/tests/weight_tests.move @@ -1,195 +1,181 @@ #[test_only] module liquid_staking::weight_tests { - /* Tests */ - use sui::vec_map::{Self}; - use sui::test_scenario::{Self, Scenario}; - use sui::coin::{Self}; - use sui::address; - use sui_system::governance_test_utils::{ - create_sui_system_state_for_testing, - advance_epoch_with_reward_amounts, + use liquid_staking::{ + fees, + liquid_staking::create_lst, + test_utils::setup_runner, + weight::{Self, WeightHook} }; - use sui_system::sui_system::{SuiSystemState}; - use sui_system::staking_pool::StakedSui; + use sui::{coin, test_scenario, vec_map}; + use sui_system::sui_system::SuiSystemState; + const MIST_PER_SUI: u64 = 1_000_000_000; - use liquid_staking::fees::{Self}; - use liquid_staking::liquid_staking::{create_lst}; - use liquid_staking::weight::{Self, WeightHook}; - use liquid_staking::test_utils::create_validators_with_stakes; public struct TEST has drop {} - #[test_only] - public fun stake_with(validator_index: u64, amount: u64, scenario: &mut Scenario): StakedSui { - scenario.next_tx(@0x0); - - let mut system_state = scenario.take_shared(); - - let ctx = scenario.ctx(); - - let staked_sui = system_state.request_add_stake_non_entry( - coin::mint_for_testing(amount * MIST_PER_SUI, ctx), - address::from_u256(validator_index as u256), - ctx - ); - - test_scenario::return_shared(system_state); - scenario.next_tx(@0x0); - - staked_sui - } - - #[allow(deprecated_usage)] - #[test_only] - fun setup_sui_system(scenario: &mut Scenario, stakes: vector) { - let validators = create_validators_with_stakes(stakes, scenario.ctx()); - create_sui_system_state_for_testing(validators, 0, 0, scenario.ctx()); - - advance_epoch_with_reward_amounts(0, 0, scenario); - } - - - #[test] - fun test_rebalance() { - let mut scenario = test_scenario::begin(@0x0); + #[test] + fun test_rebalance() { + let mut runner = setup_runner(vector[100, 100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100, 100]); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()).to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + fees::new_builder(runner.ctx()).to_fee_config(), + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - let mut system_state = scenario.take_shared(); - let sui = coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()); - let lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let sui = coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()); + let lst = lst_info.mint(&mut system_state, sui, runner.ctx()); - assert!(lst_info.total_lst_supply() == 100 * MIST_PER_SUI, 0); - assert!(lst_info.storage().total_sui_supply() == 100 * MIST_PER_SUI, 0); + assert!(lst_info.total_lst_supply() == 100 * MIST_PER_SUI); + assert!(lst_info.storage().total_sui_supply() == 100 * MIST_PER_SUI); - let (mut weight_hook, weight_hook_admin_cap) = weight::new(admin_cap, scenario.ctx()); + let (mut weight_hook, weight_hook_admin_cap) = weight::new(admin_cap, runner.ctx()); weight_hook.set_validator_addresses_and_weights( - &weight_hook_admin_cap, + &weight_hook_admin_cap, { let mut map = vec_map::empty(); - map.insert(address::from_u256(0), 100); - map.insert(address::from_u256(1), 300); + map.insert(validator_addresses[0], 100); + map.insert(validator_addresses[1], 300); map - } + }, ); - weight_hook.rebalance(&mut system_state, &mut lst_info, scenario.ctx()); + weight_hook.rebalance(&mut system_state, &mut lst_info, runner.ctx()); - assert!(lst_info.storage().validators().borrow(0).total_sui_amount() == 25 * MIST_PER_SUI, 0); - assert!(lst_info.storage().validators().borrow(1).total_sui_amount() == 75 * MIST_PER_SUI, 0); + assert!(lst_info.storage().validators().borrow(0).total_sui_amount() == 25 * MIST_PER_SUI); + assert!(lst_info.storage().validators().borrow(1).total_sui_amount() == 75 * MIST_PER_SUI); weight_hook.set_validator_addresses_and_weights( - &weight_hook_admin_cap, + &weight_hook_admin_cap, { let mut map = vec_map::empty(); - map.insert(address::from_u256(2), 100); + map.insert(validator_addresses[2], 100); map - } + }, ); - weight_hook.rebalance(&mut system_state, &mut lst_info, scenario.ctx()); + weight_hook.rebalance(&mut system_state, &mut lst_info, runner.ctx()); - assert!(lst_info.storage().validators().borrow(0).total_sui_amount() == 0, 0); - assert!(lst_info.storage().validators().borrow(1).total_sui_amount() == 0, 0); - assert!(lst_info.storage().validators().borrow(2).total_sui_amount() == 100 * MIST_PER_SUI, 0); + assert!(lst_info.storage().validators().borrow(0).total_sui_amount() == 0); + assert!(lst_info.storage().validators().borrow(1).total_sui_amount() == 0); + assert!(lst_info.storage().validators().borrow(2).total_sui_amount() == 100 * MIST_PER_SUI); // test update fees - let new_fees = fees::new_builder(scenario.ctx()).set_sui_mint_fee_bps(100).to_fee_config(); + let new_fees = fees::new_builder(runner.ctx()).set_sui_mint_fee_bps(100).to_fee_config(); weight_hook.update_fees(&weight_hook_admin_cap, &mut lst_info, new_fees); - assert!(lst_info.fee_config().sui_mint_fee_bps() == 100, 0); + assert!(lst_info.fee_config().sui_mint_fee_bps() == 100); // mint some lst - let sui = coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()); - let lst2 = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let sui = coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()); + let lst2 = lst_info.mint(&mut system_state, sui, runner.ctx()); // test collect fees - let collected_fees = weight_hook.collect_fees(&weight_hook_admin_cap, &mut lst_info, &mut system_state, scenario.ctx()); - assert!(collected_fees.value() == MIST_PER_SUI, 0); + let collected_fees = weight_hook.collect_fees( + &weight_hook_admin_cap, + &mut lst_info, + &mut system_state, + runner.ctx(), + ); + assert!(collected_fees.value() == MIST_PER_SUI); // sharing to make sure shared object deletion actually works lol transfer::public_share_object(weight_hook); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); - let weight_hook = scenario.take_shared>(); + let weight_hook = runner.scenario_mut().take_shared>(); let admin_cap = weight_hook.eject(weight_hook_admin_cap); test_scenario::return_shared(system_state); - sui::test_utils::destroy(admin_cap); - sui::test_utils::destroy(lst_info); - sui::test_utils::destroy(lst); - sui::test_utils::destroy(lst2); - sui::test_utils::destroy(collected_fees); + std::unit_test::destroy(admin_cap); + std::unit_test::destroy(lst_info); + std::unit_test::destroy(lst); + std::unit_test::destroy(lst2); + std::unit_test::destroy(collected_fees); - scenario.end(); - } + runner.finish(); + } - #[test] - fun test_custom_redeem_request() { - let mut scenario = test_scenario::begin(@0x0); + #[test] + fun test_custom_redeem_request() { + let mut runner = setup_runner(vector[100, 100, 100]); + let validator_addresses = runner.genesis_validator_addresses(); - setup_sui_system(&mut scenario, vector[100, 100, 100]); - scenario.next_tx(@0x0); + runner.scenario_mut().next_tx(@0x0); let (admin_cap, mut lst_info) = create_lst( - fees::new_builder(scenario.ctx()).set_custom_redeem_fee_bps(100).to_fee_config(), - coin::create_treasury_cap_for_testing(scenario.ctx()), - scenario.ctx() + fees::new_builder(runner.ctx()).set_custom_redeem_fee_bps(100).to_fee_config(), + coin::create_treasury_cap_for_testing(runner.ctx()), + runner.ctx(), ); - let mut system_state = scenario.take_shared(); - let sui = coin::mint_for_testing(100 * MIST_PER_SUI, scenario.ctx()); - let mut lst = lst_info.mint(&mut system_state, sui, scenario.ctx()); + let mut system_state = runner.scenario_mut().take_shared(); + let sui = coin::mint_for_testing(100 * MIST_PER_SUI, runner.ctx()); + let mut lst = lst_info.mint(&mut system_state, sui, runner.ctx()); - assert!(lst_info.total_lst_supply() == 100 * MIST_PER_SUI, 0); - assert!(lst_info.storage().total_sui_supply() == 100 * MIST_PER_SUI, 0); + assert!(lst_info.total_lst_supply() == 100 * MIST_PER_SUI); + assert!(lst_info.storage().total_sui_supply() == 100 * MIST_PER_SUI); - let (mut weight_hook, weight_hook_admin_cap) = weight::new(admin_cap, scenario.ctx()); + let (mut weight_hook, weight_hook_admin_cap) = weight::new(admin_cap, runner.ctx()); weight_hook.set_validator_addresses_and_weights( - &weight_hook_admin_cap, + &weight_hook_admin_cap, { let mut map = vec_map::empty(); - map.insert(address::from_u256(0), 100); - map.insert(address::from_u256(1), 300); + map.insert(validator_addresses[0], 100); + map.insert(validator_addresses[1], 300); map - } + }, ); - weight_hook.rebalance(&mut system_state, &mut lst_info, scenario.ctx()); + weight_hook.rebalance(&mut system_state, &mut lst_info, runner.ctx()); - assert!(lst_info.storage().validators().borrow(0).total_sui_amount() == 25 * MIST_PER_SUI, 0); - assert!(lst_info.storage().validators().borrow(1).total_sui_amount() == 75 * MIST_PER_SUI, 0); + assert!(lst_info.storage().validators().borrow(0).total_sui_amount() == 25 * MIST_PER_SUI); + assert!(lst_info.storage().validators().borrow(1).total_sui_amount() == 75 * MIST_PER_SUI); - let lst_to_unstake = lst.split(10 * MIST_PER_SUI, scenario.ctx()); - let mut custom_redeem_request = lst_info.custom_redeem_request(lst_to_unstake,&mut system_state, scenario.ctx()); - weight_hook.handle_custom_redeem_request(&mut system_state, &mut lst_info, &mut custom_redeem_request, scenario.ctx()); + let lst_to_unstake = lst.split(10 * MIST_PER_SUI, runner.ctx()); + let mut custom_redeem_request = lst_info.custom_redeem_request( + lst_to_unstake, + &mut system_state, + runner.ctx(), + ); + weight_hook.handle_custom_redeem_request( + &mut system_state, + &mut lst_info, + &mut custom_redeem_request, + runner.ctx(), + ); - assert!(lst_info.storage().validators().borrow(0).total_sui_amount() == 25 * MIST_PER_SUI - 2_500_000_000, 0); - assert!(lst_info.storage().validators().borrow(1).total_sui_amount() == 75 * MIST_PER_SUI - 7_500_000_000, 0); + assert!( + lst_info.storage().validators().borrow(0).total_sui_amount() == 25 * MIST_PER_SUI - 2_500_000_000, + ); + assert!( + lst_info.storage().validators().borrow(1).total_sui_amount() == 75 * MIST_PER_SUI - 7_500_000_000, + ); - let sui = lst_info.custom_redeem(custom_redeem_request, &mut system_state, scenario.ctx()); - assert!(sui.value() == 10 * MIST_PER_SUI - 100_000_000, 0); // 0.1 sui fee + let sui = lst_info.custom_redeem( + custom_redeem_request, + &mut system_state, + runner.ctx(), + ); + assert!(sui.value() == 10 * MIST_PER_SUI - 100_000_000); // 0.1 sui fee test_scenario::return_shared(system_state); - sui::test_utils::destroy(weight_hook); - sui::test_utils::destroy(weight_hook_admin_cap); - sui::test_utils::destroy(lst_info); - sui::test_utils::destroy(lst); - sui::test_utils::destroy(sui); + std::unit_test::destroy(weight_hook); + std::unit_test::destroy(weight_hook_admin_cap); + std::unit_test::destroy(lst_info); + std::unit_test::destroy(lst); + std::unit_test::destroy(sui); - scenario.end(); - } -} \ No newline at end of file + runner.finish(); + } +}