Pool

config.rs

Stores all relevant contract parameters.
1
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
2
pub struct Config {
3
pub this: CanonicalAddr,
4
pub owner: CanonicalAddr,
5
pub beneficiary: CanonicalAddr,
6
pub moneymarket: CanonicalAddr,
7
pub atoken: CanonicalAddr,
8
pub stable_denom: String,
9
pub dp_token: CanonicalAddr,
10
}
Copied!
this: the address of this pool contract.
owner: the owner address of this pool contract.
beneficiary: the address of which all interest collected from principal deposits are sent to. could be a single address, or a contract address that oversees additional distribution logic.
moneymarket: the address of the Anchor money market contract to accept deposits.
atoken: the address of the aUST token contract.
stable_denom: base Terra stablecoin denomination of this pool to be deposited to Anchor, i.e. uusd.
dp_token: the address of the DP token contract minted by this pool.

contract.rs

Defines logic entry points for InitMsg, HandleMsg and QueryMsg calls.
1
pub fn init<S: Storage, A: Api, Q: Querier>(
2
deps: &mut Extern<S, A, Q>,
3
env: Env,
4
msg: InitMsg,
5
) -> StdResult<InitResponse> {
6
let sender = env.message.sender;
7
let raw_sender = deps.api.canonical_address(&sender)?;
8
9
let mut config = config::Config {
10
this: deps.api.canonical_address(&env.contract.address)?,
11
owner: raw_sender,
12
beneficiary: deps.api.canonical_address(&msg.beneficiary)?,
13
fee_collector: deps.api.canonical_address(&msg.fee_collector)?,
14
exchange_rate_feeder: deps.api.canonical_address(&msg.exchange_rate_feeder)?,
15
moneymarket: deps.api.canonical_address(&msg.moneymarket)?,
16
stable_denom: String::default(),
17
atoken: CanonicalAddr::default(),
18
dp_token: CanonicalAddr::default(),
19
};
20
21
let market_config = querier::anchor::config(deps, &config.moneymarket)?;
22
23
config.stable_denom = market_config.stable_denom.clone();
24
config.atoken = deps.api.canonical_address(&market_config.aterra_contract)?;
25
26
config::store(&mut deps.storage, &config)?;
27
28
Ok(InitResponse {
29
messages: vec![CosmosMsg::Wasm(WasmMsg::Instantiate {
30
code_id: msg.dp_code_id,
31
send: vec![],
32
label: None,
33
msg: to_binary(&Cw20InitMsg {
34
name: format!("Deposit Token - {}", msg.pool_name),
35
symbol: "PylonDP".to_string(),
36
decimals: 6u8,
37
initial_balances: vec![],
38
mint: Some(MinterResponse {
39
minter: env.contract.address.clone(),
40
cap: None,
41
}),
42
init_hook: Some(Cw20InitHook {
43
contract_addr: env.contract.address,
44
msg: to_binary(&HandleMsg::RegisterDPToken {})?,
45
}),
46
})?,
47
})],
48
log: vec![],
49
})
50
}
Copied!
Initializes a new pool contract. Updates data storage as defined with config.rs, and initializes a new DP token contract linked to this pool contract.
1
pub fn handle<S: Storage, A: Api, Q: Querier>(
2
deps: &mut Extern<S, A, Q>,
3
env: Env,
4
msg: HandleMsg,
5
) -> StdResult<HandleResponse> {
6
match msg {
7
HandleMsg::Receive(msg) => CoreHandler::receive(deps, env, msg),
8
HandleMsg::Deposit {} => CoreHandler::deposit(deps, env),
9
HandleMsg::ClaimReward {} => CoreHandler::claim_reward(deps, env),
10
HandleMsg::RegisterDPToken {} => CoreHandler::register_dp_token(deps, env),
11
}
12
}
13
Copied!
Defines HandleMsg endpoints. Calls the following functions per HandleMsg:
    Receive(msg) : calls receive(deps, env, msg) defined under handler_exec.rs.
    Deposit {} : calls deposit(deps, env) defined under handler_exec.rs.
    ClaimReward {} : calls claim_reward(deps, env) defined under handler_exec.rs.
    [INTERNAL] RegisterDPToken {} : calls register_dp_token(deps, env) defined under handler_exec.rs.
