FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Feat staker v2 no fp and subpointers by baitcode · Pull Request #67 · EkuboProtocol/governance · GitHub

Feat staker v2 no fp and subpointers - #67

Merged
moodysalem merged 48 commits into
EkuboProtocol:mainfrom
baitcode:feat-staker-v2-no-fp-and-subpointers
Feb 19, 2025
Merged

moodysalem merged 48 commits into
EkuboProtocol:mainfrom
baitcode:feat-staker-v2-no-fp-and-subpointers

Conversation

baitcode commented Jan 10, 2025
edited
Loading

Copy link
Copy Markdown
Contributor
  • Added staker_log to track cumulatives depending on total staking amount changes.
  • Added calculation of total_staked amount at any given timestamp
  • Added calculation for seconds_per_total_staked_sum at any given timestamp ∑(Δt / total_staked)
  • Added calculation for time_weighted_total_staked at any given timestamp ∑(Δt * total_staked)
  • Added calculation for average total_staked over period of time (using seconds_per_total_staked_sum)
  • Added calculation for get_user_share_of_total_staked_over_period (using time_weighted_total_staked)
  • Changed storage for staker:
        delegated_cumulative_num_snapshots: Map<ContractAddress, u64>,
        delegated_cumulative_snapshot: Map<ContractAddress, Map<u64, DelegatedSnapshot>>,

for

        delegated_cumulative_snapshot: Map<ContractAddress, Vec<DelegatedSnapshot>>,

Closes #64 and #61

…support Q128.128 FP.

Added cumulative seconds per total staked history value, and getter for that value
Added method that allows to calculate how much staked seconds would staked token amount generate
in between 2 timestamps: `calculate_staked_seconds_for_amount_between`
Added tiny amount of tests
* removed staked seconds calculation
* added seconds per total staked test and added logic for calculation of that value when nothing is staked
…ilerplate.

Implemented custom packing and custom data SubPointers.
…ize.

Added convienience method for u64 / u128 case.
finished subpointers for staking_log. Subpointer always read whole felt252 from memory and converts it. So I had to change approach for packing.
inline everything remove storage read optimisations
Comment thread src/staker.cairo Outdated
const TWO_POW_64: u128 = 0x10000000000000000;
const TWO_POW_192: u256 = 0x1000000000000000000000000000000000000000000000000;
const TWO_POW_192_DIVISOR: NonZero<u256> = 0x1000000000000000000000000000000000000000000000000;
const HALF: u128 = 0x80000000000000000000000000000000_u128;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

TWO_POW_127 is more descriptive

Comment thread src/staker.cairo Outdated
.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');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

is this supposed to be an overflow check? this cannot happen ever because addition is checked by default

Comment thread src/staker_test.cairo Outdated
Comment on lines +445 to +446
assert_eq!(value.high, integer);
assert_eq!(value.low, fractional);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

think it would be more readable if you just wrote assert_eq!(value, u256 {high: integer, low:fractional}) and also inlined that into the below test

Comment thread src/staker_log.cairo Outdated
pub(crate) timestamp: u64,

// Only 128+32=160 bits are used
// TODO: add validation checks

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

wdym?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Oh. I've already did that. Forgot to remove. Thank you for catching.

Comment thread src/staker_log.cairo Outdated
const TWO_POW_32: u64 = 0x100000000_u64;
const MASK_32_BITS: u128 = 0x100000000_u128 - 1;
const TWO_POW_160: u256 = 0x10000000000000000000000000000000000000000;
pub const MAX_FP: u128 = 0x8000000000000110000000000000000_u128;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

what is this value?

baitcode Jan 13, 2025
edited
Loading

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

maximum value that can be store in felt252 is MAX_V = 0x800000000000011000000000000000000000000000000000000000000000000.
MAX_FP = MAX_V >> 128 (maximum value of highest 123 bit of felt252).

Comment thread src/staker.cairo Outdated
}
};

let seconds_diff = (timestamp - log_record.timestamp) / 1000;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

block timestamps are in seconds arent they? why are we dividing by 1k

baitcode Jan 13, 2025
edited
Loading

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This is absolutely right. I can't believe tests did not catch it. Aaaa found it. Turned out I saved ms in record. Thank you for catching this. Need to add think on how to test storage.

