Skip to content

Commit

Permalink
Functional routing with unit-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tasiov committed Aug 22, 2023
1 parent 8303ccf commit 3b38f7e
Show file tree
Hide file tree
Showing 23 changed files with 828 additions and 465 deletions.
4 changes: 2 additions & 2 deletions contracts/infinity-index/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub fn execute_update_pair_indices(
deps.storage,
info.sender.clone(),
&PairQuote {
pair: info.sender.clone(),
address: info.sender.clone(),
collection: collection.clone(),
quote: coin(amount.u128(), denom.clone()),
},
Expand All @@ -73,7 +73,7 @@ pub fn execute_update_pair_indices(
deps.storage,
info.sender.clone(),
&PairQuote {
pair: info.sender,
address: info.sender,
collection,
quote: coin(amount.u128(), &denom),
},
Expand Down
2 changes: 1 addition & 1 deletion contracts/infinity-index/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub const INFINITY_GLOBAL: Item<Addr> = Item::new("g");

#[cw_serde]
pub struct PairQuote {
pub pair: Addr,
pub address: Addr,
pub collection: Addr,
pub quote: Coin,
}
Expand Down
11 changes: 8 additions & 3 deletions contracts/infinity-pair/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::error::ContractError;
use crate::helpers::{load_pair, load_payout_context, only_active, only_pair_owner};
use crate::msg::ExecuteMsg;
use crate::pair::Pair;
use crate::state::{BondingCurve, PairType, TokenId, NFT_DEPOSITS};
use crate::state::{BondingCurve, PairType, TokenId, INFINITY_GLOBAL, NFT_DEPOSITS};

use cosmwasm_std::{
coin, ensure, ensure_eq, has_coins, Addr, Coin, DepsMut, Env, MessageInfo, Order, StdResult,
Expand All @@ -29,8 +29,13 @@ pub fn execute(

let (mut pair, mut response) = handle_execute_msg(deps.branch(), env, info, msg, pair)?;

let payout_context =
load_payout_context(deps.as_ref(), &pair.immutable.collection, &pair.immutable.denom)?;
let infinity_global = INFINITY_GLOBAL.load(deps.storage)?;
let payout_context = load_payout_context(
deps.as_ref(),
&infinity_global,
&pair.immutable.collection,
&pair.immutable.denom,
)?;

response = pair.save_and_update_indices(deps.storage, &payout_context, response)?;

Expand Down
8 changes: 3 additions & 5 deletions contracts/infinity-pair/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::{
pair::Pair,
state::{
QuoteSummary, TokenPayment, INFINITY_GLOBAL, PAIR_CONFIG, PAIR_IMMUTABLE, PAIR_INTERNAL,
},
state::{QuoteSummary, TokenPayment, PAIR_CONFIG, PAIR_IMMUTABLE, PAIR_INTERNAL},
ContractError,
};

Expand Down Expand Up @@ -133,10 +131,10 @@ impl PayoutContext {

pub fn load_payout_context(
deps: Deps,
infinity_global: &Addr,
collection: &Addr,
denom: &String,
) -> Result<PayoutContext, ContractError> {
let infinity_global = INFINITY_GLOBAL.load(deps.storage)?;
let global_config = load_global_config(&deps.querier, &infinity_global)?;

let min_price = load_min_price(&deps.querier, &infinity_global, denom)?
Expand All @@ -153,7 +151,7 @@ pub fn load_payout_context(
global_config,
royalty_entry,
min_price,
infinity_global,
infinity_global: infinity_global.clone(),
denom: denom.clone(),
})
}
27 changes: 12 additions & 15 deletions contracts/infinity-pair/src/pair.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use crate::error::ContractError;
use crate::helpers::PayoutContext;
use crate::math::{
calc_cp_trade_buy_from_pair_price, calc_cp_trade_sell_to_pair_price,
calc_exponential_spot_price_user_submits_nft, calc_exponential_spot_price_user_submits_tokens,
calc_exponential_trade_buy_from_pair_price, calc_linear_spot_price_user_submits_nft,
calc_linear_spot_price_user_submits_tokens, calc_linear_trade_buy_from_pair_price,
};
use crate::math;
use crate::msg::TransactionType;
use crate::state::{
BondingCurve, PairConfig, PairImmutable, PairInternal, PairType, QuoteSummary, PAIR_CONFIG,
Expand Down Expand Up @@ -198,10 +193,10 @@ impl Pair {
} => {
let result = match tx_type {
TransactionType::UserSubmitsNfts => {
calc_linear_spot_price_user_submits_nft(spot_price, delta)
math::calc_linear_spot_price_user_submits_nft(spot_price, delta)
},
TransactionType::UserSubmitsTokens => {
calc_linear_spot_price_user_submits_tokens(spot_price, delta)
math::calc_linear_spot_price_user_submits_tokens(spot_price, delta)
},
};
match result {
Expand All @@ -222,10 +217,10 @@ impl Pair {
} => {
let result = match tx_type {
TransactionType::UserSubmitsNfts => {
calc_exponential_spot_price_user_submits_nft(spot_price, delta)
math::calc_exponential_spot_price_user_submits_nft(spot_price, delta)
},
TransactionType::UserSubmitsTokens => {
calc_exponential_spot_price_user_submits_tokens(spot_price, delta)
math::calc_exponential_spot_price_user_submits_tokens(spot_price, delta)
},
};
match result {
Expand Down Expand Up @@ -260,7 +255,8 @@ impl Pair {
..
} => Some(spot_price),
BondingCurve::ConstantProduct => {
calc_cp_trade_sell_to_pair_price(self.total_tokens, self.internal.total_nfts).ok()
math::calc_cp_trade_sell_to_pair_price(self.total_tokens, self.internal.total_nfts)
.ok()
},
};

Expand Down Expand Up @@ -301,7 +297,7 @@ impl Pair {
spot_price,
delta,
},
) => calc_linear_trade_buy_from_pair_price(*spot_price, *delta).ok(),
) => math::calc_linear_trade_buy_from_pair_price(*spot_price, *delta).ok(),
(
PairType::Trade {
..
Expand All @@ -310,14 +306,15 @@ impl Pair {
spot_price,
delta,
},
) => calc_exponential_trade_buy_from_pair_price(*spot_price, *delta).ok(),
) => math::calc_exponential_trade_buy_from_pair_price(*spot_price, *delta).ok(),
(
PairType::Trade {
..
},
BondingCurve::ConstantProduct,
) => {
calc_cp_trade_buy_from_pair_price(self.total_tokens, self.internal.total_nfts).ok()
math::calc_cp_trade_buy_from_pair_price(self.total_tokens, self.internal.total_nfts)
.ok()
},
_ => None,
};
Expand All @@ -333,7 +330,7 @@ impl Pair {
self.internal.sell_to_pair_quote_summary.as_ref().map(|summary| summary.seller_amount);

let buy_from_pair_quote =
self.internal.sell_to_pair_quote_summary.as_ref().map(|summary| summary.total());
self.internal.buy_from_pair_quote_summary.as_ref().map(|summary| summary.total());

response.add_message(WasmMsg::Execute {
contract_addr: infinity_index.to_string(),
Expand Down
1 change: 1 addition & 0 deletions contracts/infinity-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ thiserror = { workspace = true }
sha2 = { workspace = true }
cw721-base = { workspace = true }
cw721 = { workspace = true }
cw-address-like = { workspace = true }
100 changes: 62 additions & 38 deletions contracts/infinity-router/src/execute.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
use std::iter::zip;

use crate::error::ContractError;
use crate::msg::{ExecuteMsg, NftOrder, SwapParams};
use crate::nfts_for_tokens_iterators::{NftForTokensQuote, NftForTokensSource, NftsForTokens};
use crate::tokens_for_nfts_iterators::{TokensForNftQuote, TokensForNftSource, TokensForNfts};
use crate::helpers::approve_nft;
use crate::msg::{ExecuteMsg, SellOrder, SwapParams};
use crate::nfts_for_tokens_iterators::{
iter::NftsForTokens,
types::{NftForTokensQuote, NftForTokensSource, NftForTokensSourceData},
};
use crate::state::INFINITY_GLOBAL;
use crate::tokens_for_nfts_iterators::types::{TokensForNftQuote, TokensForNftSourceData};
use crate::tokens_for_nfts_iterators::{iter::TokensForNfts, types::TokensForNftSource};

use cosmwasm_std::{
coin, ensure, ensure_eq, to_binary, Addr, CosmosMsg, DepsMut, Env, MessageInfo, Uint128,
WasmMsg,
};
use cw_utils::{maybe_addr, must_pay, nonpayable};
use cw_utils::{must_pay, nonpayable};
use infinity_pair::msg::ExecuteMsg as PairExecuteMsg;
use infinity_shared::InfinityError;
use sg_marketplace_common::address::address_or;
use sg_marketplace_common::coin::transfer_coin;
use sg_marketplace_common::nft::transfer_nft;
use sg_std::Response;
use std::iter::zip;

#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
Expand All @@ -33,7 +38,7 @@ pub fn execute(
ExecuteMsg::SwapNftsForTokens {
collection,
denom,
nft_orders,
sell_orders,
swap_params,
filter_sources,
} => execute_swap_nfts_for_tokens(
Expand All @@ -42,8 +47,8 @@ pub fn execute(
info,
api.addr_validate(&collection)?,
denom,
nft_orders,
swap_params.unwrap_or_default(),
sell_orders,
swap_params.unwrap_or_default().str_to_addr(api)?,
filter_sources.unwrap_or_default(),
),
ExecuteMsg::SwapTokensForNfts {
Expand All @@ -59,7 +64,7 @@ pub fn execute(
api.addr_validate(&collection)?,
denom,
max_inputs,
swap_params.unwrap_or_default(),
swap_params.unwrap_or_default().str_to_addr(api)?,
filter_sources.unwrap_or_default(),
),
}
Expand All @@ -72,37 +77,48 @@ pub fn execute_swap_nfts_for_tokens(
info: MessageInfo,
collection: Addr,
denom: String,
nft_orders: Vec<NftOrder>,
swap_params: SwapParams,
sell_orders: Vec<SellOrder>,
swap_params: SwapParams<Addr>,
filter_sources: Vec<NftForTokensSource>,
) -> Result<Response, ContractError> {
nonpayable(&info)?;

let iterator =
NftsForTokens::initialize(deps.as_ref(), collection.clone(), denom.clone(), filter_sources);
let infinity_global = INFINITY_GLOBAL.load(deps.storage)?;
let iterator = NftsForTokens::initialize(
deps.as_ref(),
&infinity_global,
&collection,
&denom,
filter_sources,
)?;

let requested_swaps = nft_orders.len();
let requested_swaps = sell_orders.len();
let quotes = iterator.take(requested_swaps).collect::<Vec<NftForTokensQuote>>();

let mut response = Response::new();

let mut num_swaps = 0u32;
for (nft_order, quote) in zip(nft_orders, quotes) {
response =
transfer_nft(&collection, &nft_order.input_token_id, &env.contract.address, response);

if quote.amount < nft_order.min_output {
for (sell_order, quote) in zip(sell_orders, quotes) {
if quote.amount < sell_order.min_output {
break;
}

match quote.source {
NftForTokensSource::Infinity {} => {
response =
transfer_nft(&collection, &sell_order.input_token_id, &env.contract.address, response);

match quote.source_data {
NftForTokensSourceData::Infinity(_) => {
response =
approve_nft(&collection, &quote.address, &sell_order.input_token_id, response);
response = response.add_message(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: quote.address.to_string(),
msg: to_binary(&PairExecuteMsg::SwapNftForTokens {
token_id: nft_order.input_token_id,
min_output: coin(nft_order.min_output.u128(), &denom),
asset_recipient: swap_params.asset_recipient.clone(),
token_id: sell_order.input_token_id,
min_output: coin(sell_order.min_output.u128(), &denom),
asset_recipient: Some(
address_or(swap_params.asset_recipient.as_ref(), &info.sender)
.to_string(),
),
})?,
funds: vec![],
}))
Expand Down Expand Up @@ -132,10 +148,10 @@ pub fn execute_swap_tokens_for_nfts(
collection: Addr,
denom: String,
max_inputs: Vec<Uint128>,
swap_params: SwapParams,
swap_params: SwapParams<Addr>,
filter_sources: Vec<TokensForNftSource>,
) -> Result<Response, ContractError> {
let mut received_amount = must_pay(&info, &denom)?;
let received_amount = must_pay(&info, &denom)?;
let expected_amount = max_inputs.iter().sum::<Uint128>();
ensure_eq!(
received_amount,
Expand All @@ -145,33 +161,42 @@ pub fn execute_swap_tokens_for_nfts(
}
);

let iterator =
TokensForNfts::initialize(deps.as_ref(), collection, denom.clone(), filter_sources);
let infinity_global = INFINITY_GLOBAL.load(deps.storage)?;
let iterator = TokensForNfts::initialize(
deps.as_ref(),
&infinity_global,
&collection,
&denom,
filter_sources,
);

let requested_swaps = max_inputs.len();
let quotes = iterator.take(requested_swaps).collect::<Vec<TokensForNftQuote>>();

let mut response = Response::new();

let asset_recipient = address_or(swap_params.asset_recipient.as_ref(), &info.sender);

let mut num_swaps = 0u32;
let mut paid_amount = Uint128::zero();
for (max_input, quote) in zip(max_inputs, quotes) {
if max_input > quote.amount {
if max_input < quote.amount {
break;
}

match quote.source {
TokensForNftSource::Infinity {} => {
match quote.source_data {
TokensForNftSourceData::Infinity(_) => {
response = response.add_message(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: quote.address.to_string(),
msg: to_binary(&PairExecuteMsg::SwapTokensForAnyNft {
asset_recipient: swap_params.asset_recipient.clone(),
asset_recipient: Some(asset_recipient.to_string()),
})?,
funds: vec![coin(quote.amount.u128(), &denom)],
}))
},
}

received_amount -= quote.amount;
paid_amount += quote.amount;
num_swaps += 1;
}

Expand All @@ -184,10 +209,9 @@ pub fn execute_swap_tokens_for_nfts(
)));
}

if !received_amount.is_zero() {
let recipient =
address_or(maybe_addr(deps.api, swap_params.asset_recipient)?.as_ref(), &info.sender);
response = transfer_coin(coin(received_amount.u128(), &denom), &recipient, response);
let refund_amount = received_amount.checked_sub(paid_amount).unwrap();
if !refund_amount.is_zero() {
response = transfer_coin(coin(refund_amount.u128(), &denom), &asset_recipient, response);
}

Ok(response)
Expand Down
21 changes: 21 additions & 0 deletions contracts/infinity-router/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use cosmwasm_std::{to_binary, Addr, SubMsg, WasmMsg};
use cw721::Cw721ExecuteMsg;
use sg_std::Response;

pub fn approve_nft(
collection: &Addr,
spender: &Addr,
token_id: &String,
response: Response,
) -> Response {
response.add_submessage(SubMsg::new(WasmMsg::Execute {
contract_addr: collection.to_string(),
msg: to_binary(&Cw721ExecuteMsg::Approve {
spender: spender.to_string(),
token_id: token_id.to_string(),
expires: None,
})
.unwrap(),
funds: vec![],
}))
}
Loading

0 comments on commit 3b38f7e

Please sign in to comment.