Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
# WASM reproducibility check akin to SourceScan won't be available for staging contracts, deployed from PRs
run: |
if [ -n "$NEWLY_CREATED" ]; then
INIT_ARGS=$(cargo run --package test-utils --example generate_testnet_configuration)
INIT_ARGS=$(cargo run --package contract-mvp --example generate_testnet_configuration)
cargo near deploy build-reproducible-wasm --skip-git-remote-check "${{ env.NEAR_CONTRACT_PR_STAGING_ACCOUNT_ID }}" \
with-init-call new \
json-args "$INIT_ARGS" \
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,6 @@ overflow-checks = true

[lints]
workspace = true

[[example]]
name = "generate_testnet_configuration"
21 changes: 20 additions & 1 deletion common/src/market/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use near_sdk::{json_types::U64, near, AccountId};

use crate::{
asset::{BorrowAsset, BorrowAssetAmount, CollateralAsset, FungibleAsset},
asset::{
BorrowAsset, BorrowAssetAmount, CollateralAsset, CollateralAssetAmount, FungibleAsset,
},
borrow::{BorrowPosition, BorrowStatus, LiquidationReason},
fee::{Fee, TimeBasedFee},
rational::{Fraction, Rational},
Expand Down Expand Up @@ -92,6 +94,23 @@ impl MarketConfiguration {

scaled_collateral_value >= scaled_borrow_value
}

pub fn minimum_acceptable_liquidation_amount(
&self,
amount: CollateralAssetAmount,
oracle_price_proof: OraclePriceProof,
) -> BorrowAssetAmount {
// minimum_acceptable_amount = collateral_amount * (1 - maximum_liquidator_spread) * collateral_price / borrow_price
self.maximum_liquidator_spread
.complement()
.upcast::<u128>()
.checked_mul(oracle_price_proof.collateral_asset_price)
.and_then(|x| x.checked_div(oracle_price_proof.borrow_asset_price))
.and_then(|x| x.checked_scalar_mul(amount.as_u128()))
.and_then(|x| x.ceil())
.unwrap() // TODO: Eliminate .unwrap()
.into()
}
}

#[cfg(test)]
Expand Down
22 changes: 17 additions & 5 deletions common/src/market/external.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ pub trait MarketExternalInterface {
// BORROW FUNCTIONS
// ==================

// Required to implement NEP-141 FT token receiver to receive local fungible tokens.
// ft_on_receive :: where msg = collateralize
// ft_on_receive :: where msg = repay
// ft_on_receive :: where msg = Collateralize
fn collateralize_native(&mut self);
// ft_on_receive :: where msg = Repay
fn repay_native(&mut self) -> PromiseOrValue<()>;

fn get_borrow_position(&self, account_id: AccountId) -> Option<BorrowPosition>;
/// This is just a read-only function, so we don't care about validating
Expand Down Expand Up @@ -68,8 +69,8 @@ pub trait MarketExternalInterface {
// We assume that all borrowed assets are NEAR-local. That is to say, we
// don't yet support supplying of remote assets.

// Required to implement NEP-141 FT token receiver to receive local fungible tokens.
// ft_on_receive :: where msg = supply
// ft_on_receive :: where msg = Supply
fn supply_native(&mut self);

fn get_supply_position(&self, account_id: AccountId) -> Option<SupplyPosition>;

Expand All @@ -85,6 +86,17 @@ pub trait MarketExternalInterface {

fn harvest_yield(&mut self);

// =====================
// LIQUIDATION FUNCTIONS
// =====================

// ft_on_receive :: where msg = Liquidate { account_id }
fn liquidate_native(
&mut self,
account_id: AccountId,
oracle_price_proof: OraclePriceProof,
) -> Promise;

// =================
// YIELD FUNCTIONS
// =================
Expand Down
32 changes: 32 additions & 0 deletions examples/generate_testnet_configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#![allow(clippy::unwrap_used)]
//! Used by GitHub Actions to generate default market configuration.

use near_sdk::serde_json;
use templar_common::{
asset::{FungibleAsset, FungibleAssetAmount},
fee::{Fee, TimeBasedFee},
market::{MarketConfiguration, YieldWeights},
rational::{Fraction, Rational},
};

pub fn main() {
println!(
"{{\"configuration\":{}}}",
serde_json::to_string(&MarketConfiguration {
borrow_asset: FungibleAsset::nep141("usdt.fakes.testnet".parse().unwrap()),
collateral_asset: FungibleAsset::nep141("wrap.testnet".parse().unwrap()),
balance_oracle_account_id: "balance_oracle".parse().unwrap(),
minimum_collateral_ratio_per_borrow: Rational::new(120, 100),
maximum_borrow_asset_usage_ratio: Fraction::new(99, 100).unwrap(),
borrow_origination_fee: Fee::zero(),
borrow_annual_maintenance_fee: Fee::zero(),
maximum_borrow_duration_ms: None,
minimum_borrow_amount: FungibleAssetAmount::new(1),
maximum_borrow_amount: FungibleAssetAmount::new(u128::MAX),
supply_withdrawal_fee: TimeBasedFee::zero(),
yield_weights: YieldWeights::new_with_supply_weight(1),
maximum_liquidator_spread: Fraction::new(5, 100).unwrap(),
})
.unwrap(),
);
}
82 changes: 82 additions & 0 deletions src/impl_ft_receiver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver;
use near_sdk::{env, json_types::U128, near, AccountId, PromiseOrValue};
use templar_common::{
asset::{BorrowAssetAmount, CollateralAssetAmount},
market::{LiquidateMsg, Nep141MarketDepositMessage},
};

use crate::{Contract, ContractExt};

#[near]
impl FungibleTokenReceiver for Contract {
fn ft_on_transfer(
&mut self,
sender_id: AccountId,
amount: U128,
msg: String,
) -> PromiseOrValue<U128> {
let msg = near_sdk::serde_json::from_str::<Nep141MarketDepositMessage>(&msg)
.unwrap_or_else(|_| env::panic_str("Invalid ft_on_transfer msg"));

let asset_id = env::predecessor_account_id();

let use_borrow_asset = || {
if !self.configuration.borrow_asset.is_nep141(&asset_id) {
env::panic_str("Unsupported borrow asset");
}

BorrowAssetAmount::new(amount.0)
};

let use_collateral_asset = || {
if !self.configuration.collateral_asset.is_nep141(&asset_id) {
env::panic_str("Unsupported collateral asset");
}

CollateralAssetAmount::new(amount.0)
};

match msg {
Nep141MarketDepositMessage::Supply => {
let amount = use_borrow_asset();

self.execute_supply(&sender_id, amount);

PromiseOrValue::Value(U128(0))
}
Nep141MarketDepositMessage::Collateralize => {
let amount = use_collateral_asset();

self.execute_collateralize(&sender_id, amount);

PromiseOrValue::Value(U128(0))
}
Nep141MarketDepositMessage::Repay => {
let amount = use_borrow_asset();

let refund = self.execute_repay(&sender_id, amount);

PromiseOrValue::Value(refund.into())
}
Nep141MarketDepositMessage::Liquidate(LiquidateMsg {
account_id,
oracle_price_proof,
}) => {
let amount = use_borrow_asset();

let liquidated_collateral =
self.execute_liquidate_initial(&account_id, amount, oracle_price_proof);

PromiseOrValue::Promise(
self.configuration
.collateral_asset
.transfer(sender_id, liquidated_collateral)
.then(
Self::ext(env::current_account_id())
.after_liquidate_via_ft_transfer_call(account_id, amount),
),
)
}
}
}
}
Loading