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

Feat/add unbonding time #36

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
61 changes: 54 additions & 7 deletions contracts/oraiswap_staking/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ use crate::rewards::{
use crate::staking::{auto_stake, auto_stake_hook, bond, unbond};
use crate::state::{
read_all_pool_infos, read_config, read_finish_migrate_store_status, read_pool_info,
read_rewards_per_sec, remove_pool_info, stakers_read, store_config,
store_finish_migrate_store_status, store_pool_info, store_rewards_per_sec, Config,
MigrationParams, PoolInfo,
read_rewards_per_sec, read_user_lock_info, remove_pool_info, stakers_read, store_config,
store_finish_migrate_store_status, store_pool_info, store_rewards_per_sec,
store_unbonding_period, Config, MigrationParams, PoolInfo,
};

use cosmwasm_std::{
Expand All @@ -24,8 +24,9 @@ use cosmwasm_std::{
};
use oraiswap::asset::{Asset, AssetRaw, ORAI_DENOM};
use oraiswap::staking::{
ConfigResponse, Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, OldStoreType,
PoolInfoResponse, QueryMsg, QueryPoolInfoResponse, RewardsPerSecResponse,
ConfigResponse, Cw20HookMsg, ExecuteMsg, InstantiateMsg, LockInfoResponse, LockInfosResponse,
MigrateMsg, OldStoreType, PoolInfoResponse, QueryMsg, QueryPoolInfoResponse,
RewardsPerSecResponse,
};

use cw20::Cw20ReceiveMsg;
Expand Down Expand Up @@ -70,7 +71,10 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S
assets,
} => update_rewards_per_sec(deps, info, staking_token, assets),
ExecuteMsg::DepositReward { rewards } => deposit_reward(deps, info, rewards),
ExecuteMsg::RegisterAsset { staking_token } => register_asset(deps, info, staking_token),
ExecuteMsg::RegisterAsset {
staking_token,
unbonding_period,
} => register_asset(deps, info, staking_token, unbonding_period),
ExecuteMsg::DeprecateStakingToken {
staking_token,
new_staking_token,
Expand Down Expand Up @@ -217,7 +221,12 @@ fn update_rewards_per_sec(
Ok(Response::new().add_attribute("action", "update_rewards_per_sec"))
}

fn register_asset(deps: DepsMut, info: MessageInfo, staking_token: Addr) -> StdResult<Response> {
fn register_asset(
deps: DepsMut,
info: MessageInfo,
staking_token: Addr,
unbonding_period: Option<u64>,
) -> StdResult<Response> {
validate_migrate_store_status(deps.storage)?;
let config: Config = read_config(deps.storage)?;

Expand All @@ -243,9 +252,19 @@ fn register_asset(deps: DepsMut, info: MessageInfo, staking_token: Addr) -> StdR
},
)?;

if let Some(unbonding_period) = unbonding_period {
if unbonding_period > 0 {
store_unbonding_period(deps.storage, staking_token.as_bytes(), unbonding_period)?;
}
}

Ok(Response::new().add_attributes([
("action", "register_asset"),
("staking_token", staking_token.as_str()),
(
"unbonding_period",
&unbonding_period.unwrap_or(0).to_string(),
),
]))
}

Expand Down Expand Up @@ -324,10 +343,38 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
order,
)?),
QueryMsg::GetPoolsInformation {} => to_binary(&query_get_pools_infomation(deps)?),
QueryMsg::LockInfos {
staker_addr,
staking_token,
} => to_binary(&query_lock_infos(deps, _env, staker_addr, staking_token)?),
QueryMsg::QueryOldStore { store_type } => query_old_store(deps, store_type),
}
}

pub fn query_lock_infos(
deps: Deps,
_env: Env,
staker_addr: Addr,
staking_token: Addr,
) -> StdResult<LockInfosResponse> {
let lock_infos = read_user_lock_info(
deps.storage,
staking_token.as_bytes(),
staker_addr.as_bytes(),
)?;
Ok(LockInfosResponse {
staker_addr,
staking_token,
lock_infos: lock_infos
.into_iter()
.map(|lock| LockInfoResponse {
amount: lock.amount,
unlock_time: lock.unlock_time.seconds(),
})
.collect(),
})
}