1
pub fn query<S: Storage, A: Api, Q: Querier>(
2
deps: &Extern<S, A, Q>,
3
msg: QueryMsg,
4
) -> StdResult<Binary> {
5
match msg {
6
QueryMsg::DepositAmountOf { owner } => QueryHandler::deposit_amount(deps, owner), // dp_token.balanceOf(msg.sender)
7
QueryMsg::TotalDepositAmount {} => QueryHandler::total_deposit_amount(deps), // dp_token.totalSupply()
8
QueryMsg::Config {} => QueryHandler::config(deps), // config
9
QueryMsg::ClaimableReward {} => QueryHandler::claimable_reward(deps), // config.strategy.reward()
10
}
11
}
12
Copied!
Defines QueryMsg endpoints. Calls the following functions per QueryMsg:
    DepositAmountOf { owner } : calls deposit_amount(deps, owner) defined under handler_query.rs; returns the current DP token balance of the sender of this message.
    TotalDepositAmount {} : calls total_deposit_amount(deps) defined under handler_query.rs; returns the total DP token balanced linked to this pool contract.
    Config {} : calls config(deps) defined under handler/query.rs; returns the following:
      beneficiary: returns the current beneficiary address set for this pool contract.
      moneymarket: returns the Anchor money market contract set for this pool contract to call for deposit_stable and redeem_stable messages. For further information regarding logic of the Anchor money market, please refer to Anchor Protocol's official documentation.
      stable_denom: returns the current Terra stablecoin denomination that the underlying Anchor money market contract accepts.
      anchor_token: returns the aToken contract address that the underlying Anchor money market contract returns on deposits.
      dp_token: returns the DP token contract address minted by this pool contract on new deposits.
    GetClaimableReward {} : calls claimable_reward(deps) defined under handler_query.rs; returns all claimable UST rewards for all UST deposits held by this pool.

handler/core.rs

Defines core contract execution logic. Functions are as follows:
    receive: defines a CW-20 token contract receive hook, which is executed when a Pylon DP token is sent to this Pylon pool.
    deposit: deposits UST to this Pylon pool, deposits them to Anchor, and mints Pylon DP tokens back to the depositor.
    redeem: consecutively called on a CW-20 receive call. Burns received DP tokens, retrieves equivalent amounts of UST from Anchor, and returns principal UST deposits excluding any interest generated.
    claim_reward: claims any remaining interest remaining in the pool, and sends them over to the beneficiary address. Interest is calculated as (total_aust_amount * exchange_rate) - (total_dp_balance)
    register_dp_token: registers a DP token contract controlled by this Pylon deposit pool for new DP token mints/burns on deposits/redemptions.
1
pub fn receive<S: Storage, A: Api, Q: Querier>(
2
deps: &Extern<S, A, Q>,
3
env: Env,
4
cw20_msg: Cw20ReceiveMsg,
5
) -> StdResult<HandleResponse> {
6
let sender = env.message.sender.clone();
7
if let Some(msg) = cw20_msg.msg {
8
match from_binary(&msg)? {
9
Cw20HookMsg::Redeem {} => {
10
// only asset contract can execute this message
11
let config: config::Config = config::read(&deps.storage)?;
12
if deps.api.canonical_address(&sender)? != config.dp_token {
13
return Err(StdError::unauthorized());
14
}
15
16
redeem(deps, env, cw20_msg.sender, cw20_msg.amount)
17
}
18
}
19
} else {
20
Err(StdError::generic_err(
21
"Invalid request: \"redeem\" message not included in request",
22
))
23
}
24
}
25
Copied!
If a CW-20 transfer to this contract includes a Redeem {} Cw20HookMsg:
    checks if the sender of this HookMsg is the registered DP token address (i.e. not any other token contract address being sent to this pool) - return unauthorized() error if not, else continue
    if passed, call redeem(deps, _env, cw20_msg.sender, cw20_msg.amount)