* uniform get_block_timestamp usage
* store log_record timestamp in seconds
* inline test assert for fp value
* rename HALF to TWO_POW_127
Comment thread src/staker.cairo Outdated
fn get_cumulative_seconds_per_total_staked_at(
self: @ContractState, timestamp: u64,
) -> u256 {
let timestamp_seconds = timestamp / 1000;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

timestamp should not be expressed in milliseconds when everything onchain is in seconds

because of this division, there is no difference between e.g. 4000 and 4999

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Oh my! I though on-chain everything is in ms.

Comment thread src/staker.cairo Outdated
// substract fixed point values
let divisor = next_log_record.cumulative_seconds_per_total_staked
- log_record.cumulative_seconds_per_total_staked;
assert(divisor.high < MAX_FP, 'FP_OVERFLOW');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

can this actually happen?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Unfortunately, in my practice things tend to happen. Assertions are also a good way of documenting code. But I see your point. Will remove.

moodysalem mentioned this pull request Jan 20, 2025
Comment thread src/staker.cairo Outdated
Comment on lines +268 to +269
self.total_staked.write(total_staked + amount);
self.staking_log.log_change(amount, total_staked);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

is it required to write total staked before calling log_change? if so, it's not locally obvious from the function names and arguments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

it is not. I'll think on how to rename those.

Comment thread src/staker.cairo Outdated
) -> u256 {
if let Option::Some((log_record, idx)) = self
.staking_log
.find_in_change_log(timestamp) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

it's not finding that specific timestamp, maybe rename to find_previous.. or something

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Good point.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Changed to find_change_log_on_or_before_timestamp.

Comment thread src/staker.cairo Outdated
- log_record.cumulative_seconds_per_total_staked;

if divisor.is_zero() {
return 0_u64.into();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

in hindsight, we should actually treat total staked of 0 as if it's 1 with regards to this accumulator

that allows us to still compute the harmonic mean for periods where total staked is zero (it will not be perfectly accurate, but ok for it to be off by one)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

it also means you don't have to worry about this value being 0 between two indices

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I refactored that bit. I calculate total_staked using total_staked * seconds passed. It's much simpler.

Comment thread src/staker.cairo Outdated

let seconds_diff = timestamp - log_record.timestamp;

let staked_seconds: u256 = if total_staked == 0 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

variable name is confusing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

changed to seconds_per_total_staked

Comment thread src/staker.cairo Outdated
let seconds_diff = timestamp - log_record.timestamp;

let staked_seconds: u256 = if total_staked == 0 {
0_u256

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

here you can just use the value 1 for total_staked

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@moodysalem
Do I understand you correctly, that this is a technic to cap the infinity? meaning that we probably wont really calculate stake shares over periods like that and those will be negated when snapshots are subtracted?

Comment thread src/staker.cairo Outdated

// Sum fixed posits
let result = log_record.cumulative_seconds_per_total_staked + staked_seconds;
assert(result.high < MAX_FP, 'FP_OVERFLOW');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

why are you doing this assertion? if it's because you're storing a u256 as a felt252 in the store packing, then it should be asserted where you're actually doing the storage

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Had same though, but decided to fail as early as possible.

baitcode Jan 23, 2025
edited
Loading

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Looks like in storage packing it'll fail anyway during conversion. Removing.

Comment thread src/staker.cairo Outdated
self: @TContractState, delegate: ContractAddress, period: u64,
) -> u128;

// Gets the cumulative staked amount * per second staked for the given timestamp and account.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

is this comment accurate? there were 2 diff quantities we wanted to be able to compute:

average total staked for a period of time

  • requires storing seconds passed * total_staked, a 160+ bit wide value
  • can be computed by taking (snapshotB-snapshotA) / periodLength
  • can be used to calculate the total voting weight at the time a proposal's voting starts, so functionality could be added for proposal to be immediately passed if it reaches quorum with majority of total staked in favor

user's share of total stake over a period

  • requires storing seconds passed / total staked, a 160+ bit wide value
  • computed via stakedAmount * snapshotStakeEnd - snapshotStakeStart
  • this also lets you compute the harmonic mean of total staked over a period, which could be useful for dynamically adjusting quorum

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

No it's not. It's outdated. I've updated it. This method calculates shapshots for "user's share of total stake over a period"

Fixed. That one. Will add other snapshots soon. Then add methods for 2 diff quantities.

baitcode Jan 31, 2025
edited
Loading

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@moodysalem

computed via stakedAmount * snapshotStakeEnd - snapshotStakeStart

Formula for user's share seems incorrect:

  1. the dimension of the result is seconds. which raises suspicion.
  2. brackets are missing.

I think that correct one is: stakedAmount * (snapshotStakeEnd - snapshotStakeStart) / (snapshotTimestampEnd-snapshotTimestampStart)
Can you please confirm that this is correct?

* find_in_change_log -> find_change_log_on_or_before_timestamp
* log_change argument rename
added get_time_weighted_total_staked_sum_at
added get_total_staked_at to staker
added get_user_share_of_total_staked_over_period to staker
added get_average_total_staked_over_period to staker
baitcode requested a review from moodysalem January 31, 2025 01:49

Copy link
Copy Markdown
Contributor Author

@moodysalem I decided not to wait your replies and implemented. There are tests missing for
get_average_total_staked_over_period
get_user_share_of_total_staked_over_period
I'll add a bit later.

Copy link
Copy Markdown
Contributor Author

@moodysalem
Should user share be fixed point value?

baitcode and others added 8 commits February 17, 2025 21:34
- reverted changes to staker
- moved changes to staker_v2
- changed delegated_cumulative_snapshot to Map<ContractAddress, Vec<DelegatedSnapshot>>
moodysalem merged commit eac52cc into EkuboProtocol:main Feb 19, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

change map to vec for staker delegated amount snapshots

2 participants


Back | FazBrowse Home | New Git URL