Skip to content

Commit

Permalink
feat(vxASTRO): remove max pool limit; vote once per epoch; privileged…
Browse files Browse the repository at this point in the history
… instant unlock (#111)

* remove max pools per vote limitation on the hub

* feat(emissions controller): allow to vote once per epoch

Aligned voting with vxASTRO epochs.
Removed separate per-user voting cooldown of 10 days

* Fix build script. See astroport-fi/astroport-core#428

* add emissions controller migration

* align docs with new changes

* feat(vxastro): add privileged feature to allow unlocking vxASTRO instantly

* fix instant unlocking logic
  • Loading branch information
epanchee authored Sep 13, 2024
1 parent febfe3d commit bb07861
Show file tree
Hide file tree
Showing 29 changed files with 542 additions and 76 deletions.
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified assets/emissions_controller_general.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion contracts/emissions_controller/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "astroport-emissions-controller"
version = "1.0.0"
version = "1.0.1"
authors = ["Astroport"]
edition = "2021"
description = "Astroport vxASTRO Emissions Voting Contract"
Expand Down
3 changes: 2 additions & 1 deletion contracts/emissions_controller/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ If the pool is located on the Hub contract also checks, this LP token correspond
## Voting

Users are required to have vxASTRO to cast their votes.
They can vote for up to five whitelisted pools at once every 10 days.
They can vote for whitelisted pools at once every epoch.
Vote changes are not allowed after votes are cast.
After voting, they can't change their votes until the cooldown period ends.
Executable message accepts an array of tuples with LP token and vote weight.
Vote weight is a number between 0 and 1. Total vote weight can't exceed 1.
Expand Down
8 changes: 3 additions & 5 deletions contracts/emissions_controller/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ use cw_utils::{ParseReplyError, PaymentError};
use neutron_sdk::NeutronError;
use thiserror::Error;

use astroport_governance::emissions_controller::consts::MAX_POOLS_TO_VOTE;

/// This enum describes contract errors
#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
Expand Down Expand Up @@ -65,9 +63,6 @@ pub enum ContractError {
#[error("Failed to determine outpost for pool {0}")]
NoOutpostForPool(String),

#[error("You can vote maximum for {MAX_POOLS_TO_VOTE} pools")]
ExceededMaxPoolsToVote {},

#[error("Message contains duplicated pools")]
DuplicatedVotes {},

Expand All @@ -79,4 +74,7 @@ pub enum ContractError {

#[error("Can't set zero emissions for astro pool")]
ZeroAstroEmissions {},

#[error("Failed to migrate contract")]
MigrationError {},
}
18 changes: 7 additions & 11 deletions contracts/emissions_controller/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ use itertools::Itertools;
use neutron_sdk::bindings::msg::NeutronMsg;
use neutron_sdk::bindings::query::NeutronQuery;

use astroport_governance::emissions_controller::consts::{
EPOCH_LENGTH, IBC_TIMEOUT, MAX_POOLS_TO_VOTE, VOTE_COOLDOWN,
};
use astroport_governance::emissions_controller::consts::{EPOCH_LENGTH, IBC_TIMEOUT};
use astroport_governance::emissions_controller::hub::{
AstroPoolConfig, HubMsg, OutpostInfo, OutpostParams, OutpostStatus, TuneInfo, UserInfo,
VotedPoolInfo,
Expand Down Expand Up @@ -53,10 +51,6 @@ pub fn execute(
votes.len() == votes_map.len(),
ContractError::DuplicatedVotes {}
);
ensure!(
votes_map.len() <= MAX_POOLS_TO_VOTE,
ContractError::ExceededMaxPoolsToVote {}
);
let deps = deps.into_empty();
let config = CONFIG.load(deps.storage)?;
let voting_power = get_voting_power(deps.querier, &config.vxastro, &info.sender, None)?;
Expand Down Expand Up @@ -415,7 +409,7 @@ pub fn retry_failed_outposts(
}

/// The function checks that:
/// * user didn't vote for the last 10 days,
/// * user didn't vote at the current epoch,
/// * sum of all percentage values <= 1.
/// User can direct his voting power partially.
///
Expand All @@ -434,10 +428,12 @@ pub fn handle_vote(
) -> Result<Response<NeutronMsg>, ContractError> {
let user_info = USER_INFO.may_load(deps.storage, voter)?.unwrap_or_default();
let block_ts = env.block.time.seconds();
// Is the user eligible to vote again?

let epoch_start = get_epoch_start(block_ts);
// User can vote once per epoch
ensure!(
user_info.vote_ts + VOTE_COOLDOWN <= block_ts,
ContractError::VoteCooldown(user_info.vote_ts + VOTE_COOLDOWN)
user_info.vote_ts < epoch_start,
ContractError::VoteCooldown(epoch_start + EPOCH_LENGTH)
);

let mut total_weight = Decimal::zero();
Expand Down
9 changes: 6 additions & 3 deletions contracts/emissions_controller/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,10 @@ mod unit_tests {
POOLS_WHITELIST
.save(deps.as_mut().storage, &vec!["osmo1pool1".to_string()])
.unwrap();
let env = mock_env();

let mut env = mock_env();
env.block.time = Timestamp::from_seconds(1724922008);

VOTED_POOLS
.save(
deps.as_mut().storage,
Expand All @@ -404,12 +407,12 @@ mod unit_tests {
let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
assert_eq!(ack_err, IbcAckResult::Ok(b"ok".into()));

// The same user has voting cooldown for 10 days
// The same user can only vote at the next epoch
let resp = ibc_packet_receive(deps.as_mut().into_empty(), env.clone(), ibc_msg).unwrap();
let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
assert_eq!(
ack_err,
IbcAckResult::Error("Next time you can change your vote is at 1572661419".to_string())
IbcAckResult::Error("Next time you can change your vote is at 1725840000".to_string())
);

// Voting from random channel is not possible
Expand Down
1 change: 1 addition & 0 deletions contracts/emissions_controller/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod state;
pub mod error;
pub mod ibc;
pub mod instantiate;
pub mod migration;
pub mod query;
pub mod sudo;
pub mod utils;
28 changes: 28 additions & 0 deletions contracts/emissions_controller/src/migration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#![cfg(not(tarpaulin_include))]

use cosmwasm_std::{entry_point, DepsMut, Empty, Env, Response};
use cw2::{get_contract_version, set_contract_version};

use crate::error::ContractError;
use crate::instantiate::{CONTRACT_NAME, CONTRACT_VERSION};

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result<Response, ContractError> {
let contract_version = get_contract_version(deps.storage)?;

match contract_version.contract.as_ref() {
CONTRACT_NAME => match contract_version.version.as_ref() {
"1.0.0" => Ok(()),
_ => Err(ContractError::MigrationError {}),
},
_ => Err(ContractError::MigrationError {}),
}?;

set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

Ok(Response::new()
.add_attribute("previous_contract_name", &contract_version.contract)
.add_attribute("previous_contract_version", &contract_version.version)
.add_attribute("new_contract_name", CONTRACT_NAME)
.add_attribute("new_contract_version", CONTRACT_VERSION))
}
31 changes: 31 additions & 0 deletions contracts/emissions_controller/tests/common/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,30 @@ impl ControllerHelper {
)
}

pub fn instant_unlock(&mut self, user: &Addr, amount: u128) -> AnyResult<AppResponse> {
self.app.execute_contract(
user.clone(),
self.vxastro.clone(),
&voting_escrow::ExecuteMsg::InstantUnlock {
amount: amount.into(),
},
&[],
)
}

pub fn set_privileged_list(
&mut self,
sender: &Addr,
list: Vec<String>,
) -> AnyResult<AppResponse> {
self.app.execute_contract(
sender.clone(),
self.vxastro.clone(),
&voting_escrow::ExecuteMsg::SetPrivilegedList { list },
&[],
)
}

pub fn relock(&mut self, user: &Addr) -> AnyResult<AppResponse> {
self.app.execute_contract(
user.clone(),
Expand Down Expand Up @@ -411,6 +435,13 @@ impl ControllerHelper {
)
}

pub fn total_vp(&self, timestamp: Option<u64>) -> StdResult<Uint128> {
self.app.wrap().query_wasm_smart(
&self.vxastro,
&voting_escrow::QueryMsg::TotalVotingPower { timestamp },
)
}

pub fn user_info(&self, user: &Addr, timestamp: Option<u64>) -> StdResult<UserInfoResponse> {
self.app.wrap().query_wasm_smart(
&self.emission_controller,
Expand Down
Loading

0 comments on commit bb07861

Please sign in to comment.