If a Redeem {} message is not included with this function call, return an invalid_request error.
1
pub fn deposit<S: Storage, A: Api, Q: Querier>(
2
deps: &Extern<S, A, Q>,
3
env: Env,
4
) -> StdResult<HandleResponse> {
5
let config = config::read(&deps.storage)?;
6
7
// check deposit
8
let received: Uint256 = env
9
.message
10
.sent_funds
11
.iter()
12
.find(|c| c.denom == config.stable_denom)
13
.map(|c| Uint256::from(c.amount))
14
.unwrap_or_else(Uint256::zero);
15
16
if received.is_zero() {
17
return Err(StdError::generic_err(format!(
18
"Pool: insufficient token amount {}",
19
config.stable_denom,
20
)));
21
}
22
23
let deposit_amount = deduct_tax(
24
deps,
25
Coin {
26
denom: config.stable_denom.clone(),
27
amount: received.into(),
28
},
29
)?
30
.amount;
31
32
Ok(HandleResponse {
33
messages: [
34
querier::feeder::update_msg(deps, &config.exchange_rate_feeder, &config.dp_token)?,
35
querier::anchor::deposit_stable_msg(
36
deps,
37
&config.moneymarket,
38
&config.stable_denom,
39
deposit_amount,
40
)?,
41
vec![CosmosMsg::Wasm(WasmMsg::Execute {
42
contract_addr: deps.api.human_address(&config.dp_token)?,
43
msg: to_binary(&Cw20HandleMsg::Mint {
44
recipient: env.message.sender.clone(),
45
amount: deposit_amount,
46
})?,
47
send: vec![],
48
})],
49
]
50
.concat(),
51
log: vec![
52
log("action", "deposit"),
53
log("sender", env.message.sender),
54
log("amount", deposit_amount.to_string()),
55
],
56
data: None,
57
})
58
}
59
Copied!
Sending native Terra stablecoins (UST) with this MsgExecuteContract call:
    Check how much deposits have been made to this pool contract for stable_denom.
      If there are no deposits, return a generic_err.
    Issue a HandleResponse issuing the following messages:
      call deposit_stable from the Anchor money market contract for deposit_amount units of stable_denom(e.g. 100 UST). aUST will be returned from the Anchor money market contract to this pool contract.
      issue another MsgExecuteContract call that mints deposit_amount units of Pylon DP tokens for this particular pool to the caller.
1
pub fn redeem<S: Storage, A: Api, Q: Querier>(
2
deps: &Extern<S, A, Q>,
3
env: Env,
4
sender: HumanAddr,
5
amount: Uint128,
6
) -> StdResult<HandleResponse> {
7
let config = config::read(&deps.storage)?;
8
9
let (market_redeem_amount, pool_redeem_amount, _) =
10
querier::pool::calculate_return_amount(deps, &config, Uint256::from(amount))?;
11
12
Ok(HandleResponse {
13
messages: [
14
querier::feeder::update_msg(deps, &config.exchange_rate_feeder, &config.dp_token)?,
15
vec![CosmosMsg::Wasm(WasmMsg::Execute {
16
contract_addr: deps.api.human_address(&config.dp_token)?,
17
msg: to_binary(&Cw20HandleMsg::Burn { amount })?,
18
send: vec![],
19
})],
20
querier::anchor::redeem_stable_msg(
21
deps,
22
&config.moneymarket,
23
&config.atoken,
24
market_redeem_amount.into(),
25
)?,
26
vec![CosmosMsg::Bank(BankMsg::Send {
27
from_address: env.contract.address,
28
to_address: sender,
29
amount: vec![pool_redeem_amount.clone()],
30
})],
31
]
32
.concat(),
33
log: vec![
34
log("action", "redeem"),
35
log("sender", env.message.sender),
36
log("amount", pool_redeem_amount.amount),
37
],
38
data: None,
39
})
40
}
41
Copied!
When a Redeem {} Cw20HookMsg is called:
    Query the current epoch_state from the Anchor money market.
    Calculate how much aUST needs to be redeemed back to UST to maintain principle: dp_token_balance / epoch_state.exchange_rate
    Calculate how much Terra tax is charged during redemptions. As tax is charged for every Terra stablecoin MsgSends on the Terra blockchain, deduct_tax should be called twice, as there are two cases when Terra tax is charged for UST transfers during redemptions:
      when UST is transferred from the Anchor money market to this Pylon pool contract
      when UST is transferred from this Pylon pool contract to the user's wallet
    Issue a HandleResponse containing the following messages:
      burn amount units of DP tokens for this Pylon pool.
      redeem market_redeem_amount aUST (calculated as dp_token_balance / epoch_state.exchange_rate) for pool_redeem_amount UST.
      MsgSends pool_redeem_amount back to the caller of this contract
