Skip to content

Commit

Permalink
feat(query): allow contract owner to add supported pool manually, ref…
Browse files Browse the repository at this point in the history
…actor tests
  • Loading branch information
TropicalDog17 committed Nov 23, 2023
1 parent 51ddd8c commit fed6800
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 76 deletions.
215 changes: 165 additions & 50 deletions src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ use cw2::set_contract_version;
use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{State, STATE};
use astroport::pair;
use cosmwasm_std::StdError;
use cosmwasm_std::WasmMsg;
use astroport::pair::{self};
use cosmwasm_std::{Addr, Order, StdError, WasmMsg};

use self::query::*;
// version info for migration info
const CONTRACT_NAME: &str = "crates.io:lq-express-sm";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
Expand Down Expand Up @@ -42,6 +43,11 @@ pub fn execute(
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::Astro { pair_address } => execute::astro_exec(deps, info, pair_address),
ExecuteMsg::AddSupportedPool {
pool_address,
token_1,
token_2,
} => execute::add_supported_pool(deps, info, pool_address, token_1, token_2),
}
}

Expand Down Expand Up @@ -73,7 +79,6 @@ fn handle_swap_reply(_deps: DepsMut, _env: Env, msg: Reply) -> StdResult<Respons
.iter()
.find(|e| e.key == "return_amount")
.ok_or_else(|| StdError::generic_err("unable to find coin spent event".to_string()))?;

