Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add per tranche info for lockups #208

Merged
merged 10 commits into from
Dec 18, 2024
2 changes: 2 additions & 0 deletions .changelog/unreleased/features/208-add-per-tranche-info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Add information about when lockups can vote again to the `per_tranche_info` endpoint.
([\#208](https://github.com/informalsystems/hydro/pull/208))
74 changes: 67 additions & 7 deletions contracts/hydro/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ use crate::lsm_integration::{
use crate::msg::{ExecuteMsg, InstantiateMsg, LiquidityDeployment, ProposalToLockups, TrancheInfo};
use crate::query::{
AllUserLockupsResponse, ConstantsResponse, CurrentRoundResponse, ExpiredUserLockupsResponse,
ICQManagersResponse, LiquidityDeploymentResponse, LockEntryWithPower, ProposalResponse,
QueryMsg, RegisteredValidatorQueriesResponse, RoundEndResponse, RoundProposalsResponse,
RoundTotalVotingPowerResponse, RoundTrancheLiquidityDeploymentsResponse, TopNProposalsResponse,
TotalLockedTokensResponse, TranchesResponse, UserVotesResponse, UserVotingPowerResponse,
ValidatorPowerRatioResponse, WhitelistAdminsResponse, WhitelistResponse,
ICQManagersResponse, LiquidityDeploymentResponse, LockEntryWithPower, LockupWithPerTrancheInfo,
PerTrancheLockupInfo, ProposalResponse, QueryMsg, RegisteredValidatorQueriesResponse,
RoundEndResponse, RoundProposalsResponse, RoundTotalVotingPowerResponse,
RoundTrancheLiquidityDeploymentsResponse, TopNProposalsResponse, TotalLockedTokensResponse,
TranchesResponse, UserVotesResponse, UserVotingPowerResponse, ValidatorPowerRatioResponse,
WhitelistAdminsResponse, WhitelistResponse,
};
use crate::score_keeper::{
add_validator_shares_to_proposal, get_total_power_for_proposal,
Expand Down Expand Up @@ -1681,12 +1682,19 @@ pub fn query_all_user_lockups(
limit,
);

let converted_addr = deps.api.addr_validate(&address)?;

let tranche_ids = TRANCHE_MAP
.range(deps.storage, None, None, Order::Ascending)
.map(|tranche| tranche.unwrap().1.id)
.collect::<Vec<u64>>();

let constants = CONSTANTS.load(deps.storage)?;
let current_round_id = compute_current_round_id(&env, &constants)?;
let round_end = compute_round_end(&constants, current_round_id)?;

// enrich the lockups by computing the voting power for each lockup
let enriched_lockups = raw_lockups
let locks_with_power: Vec<LockEntryWithPower> = raw_lockups
.iter()
.map(|lock| {
to_lockup_with_power(
Expand All @@ -1700,8 +1708,60 @@ pub fn query_all_user_lockups(
})
.collect();

// enrich lockups with some info per tranche
let lockups_with_per_tranche_info: Vec<LockupWithPerTrancheInfo> = locks_with_power
.iter()
.map(|lock| {
// iterate all tranches
let tranche_infos = tranche_ids
.iter()
.map(|tranche_id| {
// add which proposal the lock voted for
let voted_for_proposal: Option<u64> = VOTE_MAP
.may_load(
deps.storage,
(
(current_round_id, *tranche_id),
converted_addr.clone(),
lock.lock_entry.lock_id,
),
)
.unwrap_or(None)
.map(|vote| vote.prop_id);

let next_round_voting_allowed: u64 = if voted_for_proposal.is_some() {
// if the proposal voted in this round, we ignore the VOTING_ALLOWED_ROUND map,
// since it just contains the future information on when the lockup will be able to vote again
// if it doesn't change the vote, but it can vote in the current round either way
current_round_id
} else {
// if the lockup has not voted in this round, VOTING_ALLOWED_ROUND does contain
// current information on whether the votup can vote right now or not
p-offtermatt marked this conversation as resolved.
Show resolved Hide resolved
VOTING_ALLOWED_ROUND
.may_load(deps.storage, (*tranche_id, lock.lock_entry.lock_id))
.unwrap_or_default()
p-offtermatt marked this conversation as resolved.
Show resolved Hide resolved
.unwrap_or(0)
p-offtermatt marked this conversation as resolved.
Show resolved Hide resolved
};

// return the info for this tranche
PerTrancheLockupInfo {
tranche_id: *tranche_id,
next_round_lockup_can_vote: next_round_voting_allowed,
current_voted_on_proposal: voted_for_proposal,
p-offtermatt marked this conversation as resolved.
Show resolved Hide resolved
}
})
.collect::<Vec<PerTrancheLockupInfo>>();
// combine the info for all tranches
LockupWithPerTrancheInfo {
lock_with_power: lock.clone(),
per_tranche_info: tranche_infos,
}
})
.collect();

// return the enriched lockups
Ok(AllUserLockupsResponse {
lockups: enriched_lockups,
lockups: lockups_with_per_tranche_info,
})
}

Expand Down
18 changes: 17 additions & 1 deletion contracts/hydro/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,25 @@ pub struct LockEntryWithPower {
pub current_voting_power: Uint128,
}

// PerTrancheLockupInfo is used to store the lockup information for a specific tranche.
#[cw_serde]
pub struct PerTrancheLockupInfo {
pub tranche_id: u64,
pub next_round_lockup_can_vote: u64,
pub current_voted_on_proposal: Option<u64>,
}

// LockupWithPerTrancheInfo is used to store the lockup information for a specific lockup,
// together with lockup-specific information for each tranche.
#[cw_serde]
pub struct LockupWithPerTrancheInfo {
pub lock_with_power: LockEntryWithPower,
pub per_tranche_info: Vec<PerTrancheLockupInfo>,
}

#[cw_serde]
pub struct AllUserLockupsResponse {
pub lockups: Vec<LockEntryWithPower>,
pub lockups: Vec<LockupWithPerTrancheInfo>,
}

#[cw_serde]
Expand Down
31 changes: 19 additions & 12 deletions contracts/hydro/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,37 +212,43 @@ fn lock_tokens_basic_test() {

let lockup = &res.lockups[0];
// check that the id is 0
assert_eq!(0, lockup.lock_entry.lock_id);
assert_eq!(0, lockup.lock_with_power.lock_entry.lock_id);
assert_eq!(
info1.funds[0].amount.u128(),
lockup.lock_entry.funds.amount.u128()
lockup.lock_with_power.lock_entry.funds.amount.u128()
);
assert_eq!(info1.funds[0].denom, lockup.lock_entry.funds.denom);
assert_eq!(env.block.time, lockup.lock_entry.lock_start);
assert_eq!(
info1.funds[0].denom,
lockup.lock_with_power.lock_entry.funds.denom
);
assert_eq!(env.block.time, lockup.lock_with_power.lock_entry.lock_start);
assert_eq!(
env.block.time.plus_nanos(ONE_MONTH_IN_NANO_SECONDS),
lockup.lock_entry.lock_end
lockup.lock_with_power.lock_entry.lock_end
);
// check that the power is correct: 1000 tokens locked for one epoch
// so power is 1000 * 1
assert_eq!(1000, lockup.current_voting_power.u128());
assert_eq!(1000, lockup.lock_with_power.current_voting_power.u128());

let lockup = &res.lockups[1];
// check that the id is 1
assert_eq!(1, lockup.lock_entry.lock_id);
assert_eq!(1, lockup.lock_with_power.lock_entry.lock_id);
assert_eq!(
info2.funds[0].amount.u128(),
lockup.lock_entry.funds.amount.u128()
lockup.lock_with_power.lock_entry.funds.amount.u128()
);
assert_eq!(
info2.funds[0].denom,
lockup.lock_with_power.lock_entry.funds.denom
);
assert_eq!(info2.funds[0].denom, lockup.lock_entry.funds.denom);
assert_eq!(env.block.time, lockup.lock_entry.lock_start);
assert_eq!(env.block.time, lockup.lock_with_power.lock_entry.lock_start);
assert_eq!(
env.block.time.plus_nanos(THREE_MONTHS_IN_NANO_SECONDS),
lockup.lock_entry.lock_end
lockup.lock_with_power.lock_entry.lock_end
);
// check that the power is correct: 3000 tokens locked for three epochs
// so power is 3000 * 1.5 = 4500
assert_eq!(4500, lockup.current_voting_power.u128());
assert_eq!(4500, lockup.lock_with_power.current_voting_power.u128());
}

#[test]
Expand Down Expand Up @@ -2772,6 +2778,7 @@ fn test_refresh_multiple_locks() {
for (i, &expected_duration) in expected_durations.iter().enumerate() {
let expected_nanos = expected_duration * ONE_MONTH_IN_NANO_SECONDS;
let remaining_lock_duration = lockups[i]
.lock_with_power
.lock_entry
.lock_end
.minus_nanos(env.block.time.nanos());
Expand Down
Loading
Loading