1
pub fn claim_reward<S: Storage, A: Api, Q: Querier>(
2
deps: &Extern<S, A, Q>,
3
env: Env,
4
) -> StdResult<HandleResponse> {
5
// calculate (total_aust_amount * exchange_rate) - (total_dp_balance)
6
let config = config::read(&deps.storage)?;
7
if config.beneficiary != deps.api.canonical_address(&env.message.sender)? {
8
return Err(StdError::unauthorized());
9
}
10
11
let (reward_amount, fee_amount) =
12
querier::pool::calculate_reward_amount(deps, &config, Option::from(env.block.time))?;
13
let (market_redeem_amount, _, _) =
14
querier::pool::calculate_return_amount(deps, &config, reward_amount.add(fee_amount))?;
15
let (_, beneficiary_redeem_amount, _) =
16
querier::pool::calculate_return_amount(deps, &config, reward_amount)?;
17
let (_, collector_redeem_amount, _) =
18
querier::pool::calculate_return_amount(deps, &config, fee_amount)?;
19
20
Ok(HandleResponse {
21
messages: [
22
querier::feeder::update_msg(deps, &config.exchange_rate_feeder, &config.dp_token)?,
23
querier::anchor::redeem_stable_msg(
24
deps,
25
&config.moneymarket,
26
&config.atoken,
27
market_redeem_amount.into(),
28
)?,
29
vec![
30
CosmosMsg::Bank(BankMsg::Send {
31
from_address: env.contract.address.clone(),
32
to_address: deps.api.human_address(&config.beneficiary)?,
33
amount: vec![beneficiary_redeem_amount.clone()],
34
}),
35
CosmosMsg::Bank(BankMsg::Send {
36
from_address: env.contract.address.clone(),
37
to_address: deps.api.human_address(&config.fee_collector)?,
38
amount: vec![collector_redeem_amount.clone()],
39
}),
40
],
41
]
42
.concat(),
43
log: vec![
44
log("action", "claim_reward"),
45
log("sender", env.message.sender),
46
log("amount", beneficiary_redeem_amount.amount),
47
log("fee", collector_redeem_amount.amount),
48
],
49
data: None,
50
})
51
}
52
Copied!
On a ClaimReward {} HandleMsg call:
    calculate how much aUST should be redeemed: (total_aust_amount * exchange_rate) - (total_dp_balance)
    apply a virtual exchange rate queryable from the Pylon Exchange Rate contract. This effectively re-calculates how much UST should be claimed excluding MINE token buybacks, which is sent to the Collector contract.
    calculates how much UST should be redeemed for:
      market_redeem_amount : amount of aUST for redemptions before Terra blockchain tax for both beneficiary and collector. returns Uint256.
      beneficiary_redeem_amount : amount of aUST for redemptions after Terra blockchain tax for beneficiary. returns a Coin object.
      collector_redeem_amount : amount of aUST for redemptions after Terra blockchain tax for collector, sent to the Collector contract for buybacks. returns a Coin object.
    Issue a HandleResponse that includes the following messages:
      update virtual exchange rate
      redeem market_redeem_amount aUST from Anchor
      send beneficiary_redeem_amount to beneficiary account
      send collector_redeem_amount to Collector contract
Last modified 3mo ago