pub fn query_config(deps: Deps) -> StdResult<ConfigResponse> {
let state = read_config(deps.storage)?;
let resp = ConfigResponse {
Expand Down
5 changes: 1 addition & 4 deletions contracts/oraiswap_staking/src/rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::convert::TryFrom;
use crate::contract::validate_migrate_store_status;
use crate::state::{
read_config, read_is_migrated, read_pool_info, read_rewards_per_sec, rewards_read,
rewards_store, stakers_read, store_pool_info, PoolInfo, RewardInfo,
rewards_store, stakers_read, store_pool_info, PoolInfo, RewardInfo, DEFAULT_LIMIT, MAX_LIMIT,
};
use cosmwasm_std::{
Addr, Api, CanonicalAddr, CosmosMsg, Decimal, Deps, DepsMut, Env, MessageInfo, Order, Response,
Expand All @@ -13,9 +13,6 @@ use oraiswap::asset::{Asset, AssetRaw};
use oraiswap::querier::calc_range_start;
use oraiswap::staking::{RewardInfoResponse, RewardInfoResponseItem, RewardMsg};

const DEFAULT_LIMIT: u32 = 10;
const MAX_LIMIT: u32 = 30;

// deposit_reward must be from reward token contract
pub fn deposit_reward(
deps: DepsMut,
Expand Down
136 changes: 106 additions & 30 deletions contracts/oraiswap_staking/src/staking.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::contract::validate_migrate_store_status;
use crate::rewards::before_share_change;
use crate::state::{
read_config, read_is_migrated, read_pool_info, rewards_read, rewards_store, stakers_store,
store_is_migrated, store_pool_info, Config, PoolInfo, RewardInfo,
insert_lock_info, read_config, read_is_migrated, read_pool_info, read_unbonding_period,
remove_and_accumulate_lock_info, rewards_read, rewards_store, stakers_store, store_is_migrated,
store_pool_info, Config, PoolInfo, RewardInfo,
};
use cosmwasm_std::{
attr, to_binary, Addr, Api, CanonicalAddr, Coin, CosmosMsg, Decimal, DepsMut, Env, MessageInfo,
Expand All @@ -12,7 +13,7 @@ use cw20::Cw20ExecuteMsg;
use oraiswap::asset::{Asset, AssetInfo, PairInfo};
use oraiswap::pair::ExecuteMsg as PairExecuteMsg;
use oraiswap::querier::{query_pair_info, query_token_balance};
use oraiswap::staking::ExecuteMsg;
use oraiswap::staking::{ExecuteMsg, LockInfo};

pub fn bond(
deps: DepsMut,
Expand All @@ -39,46 +40,83 @@ pub fn bond(

pub fn unbond(
deps: DepsMut,
_env: Env,
env: Env,
staker_addr: Addr,
staking_token: Addr,
amount: Uint128,
) -> StdResult<Response> {
validate_migrate_store_status(deps.storage)?;
let staker_addr_raw: CanonicalAddr = deps.api.addr_canonicalize(staker_addr.as_str())?;
let (staking_token, reward_assets) = _decrease_bond_amount(

let (staking_token_canonicalize, reward_assets) = _decrease_bond_amount(
deps.storage,
deps.api,
&staker_addr_raw,
&staking_token,
amount,
)?;

let staking_token_addr = deps.api.addr_humanize(&staking_token)?;
let mut messages = vec![WasmMsg::Execute {
contract_addr: staking_token_addr.to_string(),
msg: to_binary(&Cw20ExecuteMsg::Transfer {
recipient: staker_addr.to_string(),
amount,
})?,
funds: vec![],
}
.into()];
let staking_token_addr = deps.api.addr_humanize(&staking_token_canonicalize)?;

let mut messages = vec![];
// withdraw pending_withdraw assets (accumulated when changing reward_per_sec)
messages.extend(
reward_assets
.into_iter()
.map(|ra| Ok(ra.into_msg(None, &deps.querier, staker_addr.clone())?))
.map(|ra| ra.into_msg(None, &deps.querier, staker_addr.clone()))
.collect::<StdResult<Vec<CosmosMsg>>>()?,
);
let mut response = Response::new();

Ok(Response::new().add_messages(messages).add_attributes([
attr("action", "unbond"),
attr("staker_addr", staker_addr.as_str()),
attr("amount", &amount.to_string()),
attr("staking_token", staking_token_addr.as_str()),
]))
// withdraw_avaiable_lock
let withdraw_response = _withdraw_lock(deps.storage, &env, &staker_addr, &staking_token)?;

messages.extend(
withdraw_response
.clone()
.messages
.into_iter()
.map(|msg| msg.msg)
.collect::<Vec<CosmosMsg>>(),
);
let withdraw_attrs = withdraw_response.attributes;

// checking bonding period
if let Ok(period) = read_unbonding_period(deps.storage, staking_token_addr.as_bytes()) {
if amount.gt(&Uint128::from(0u128)) {
let unlock_time = env.block.time.plus_seconds(period);
insert_lock_info(
deps.storage,
staking_token_addr.as_bytes(),
staker_addr.as_bytes(),
LockInfo {
amount,
unlock_time,
},
)?;

response = response.add_attributes([
attr("action", "unbonding"),
attr("staker_addr", staker_addr.as_str()),
attr("amount", amount.to_string()),
attr("staking_token", staking_token_addr.as_str()),
attr("unlock_time", unlock_time.seconds().to_string()),
])
}
} else {
let unbond_response = _unbond(&staker_addr, &staking_token_addr, amount)?;
messages.extend(
unbond_response
.messages
.into_iter()
.map(|msg| msg.msg)
.collect::<Vec<CosmosMsg>>(),
);
response = response.add_attributes(unbond_response.attributes);
}
Ok(response
.add_messages(messages)
.add_attributes(withdraw_attrs))
}

pub fn auto_stake(
Expand Down Expand Up @@ -205,6 +243,29 @@ pub fn auto_stake_hook(
bond(deps, staker_addr, staking_token, amount_to_stake)
}

pub fn _withdraw_lock(
storage: &mut dyn Storage,
env: &Env,
staker_addr: &Addr,
staking_token: &Addr,
) -> StdResult<Response> {
// execute 10 lock a time
let unlock_amount = remove_and_accumulate_lock_info(
storage,
staking_token.as_bytes(),
staker_addr.as_bytes(),
env.block.time,
)?;

if unlock_amount.is_zero() {
return Ok(Response::new());
}

let unbond_response = _unbond(staker_addr, staking_token, unlock_amount)?;

Ok(unbond_response)
}

fn _increase_bond_amount(
storage: &mut dyn Storage,
api: &dyn Api,
Expand Down Expand Up @@ -304,19 +365,34 @@ fn _decrease_bond_amount(
// if pending_withdraw is not empty, then return reward_assets to withdraw money
reward_assets = reward_info
.pending_withdraw
.into_iter()
.map(|ra| Ok(ra.to_normal(api)?))
.iter()
.map(|ra| ra.to_normal(api))
.collect::<StdResult<Vec<Asset>>>()?;

rewards_store(storage, staker_addr).remove(&asset_key);
// remove staker from the pool
stakers_store(storage, &asset_key).remove(staker_addr);
} else {
rewards_store(storage, staker_addr).save(&asset_key, &reward_info)?;
reward_info.pending_withdraw = vec![];
}
rewards_store(storage, staker_addr).save(&asset_key, &reward_info)?;

// Update pool info
store_pool_info(storage, &asset_key, &pool_info)?;

Ok((staking_token, reward_assets))
}

fn _unbond(staker_addr: &Addr, staking_token_addr: &Addr, amount: Uint128) -> StdResult<Response> {
let messages: Vec<CosmosMsg> = vec![WasmMsg::Execute {
contract_addr: staking_token_addr.to_string(),
msg: to_binary(&Cw20ExecuteMsg::Transfer {
recipient: staker_addr.to_string(),
amount,
})?,
funds: vec![],
}
.into()];

Ok(Response::new().add_messages(messages).add_attributes([
attr("action", "unbond"),
attr("staker_addr", staker_addr.as_str()),
attr("amount", amount.to_string()),
attr("staking_token", staking_token_addr.as_str()),
]))
}
Loading