// let spender_address = coin_spent_event
// .attributes
// .iter()
Expand All @@ -93,18 +98,46 @@ pub mod execute {
use astroport::asset::Asset;
use cosmwasm_std::{Decimal, SubMsg};

use crate::state::{PoolInfo, POOL_INFO};

use super::*;

pub fn astro_exec(
_deps: DepsMut,
deps: DepsMut,
info: MessageInfo,
pair_address: String,
) -> Result<Response, ContractError> {
let inj_amount = cw_utils::must_pay(&info, "inj").unwrap().u128();
let pool_infos: Vec<_> = POOL_INFO
.idx
.address
.prefix(pair_address.clone())
.range(deps.storage, None, None, Order::Ascending)
.take(1)
.flatten()
.collect();

if pool_infos.is_empty() {
return Err(ContractError::PoolNotExist {});
}
let pair_info = pool_infos
.first()
.map(|pool_info| (pool_info.1.token_1.clone(), pool_info.1.token_2.clone()))
.unwrap();
// Based on cw_utils::must_pay implementation
let coin = cw_utils::one_coin(&info)?;
let offer_asset = coin.denom.clone();
let amount: u128 = if offer_asset != pair_info.0 && offer_asset != pair_info.1 {
return Err(ContractError::Payment(
cw_utils::PaymentError::MissingDenom(coin.denom.to_string()),
));
} else {
coin.amount.into()
};
let asked_asset = pair_info.1.clone();
// Pair of hINJ-INJ on testnet

let swap_astro_msg = pair::ExecuteMsg::Swap {
offer_asset: Asset::native("inj", inj_amount),
offer_asset: Asset::native(&offer_asset, amount),
ask_asset_info: None,
belief_price: None,
max_spread: Some(Decimal::percent(50)),
Expand All @@ -114,84 +147,166 @@ pub mod execute {
let exec_cw20_mint_msg = WasmMsg::Execute {
contract_addr: pair_address.clone(),
msg: to_json_binary(&swap_astro_msg)?,
funds: coins(inj_amount, "inj"),
funds: coins(amount, &offer_asset),
};
assert!(offer_asset == "inj");
let submessage = SubMsg::reply_on_success(exec_cw20_mint_msg, SWAP_REPLY_ID);
let res = Response::new()
.add_submessage(submessage)
.add_attribute("action", "swap")
.add_attribute("pair", pair_address)
.add_attribute("offer_asset", "hinj")
.add_attribute("ask_asset_info", "inj");
.add_attribute("offer_asset", offer_asset)
.add_attribute("ask_asset_info", asked_asset);
Ok(res)
}
pub fn add_supported_pool(
deps: DepsMut,
info: MessageInfo,
pool_address: String,
token_1: String,
token_2: String,
) -> Result<Response, ContractError> {
// Check authorization
if info.sender != STATE.load(deps.storage)?.owner {
return Err(ContractError::Unauthorized {});
}
let pool_info = PoolInfo {
address: Addr::unchecked(pool_address),
token_1: token_1.clone(),
token_2: token_2.clone(),
};
POOL_INFO.save(
deps.storage,
[token_1.clone(), token_2.clone()].join("_").as_str(),
&pool_info,
)?;

Ok(Response::new()
.add_attribute("method", "add_supported_pool")
.add_attribute("pool_address", pool_info.address)
.add_attribute("token 1", token_1)
.add_attribute("token2", token_2))
}
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {}
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GetPair { pool_address } => to_json_binary(&query_pair(deps, env, pool_address)?),
QueryMsg::GetPoolAddr { token_1, token_2 } => {
to_json_binary(&query_pool_addr(deps, env, token_1, token_2)?)
}
}
}

pub mod query {}
pub mod query {
use crate::msg::{GetPairResponse, GetPoolAddrResponse};
use crate::state::POOL_INFO;
use crate::{contract::*, error};
use cosmwasm_std::StdResult;

pub fn query_pair(deps: Deps, env: Env, pool_address: String) -> StdResult<GetPairResponse> {
let pair: Vec<_> = POOL_INFO
.idx
.address
.prefix(pool_address)
.range(deps.storage, None, None, Order::Ascending)
.flatten()
.collect();
if pair.is_empty() {
return Err(StdError::GenericErr {
msg: "pool address not found".to_string(),
});
}
let (token_1, token_2): (String, String) = pair
.first()
.iter()
.map(|pair| (pair.1.token_1.clone(), pair.1.token_2.clone()))
.unzip();
let resp = GetPairResponse { token_1, token_2 };
Ok(resp)
}
pub fn query_pool_addr(
deps: Deps,
env: Env,
token_1: String,
token_2: String,
) -> StdResult<GetPoolAddrResponse> {
let token_1 = token_1.to_lowercase();
let token_2 = token_2.to_lowercase();

let pools = POOL_INFO
.idx
.pair
.prefix((token_1, token_2))
.range(deps.storage, None, None, Order::Ascending)
.flatten()
.collect::<Vec<_>>();

if pools.is_empty() {
return Err(StdError::GenericErr {
msg: "No pool exist for this pair yet".to_string(),
});
}
let pool_addresses = pools
.iter()
.map(|pool_info| pool_info.1.address.to_string().clone())
.collect::<Vec<_>>();
return Ok(GetPoolAddrResponse { pool_addresses });
}
}

#[cfg(test)]
mod tests {
use crate::msg::GetPairResponse;

use super::*;
use cosmwasm_std::coins;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::Addr;
use cw_multi_test::{App, ContractWrapper, Executor};

#[test]
fn proper_initialization() {
let mut deps = mock_dependencies();

let msg = InstantiateMsg {};
let info = mock_info("creator", &coins(1000, "earth"));

// we can just call .unwrap() to assert this was a success
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(0, res.messages.len());
}

#[test]
fn astro_test() {
let mut app = App::new(|router, _, storage| {
router
.bank
.init_balance(storage, &Addr::unchecked("user"), coins(1000, "uinj"))
.unwrap()
});
fn test_add_pool() {
let mut app = App::default();
let owner = Addr::unchecked("owner");
let code = ContractWrapper::new(execute, instantiate, query);
let code_id = app.store_code(Box::new(code));

let addr = app
.instantiate_contract(
code_id,
Addr::unchecked("user"),
owner.clone(),
&InstantiateMsg {},
&[],
"Contract",
None,
)
.unwrap();
let _ = app
.execute_contract(
Addr::unchecked("user"),
addr,
&ExecuteMsg::Astro {
pair_address: "pair".to_string(),
let msg = ExecuteMsg::AddSupportedPool {
pool_address: "pool1".to_string(),
token_1: "inj".to_string(),
token_2: "atom".to_string(),
};
app.execute_contract(owner.clone(), addr.clone(), &msg, &[])
.unwrap();

app.update_block(|b| b.height += 1);

let resp: GetPairResponse = app
.wrap()
.query_wasm_smart(
addr.clone(),
&QueryMsg::GetPair {
pool_address: "pool1".to_string(),
},
&coins(10, "uinj"),
)
.unwrap();
// let wasm = resp.events.iter().find(|ev| ev.ty == "wasm").unwrap();
// assert_eq!(
// wasm.attributes
// .iter()
// .find(|attr| attr.key == "action")
// .unwrap()
// .value,
// "swap"
// );
assert_eq!(
resp,
GetPairResponse {
token_1: "inj".to_string(),
token_2: "atom".to_string()
}
)
}
}
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ pub enum ContractError {
ExceedMintableBlock {},
#[error("Exceed maximum mintable amount")]
ExceedMaximumMintableAmount {},
#[error("Pool not exists")]
PoolNotExist {},
}
Loading

0 comments on commit fed6800

Please sign in to comment.