Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
187b830
test: `stake_to_validator` and `unstake_from_validator`
0xdefacto Apr 3, 2023
f63d0cf
stake_to_validator payable
0xdefacto Apr 3, 2023
1ac4301
start from the user side before stake and unstake
0xdefacto Apr 10, 2023
7fa1047
add a new test interface: set_pending_release
0xdefacto May 5, 2023
57c1a71
set_pending_releases
0xdefacto May 5, 2023
fc3d486
cargo fmt
0xdefacto May 5, 2023
1dba26a
set_total_staked_near_amount
0xdefacto May 6, 2023
05bd451
use U128
0xdefacto May 6, 2023
e77a5a5
include epoch request amounts in summary
0xdefacto May 8, 2023
256a773
make `epoch_cleanup` public
0xdefacto May 8, 2023
3e8cb7d
set_draining
0xdefacto May 8, 2023
e05739d
replace set_pending_release with set_unstake_fired_epoch
0xdefacto May 17, 2023
f265b6e
Merge remote-tracking branch 'origin/main' into test/validator-stake-…
0xdefacto Jun 14, 2023
984797d
fix compile
0xdefacto Jun 14, 2023
d8b5f7b
stake from the validator with max delta
0xdefacto Jun 17, 2023
d5daea3
Merge branch 'main' into test/validator-stake-unstake
linguists Jul 3, 2023
63da445
`assert_zero_requested` is useless
0xdefacto Jul 3, 2023
e5353fa
add `epoch_cleanup_for_test`
0xdefacto Jul 3, 2023
7b2d937
comments
0xdefacto Jul 3, 2023
399a0a4
Merge branch 'main' into test/validator-stake-unstake
linguists Jul 4, 2023
14d5b50
Merge branch 'main' into test/validator-stake-unstake
linguists Jul 13, 2023
5693048
sync in successful callbacks.
0xdefacto Aug 21, 2023
93470f3
tests passed
0xdefacto Aug 21, 2023
0d23384
remove public sync_balance
0xdefacto Aug 22, 2023
cb6b24d
refactor
0xdefacto Aug 22, 2023
ab3a1de
refactor
0xdefacto Aug 22, 2023
15bf387
tests
0xdefacto Aug 22, 2023
4860e08
prettier
0xdefacto Aug 22, 2023
bf15024
sync balance in drain_unstake
0xdefacto Aug 25, 2023
476f8ba
resolve comments
0xdefacto Aug 25, 2023
e5e1148
drain_unstake return promise
0xdefacto Aug 28, 2023
1d1d5e1
remove on_unstake_success release_lock
0xdefacto Aug 28, 2023
e6f1374
epoch_stake chain
0xdefacto Aug 28, 2023
610ee73
epoch_unstake chain
0xdefacto Aug 28, 2023
f7e1167
epoch_stake return Option<bool>
0xdefacto Aug 30, 2023
4de3b96
epoch_unstake return Option<bool>
0xdefacto Aug 30, 2023
a81fc30
chores
0xdefacto Aug 30, 2023
e8640a4
make test contracts
0xdefacto Aug 30, 2023
4c4fda0
fix
0xdefacto Aug 30, 2023
4581ddc
drain_unstake return Option<bool>
0xdefacto Aug 31, 2023
377a6df
remove GAS_CB_VALIDATOR_RETURN_TRUE
0xdefacto Aug 31, 2023
ed0f7cc
import PromiseError
0xdefacto Aug 31, 2023
8fb4301
Merge branch 'main' into test/validator-stake-unstake
0xdefacto Aug 31, 2023
6e68369
fix compile
0xdefacto Aug 31, 2023
06f1fa1
only import U46 in test
0xdefacto Aug 31, 2023
21e1e52
fix compile
0xdefacto Aug 31, 2023
6d1ef3a
Merge branch 'feat/sync-after' into test/validator-stake-unstake-sync
0xdefacto Aug 31, 2023
7a7ecbf
return bool
0xdefacto Aug 31, 2023
a6bdfc5
tests
0xdefacto Aug 31, 2023
86e7392
fix a test
0xdefacto Aug 31, 2023
faa3c33
Merge branch 'feat/sync-after' into test/validator-stake-unstake-sync
0xdefacto Aug 31, 2023
49e1365
validator_get_account_callback return bool
0xdefacto Sep 1, 2023
c6b68ba
validator_get_account_callback return bool
0xdefacto Sep 1, 2023
621f932
Merge remote-tracking branch 'origin/feat/sync-after' into test/valid…
0xdefacto Sep 1, 2023
ecf08ed
remove GAS_CB_VALIDATOR_RETURN_TRUE
0xdefacto Sep 1, 2023
94f08a8
Merge remote-tracking branch 'origin/feat/sync-after' into test/valid…
0xdefacto Sep 1, 2023
ff01ef3
test interfaces return Promise
0xdefacto Sep 1, 2023
848bf8c
fix compile
0xdefacto Sep 1, 2023
300c6ed
perfect existing comments
0xdefacto Sep 4, 2023
2d08ba4
change an error's name and content.
0xdefacto Sep 4, 2023
3d3ef9e
assertHasLog
0xdefacto Sep 4, 2023
749bd4b
assertHasLog
0xdefacto Sep 4, 2023
59c2300
perfect comments
0xdefacto Sep 4, 2023
802afd9
comment
0xdefacto Sep 4, 2023
41f1121
Merge remote-tracking branch 'origin/feat/sync-after' into test/valid…
0xdefacto Sep 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
338 changes: 245 additions & 93 deletions contracts/linear/src/epoch_actions.rs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions contracts/linear/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ pub const ERR_VALIDATOR_UNSTAKE_WHEN_LOCKED: &str =
pub const ERR_VALIDATOR_WITHDRAW_WHEN_LOCKED: &str =
"Cannot withdraw from a pending release validator";
pub const ERR_VALIDATOR_ALREADY_EXECUTING_ACTION: &str = "Validator is already executing action";
pub const ERR_VALIDATOR_SYNC_BALANCE_NOT_ALLOWED: &str =
"Validator sync balance can only be called after stake or unstake";

