Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 71 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,55 @@ 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,
start_after,
limit,
order,
} => to_binary(&query_lock_infos(
deps,
_env,
staker_addr,
staking_token,
start_after,
limit,
order,
)?),
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,
start_after: Option<u64>,
limit: Option<u32>,
order: Option<i32>,
) -> StdResult<LockInfosResponse> {
let lock_infos = read_user_lock_info(
deps.storage,
staking_token.as_bytes(),
staker_addr.as_bytes(),
start_after,
limit,
order,
)?;
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
150 changes: 111 additions & 39 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,79 @@ 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(
deps.storage,
deps.api,
&staker_addr_raw,
&staking_token,
amount,
)?;
let mut messages = vec![];
let mut response = Response::new();

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()];
// withdraw_avaiable_lock
let withdraw_response = _withdraw_lock(deps.storage, &env, &staker_addr, &staking_token)?;

// withdraw pending_withdraw assets (accumulated when changing reward_per_sec)
messages.extend(
reward_assets
withdraw_response
.clone()
.messages
.into_iter()
.map(|ra| Ok(ra.into_msg(None, &deps.querier, staker_addr.clone())?))
.collect::<StdResult<Vec<CosmosMsg>>>()?,
.map(|msg| msg.msg)
.collect::<Vec<CosmosMsg>>(),
);

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()),
]))
let withdraw_attrs = withdraw_response.attributes;
if !amount.is_zero() {
let (_, reward_assets) = _decrease_bond_amount(
deps.storage,
deps.api,
&staker_addr_raw,
&staking_token,
amount,
)?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we can refactor the unbond_lock logic so we can call it in here

// withdraw pending_withdraw assets (accumulated when changing reward_per_sec)
messages.extend(
reward_assets
.into_iter()
.map(|ra| ra.into_msg(None, &deps.querier, staker_addr.clone()))
.collect::<StdResult<Vec<CosmosMsg>>>()?,
);
// checking bonding period
if let Ok(period) = read_unbonding_period(deps.storage, staking_token.as_bytes()) {
let unlock_time = env.block.time.plus_seconds(period);
insert_lock_info(
deps.storage,
staking_token.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.as_str()),
attr("unlock_time", unlock_time.seconds().to_string()),
])
} else {
let unbond_response = _unbond(&staker_addr, &staking_token, 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 +239,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 +361,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