diff --git a/src/staker.cairo b/src/staker.cairo index e953c16..ae48713 100644 --- a/src/staker.cairo +++ b/src/staker.cairo @@ -75,8 +75,6 @@ pub mod Staker { }; use super::{IStaker}; - - #[derive(Copy, Drop, PartialEq, Debug)] pub struct DelegatedSnapshot { pub timestamp: u64, @@ -86,7 +84,7 @@ pub mod Staker { const TWO_POW_64: u128 = 0x10000000000000000; const TWO_POW_192: u256 = 0x1000000000000000000000000000000000000000000000000; const TWO_POW_192_DIVISOR: NonZero = 0x1000000000000000000000000000000000000000000000000; - const HALF: u128 = 0x80000000000000000000000000000000_u128; + const TWO_POW_127: u128 = 0x80000000000000000000000000000000_u128; pub(crate) impl DelegatedSnapshotStorePacking of StorePacking { fn pack(value: DelegatedSnapshot) -> felt252 { @@ -266,7 +264,7 @@ pub mod Staker { .write(delegate, self.insert_snapshot(delegate, get_block_timestamp()) + amount); let total_staked = self.total_staked.read(); - assert(total_staked + amount >= total_staked, 'BAD AMOUNT'); + self.total_staked.write(total_staked + amount); self.staking_log.log_change(amount, total_staked); @@ -357,7 +355,9 @@ pub mod Staker { } fn get_cumulative_seconds_per_total_staked_at(self: @ContractState, timestamp: u64) -> u256 { - if let Option::Some((log_record, idx)) = self.staking_log.find_in_change_log(timestamp) { + let timestamp_seconds = timestamp / 1000; + + if let Option::Some((log_record, idx)) = self.staking_log.find_in_change_log(timestamp_seconds) { let total_staked = if (idx == self.staking_log.len() - 1) { // if last rescord found self.total_staked.read() @@ -373,7 +373,7 @@ pub mod Staker { return 0_u64.into(); } - let diff_seconds: u128 = ((next_log_record.timestamp - log_record.timestamp) / 1000).into(); + let diff_seconds: u128 = (next_log_record.timestamp - log_record.timestamp).into(); // Divide u64 by fixed point let (total_staked_fp_medium, _) = u512_safe_div_rem_by_u256( @@ -389,14 +389,14 @@ pub mod Staker { assert(total_staked_fp.high < MAX_FP, 'FP_OVERFLOW'); // round value - total_staked_fp.high + if (total_staked_fp.low >= HALF) { + total_staked_fp.high + if (total_staked_fp.low >= TWO_POW_127) { 1 } else { 0 } }; - let seconds_diff = (timestamp - log_record.timestamp) / 1000; + let seconds_diff = timestamp_seconds - log_record.timestamp; let staked_seconds: u256 = if total_staked == 0 { 0_u256 diff --git a/src/staker_log.cairo b/src/staker_log.cairo index d963719..7c857c6 100644 --- a/src/staker_log.cairo +++ b/src/staker_log.cairo @@ -22,7 +22,6 @@ pub(crate) struct StakingLogRecord { pub(crate) timestamp: u64, // Only 128+32=160 bits are used - // TODO: add validation checks pub(crate) cumulative_total_staked: u256, pub(crate) cumulative_seconds_per_total_staked: u256, } @@ -68,10 +67,12 @@ pub impl StakingLogOperations of LogOperations { fn log_change(self: StorageBase>, amount: u128, total_staked: u128) { let log = self.as_path(); + let block_timestamp = get_block_timestamp() / 1000; + if log.len() == 0 { log.append().write( StakingLogRecord { - timestamp: get_block_timestamp(), + timestamp: block_timestamp, cumulative_total_staked: 0_u256, cumulative_seconds_per_total_staked: 0_u64.into(), } @@ -84,7 +85,7 @@ pub impl StakingLogOperations of LogOperations { let mut last_record = last_record_ptr.read(); - let mut record = if last_record.timestamp == get_block_timestamp() { + let mut record = if last_record.timestamp == block_timestamp { // update record last_record_ptr } else { @@ -93,7 +94,7 @@ pub impl StakingLogOperations of LogOperations { }; // Might be zero - let seconds_diff = (get_block_timestamp() - last_record.timestamp) / 1000; + let seconds_diff = block_timestamp - last_record.timestamp; let total_staked_by_elapsed_seconds = total_staked.into() * seconds_diff.into(); @@ -108,7 +109,7 @@ pub impl StakingLogOperations of LogOperations { // Add a new record. record.write( StakingLogRecord { - timestamp: get_block_timestamp(), + timestamp: block_timestamp, cumulative_total_staked: last_record.cumulative_total_staked + total_staked_by_elapsed_seconds, diff --git a/src/staker_test.cairo b/src/staker_test.cairo index 3d55eea..3b9e597 100644 --- a/src/staker_test.cairo +++ b/src/staker_test.cairo @@ -411,8 +411,9 @@ mod staker_staked_seconds_per_total_staked_calculation { #[test] fn test_should_return_0_if_no_data_found() { let (staker, _) = setup(10000); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(0), 0, 0_u128.into()); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(1000), 0, 0_u128.into()); + + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(0), u256{ high: 0, low: 0_u128.into()}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(1000), u256{ high: 0, low: 0_u128.into()}); } #[test] @@ -441,11 +442,6 @@ mod staker_staked_seconds_per_total_staked_calculation { staker.withdraw_amount(delegatee, token_owner, 2000); } - fn assert_fp(value: u256, integer: u128, fractional: u128) { - assert_eq!(value.high, integer); - assert_eq!(value.low, fractional); - } - #[test] fn test_should_stake_10000_tokens_for_5_seconds_adding_10000_every_second_to_staked_seconds() { let (staker, token) = setup(1000); @@ -474,23 +470,20 @@ mod staker_staked_seconds_per_total_staked_calculation { token.approve(staker.contract_address, 7); staker.stake(delegatee); // Will transfer 7 token to contract account and setup delegatee - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(0), 0, 0_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(500), 0, 0_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(999), 0, 0_u128); - - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(1000), 0, 0x80000000000000000000000000000000_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(2000), 1, 0_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(3000), 1, 0x80000000000000000000000000000000_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(4000), 2, 0_u128); - - // here value is undefined as nothing was staked. - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(5000), 0, 0_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(6000), 0, 0_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(7000), 0, 0_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(8000), 0, 0_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(9000), 0, 0_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(10000), 2, 0x80000000000000000000000000000000_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(17000), 3, 0x80000000000000000000000000000000_u128); - assert_fp(staker.get_cumulative_seconds_per_total_staked_at(24000), 4, 0x80000000000000000000000000000000_u128); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(0), u256 {high: 0, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(500), u256 {high: 0, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(999), u256 {high: 0, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(1000), u256 {high: 0, low: 0x80000000000000000000000000000000_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(2000), u256 {high: 1, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(3000), u256 {high: 1, low: 0x80000000000000000000000000000000_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(4000), u256 {high: 2, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(5000), u256 {high: 0, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(6000), u256 {high: 0, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(7000), u256 {high: 0, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(8000), u256 {high: 0, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(9000), u256 {high: 0, low: 0_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(10000), u256 {high: 2, low: 0x80000000000000000000000000000000_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(17000), u256 {high: 3, low: 0x80000000000000000000000000000000_u128}); + assert_eq!(staker.get_cumulative_seconds_per_total_staked_at(24000), u256 {high: 4, low: 0x80000000000000000000000000000000_u128}); } } \ No newline at end of file