// liquidity pool
pub const ERR_NON_POSITIVE_MIN_FEE: &str = "The min fee basis points should be positive";
Expand Down
8 changes: 7 additions & 1 deletion contracts/linear/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub enum Event<'a> {
new_unstaked_balance: &'a U128,
new_total_balance: &'a U128,
},
SyncValidatorBalanceFailed {
SyncValidatorBalanceFailedLargeDiff {
validator_id: &'a AccountId,
old_staked_balance: &'a U128,
old_unstaked_balance: &'a U128,
Expand All @@ -99,6 +99,12 @@ pub enum Event<'a> {
new_unstaked_balance: &'a U128,
new_total_balance: &'a U128,
},
SyncValidatorBalanceFailedCantGetAccount {
validator_id: &'a AccountId,
old_staked_balance: &'a U128,
old_unstaked_balance: &'a U128,
old_total_balance: &'a U128,
},
// Staking Pool Interface
Deposit {
account_id: &'a AccountId,
Expand Down
105 changes: 93 additions & 12 deletions contracts/linear/src/validator_pool.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::epoch_actions::ext_self_action_cb;
use crate::errors::*;
use crate::events::Event;
use crate::legacy::ValidatorV1_0_0;
Expand All @@ -6,15 +7,20 @@ use crate::legacy::ValidatorV1_4_0;
use crate::types::*;
use crate::utils::*;
use crate::*;
use near_sdk::PromiseOrValue;
use near_sdk::{
borsh::{self, BorshDeserialize, BorshSerialize},
collections::UnorderedMap,
ext_contract, is_promise_success,
json_types::U128,
near_bindgen, require, AccountId, Balance, EpochHeight, Promise,
};

use std::cmp::{max, min, Ordering};

#[cfg(feature = "test")]
use near_sdk::json_types::U64;

const STAKE_SMALL_CHANGE_AMOUNT: Balance = ONE_NEAR;
const UNSTAKE_FACTOR: u128 = 2;
const MAX_UPDATE_WEIGHTS_COUNT: usize = 300;
Expand Down Expand Up @@ -160,6 +166,29 @@ impl ValidatorPool {
old_weight
}

// to mock pending release validators at the beginnings of simulation tests
#[cfg(feature = "test")]
pub fn set_unstake_fired_epoch(&mut self, validator_id: &AccountId, epoch_height: EpochHeight) {
let mut validator: Validator = self
.validators
.get(validator_id)
.expect(ERR_VALIDATOR_NOT_EXIST)
.into();
validator.unstake_fired_epoch = epoch_height;
self.validators.insert(validator_id, &validator.into());
}

// to mock draining validators at the beginnings of simulation tests
#[cfg(feature = "test")]
pub fn set_draining(&mut self, validator_id: &AccountId) {
let mut validator: Validator = self
.validators
.get(validator_id)
.expect(ERR_VALIDATOR_NOT_EXIST)
.into();
validator.set_draining(self, true);
}

/// Update base stake amount of the validator
pub fn update_base_stake_amount(&mut self, validator_id: &AccountId, amount: Balance) {
let mut validator: Validator = self
Expand Down Expand Up @@ -584,6 +613,32 @@ impl LiquidStakingContract {
}
}

#[cfg(feature = "test")]
pub fn batch_set_unstake_fired_epoch(
&mut self,
validator_ids: Vec<AccountId>,
epoch_heights: Vec<U64>,
) {
self.assert_running();
self.assert_manager();
for i in 0..validator_ids.len() {
self.validator_pool
.set_unstake_fired_epoch(&validator_ids[i], epoch_heights[i].into());
}
}

#[cfg(feature = "test")]
pub fn set_drainings(&mut self, validator_ids: Vec<AccountId>) {
for i in 0..validator_ids.len() {
self.validator_pool.set_draining(&validator_ids[i]);
}
}

#[cfg(feature = "test")]
pub fn set_total_staked_near_amount(&mut self, amount: U128) {
self.total_staked_near_amount = amount.0;
}

// --- View Functions ---

#[cfg(feature = "test")]
Expand Down Expand Up @@ -611,7 +666,11 @@ impl LiquidStakingContract {

#[ext_contract(ext_self_validator_drain_cb)]
trait ValidatorDrainCallbacks {
fn validator_drain_unstaked_callback(&mut self, validator_id: AccountId, amount: U128);
fn validator_drain_unstaked_callback(
&mut self,
validator_id: AccountId,
amount: U128,
) -> PromiseOrValue<()>;

fn validator_drain_withdraw_callback(&mut self, validator_id: AccountId, amount: U128);
}
Expand All @@ -621,12 +680,16 @@ impl LiquidStakingContract {
/// This method is designed to drain a validator.
/// The weight of target validator should be set to 0 before calling this.
/// And a following call to drain_withdraw MUST be made after 4 epoches.
pub fn drain_unstake(&mut self, validator_id: AccountId) {
pub fn drain_unstake(&mut self, validator_id: AccountId) -> Promise {
self.assert_running();
self.assert_manager();

// make sure enough gas was given
let min_gas = GAS_DRAIN_UNSTAKE + GAS_EXT_UNSTAKE + GAS_CB_VALIDATOR_UNSTAKED;
let min_gas = GAS_DRAIN_UNSTAKE
+ GAS_EXT_UNSTAKE
+ GAS_CB_VALIDATOR_UNSTAKED
+ GAS_SYNC_BALANCE
+ GAS_CB_VALIDATOR_SYNC_BALANCE;
require!(
env::prepaid_gas() >= min_gas,
format!("{}. require at least {:?}", ERR_NO_ENOUGH_GAS, min_gas)
Expand Down Expand Up @@ -676,9 +739,9 @@ impl LiquidStakingContract {
unstake_amount.into(),
env::current_account_id(),
NO_DEPOSIT,
GAS_CB_VALIDATOR_UNSTAKED,
GAS_CB_VALIDATOR_UNSTAKED + GAS_SYNC_BALANCE + GAS_CB_VALIDATOR_SYNC_BALANCE,
),
);
)
}

/// Withdraw from a drained validator
Expand Down Expand Up @@ -735,7 +798,11 @@ impl LiquidStakingContract {
}

#[private]
pub fn validator_drain_unstaked_callback(&mut self, validator_id: AccountId, amount: U128) {
pub fn validator_drain_unstaked_callback(
&mut self,
validator_id: AccountId,
amount: U128,
) -> PromiseOrValue<()> {
let amount = amount.into();
let mut validator = self
.validator_pool
Expand All @@ -751,6 +818,16 @@ impl LiquidStakingContract {
amount: &U128(amount),
}
.emit();

validator
.sync_account_balance()
.then(ext_self_action_cb::validator_get_account_callback(
validator_id,
env::current_account_id(),
NO_DEPOSIT,
GAS_CB_VALIDATOR_SYNC_BALANCE,
))
.into()
} else {
// unstake failed, revert
validator.on_unstake_failed(&mut self.validator_pool);
Expand All @@ -760,6 +837,8 @@ impl LiquidStakingContract {
amount: &U128(amount),
}
.emit();

PromiseOrValue::Value(())
}
}

Expand Down Expand Up @@ -927,8 +1006,7 @@ impl Validator {
}

pub fn on_stake_success(&mut self, pool: &mut ValidatorPool, amount: Balance) {
self.post_execution(pool);

// Do not call post_execution() here because we need to sync account balance after stake
self.staked_amount += amount;
pool.save_validator(self);
}
Expand Down Expand Up @@ -965,8 +1043,7 @@ impl Validator {
}

pub fn on_unstake_success(&mut self, pool: &mut ValidatorPool, amount: Balance) {
self.post_execution(pool);

// Do not call post_execution() here because we need to sync account balance after unstake
self.staked_amount -= amount;
self.unstaked_amount += amount;
pool.save_validator(self);
Expand Down Expand Up @@ -1000,8 +1077,12 @@ impl Validator {
pool.save_validator(self);
}

pub fn sync_account_balance(&mut self, pool: &mut ValidatorPool) -> Promise {
self.pre_execution(pool);
/// Due to shares calculation and rounding of staking pool contract,
/// the amount of staked and unstaked balance might be a little bit
/// different than we requested.
/// This method is to sync the actual numbers with the validator.
pub fn sync_account_balance(&mut self) -> Promise {
require!(self.executing, ERR_VALIDATOR_SYNC_BALANCE_NOT_ALLOWED);

ext_staking_pool::get_account(
env::current_account_id(),
Expand Down
31 changes: 19 additions & 12 deletions contracts/mock-staking-pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub struct MockStakingPool {
staked: LookupMap<AccountId, u128>,
/// for testing purpose, simulates contract panic
panic: bool,
get_account_fail: bool,

staked_delta: u128,
unstaked_delta: u128,
}

#[near_bindgen]
Expand All @@ -59,6 +63,9 @@ impl MockStakingPool {
deposits: LookupMap::new(b"d"),
staked: LookupMap::new(b"s"),
panic: false,
get_account_fail: false,
staked_delta: 0,
unstaked_delta: 0,
}
}
}
Expand All @@ -84,6 +91,10 @@ impl StakingPool for MockStakingPool {

fn get_account(&self, account_id: AccountId) -> HumanReadableAccount {
require!(!self.panic, "Test Panic!");
require!(
!self.get_account_fail,
"get_account() failed, for testing purpose",
);
HumanReadableAccount {
account_id: account_id.clone(),
staked_balance: U128::from(self.internal_get_staked(&account_id)),
Expand Down Expand Up @@ -157,17 +168,13 @@ impl MockStakingPool {
self.panic = panic;
}

pub fn adjust_balance(
&mut self,
account_id: AccountId,
staked_delta: U128,
unstaked_delta: U128,
) {
let staked_amount = self.internal_get_staked(&account_id) - staked_delta.0;
let unstaked_amount = self.internal_get_unstaked_deposit(&account_id) + unstaked_delta.0;
pub fn set_get_account_fail(&mut self, value: bool) {
self.get_account_fail = value;
}

self.staked.insert(&account_id, &staked_amount);
self.deposits.insert(&account_id, &unstaked_amount);
pub fn set_balance_delta(&mut self, staked_delta: U128, unstaked_delta: U128) {
self.staked_delta = staked_delta.0;
self.unstaked_delta = unstaked_delta.0;
}
}

Expand All @@ -190,7 +197,7 @@ impl MockStakingPool {
assert!(unstaked_deposit >= amount);

let new_deposit = unstaked_deposit - amount;
let new_staked = self.internal_get_staked(&account_id) + amount;
let new_staked = self.internal_get_staked(&account_id) + amount - self.staked_delta;

self.deposits.insert(&account_id, &new_deposit);
self.staked.insert(&account_id, &new_staked);
Expand All @@ -202,7 +209,7 @@ impl MockStakingPool {
assert!(staked >= amount);

let unstaked_deposit = self.internal_get_unstaked_deposit(&account_id);
let new_deposit = unstaked_deposit + amount;
let new_deposit = unstaked_deposit + amount + self.unstaked_delta;
let new_staked = staked - amount;

self.deposits.insert(&account_id, &new_deposit);
Expand Down
4 changes: 3 additions & 1 deletion makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,16 @@ test-unit:

TEST_FILE ?= **
LOGS ?=
test-linear: linear_test mock-staking-pool mock-fungible-token mock-dex mock-lockup mock-whitelist
test-contracts: linear_test mock-staking-pool mock-fungible-token mock-dex mock-lockup mock-whitelist
@mkdir -p ./tests/compiled-contracts/
@cp ./res/linear_test.wasm ./tests/compiled-contracts/linear.wasm
@cp ./res/mock_staking_pool.wasm ./tests/compiled-contracts/mock_staking_pool.wasm
@cp ./res/mock_fungible_token.wasm ./tests/compiled-contracts/mock_fungible_token.wasm
@cp ./res/mock_dex.wasm ./tests/compiled-contracts/mock_dex.wasm
@cp ./res/mock_lockup.wasm ./tests/compiled-contracts/mock_lockup.wasm
@cp ./res/mock_whitelist.wasm ./tests/compiled-contracts/mock_whitelist.wasm

test-linear: test-contracts
cd tests && NEAR_PRINT_LOGS=$(LOGS) npx near-workspaces-ava --timeout=2m __tests__/linear/$(TEST_FILE).ava.ts --verbose

test-mock-staking-pool: mock-staking-pool
Expand Down
2 changes: 1 addition & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ To run only one test file:
To run only one test:

npm run test -- -m "root sets*" # matches tests with titles starting with "root sets"
yarn test -m "root sets*" # same thing using yarn instead of npm, see https://yarnpkg.com/
yarn test -m "root sets*" # same thing using yarn instead of npm, see https://yarnpkg.com/
Loading