diff --git a/contracts/cosmwasm/order/src/constants.rs b/contracts/cosmwasm/order/src/constants.rs index 6e8fb058..4015af39 100644 --- a/contracts/cosmwasm/order/src/constants.rs +++ b/contracts/cosmwasm/order/src/constants.rs @@ -1,4 +1,3 @@ - /// each pair waits at least this amount of blocks before being decided pub const BATCH_EPOCH: u32 = 2; diff --git a/contracts/cosmwasm/order/src/events.rs b/contracts/cosmwasm/order/src/events.rs index a488a17c..c745539b 100644 --- a/contracts/cosmwasm/order/src/events.rs +++ b/contracts/cosmwasm/order/src/events.rs @@ -50,7 +50,10 @@ pub mod order { .add_attribute("order_id", tracking.order_id.to_string()) .add_attribute("amount_taken", tracking.amount_taken.to_string()) .add_attribute("promised", tracking.promised.to_string()) - .add_attribute("meta", "solution is not verified, do not use for big funds".to_string()) + .add_attribute( + "meta", + "solution is not verified, do not use for big funds".to_string(), + ) } } @@ -70,8 +73,8 @@ pub mod solution { ) -> Event { let solution_id = crate::types::solution_id(&(owner.to_string(), ab.clone(), block_added)); let solution_chosen = Event::new("mantis-solution-chosen") - .add_attribute("token_a", ab.clone().0) - .add_attribute("token_b", ab.clone().1) + .add_attribute("token_a", ab.clone().a) + .add_attribute("token_b", ab.clone().b) .add_attribute("solver_address", ctx.info.sender.to_string()) .add_attribute("cow_volume", cow_volume.to_string()) .add_attribute("cross_chain_volume", cow_volume.to_string()) @@ -83,8 +86,8 @@ pub mod solution { pub fn mantis_solution_upserted(ab: &DenomPair, ctx: &ExecCtx<'_>) -> Event { let solution_upserted = Event::new("mantis-solution-upserted") - .add_attribute("token_a", ab.clone().0) - .add_attribute("token_b", ab.clone().1) + .add_attribute("token_a", ab.clone().a) + .add_attribute("token_b", ab.clone().b) .add_attribute("solver_address", ctx.info.sender.to_string()); solution_upserted } diff --git a/contracts/cosmwasm/order/src/lib.rs b/contracts/cosmwasm/order/src/lib.rs index 0de1fba4..85db541d 100644 --- a/contracts/cosmwasm/order/src/lib.rs +++ b/contracts/cosmwasm/order/src/lib.rs @@ -204,23 +204,23 @@ impl OrderContract<'_> { let a_funds = cvm_filled .iter() - .filter(|x| x.tracking.amount_taken.denom == msg.pair.0) + .filter(|x| x.tracking.amount_taken.denom == msg.pair.a) .map(|x| x.tracking.amount_taken.amount) .sum(); let b_funds = cvm_filled .iter() - .filter(|x| x.tracking.amount_taken.denom == msg.pair.1) + .filter(|x| x.tracking.amount_taken.denom == msg.pair.b) .map(|x| x.tracking.amount_taken.amount) .sum(); let funds = vec![ Coin { - denom: msg.pair.0, + denom: msg.pair.a, amount: a_funds, }, Coin { - denom: msg.pair.1, + denom: msg.pair.b, amount: b_funds, }, ]; @@ -242,11 +242,10 @@ impl OrderContract<'_> { let at_least_one = all_orders.first().expect("at least one"); // normalize pair - let mut ab = ( + let mut ab = DenomPair::new( at_least_one.given().denom.clone(), at_least_one.wants().denom.clone(), ); - ab.sort_selection(); // add solution to total solutions let possible_solution = SolutionItem { @@ -258,11 +257,7 @@ impl OrderContract<'_> { self.start_solution(ctx.branch(), ab.clone())?; - let solution_key = ( - ab.clone().0, - ab.clone().1, - ctx.info.sender.clone().to_string(), - ); + let solution_key = (ab.clone(), ctx.info.sender.clone().to_string()); self.solutions .save(ctx.deps.storage, &solution_key, &possible_solution)?; @@ -276,7 +271,7 @@ impl OrderContract<'_> { // get all solution for pair let all_solutions: Result, _> = self .solutions - .prefix(ab.clone()) + .prefix(ab.clone().into()) .range(ctx.deps.storage, None, None, Order::Ascending) .map(|r| r.map(|(_, solution)| solution)) .collect(); @@ -300,7 +295,6 @@ impl OrderContract<'_> { // pick up optimal solution with solves with bank let mut a_in = 0u128; let mut b_in = 0u128; - let (a, b) = ab.clone(); let mut transfers = vec![]; let mut solution_item: SolutionItem = possible_solution; let mut volume = 0u128; @@ -308,12 +302,12 @@ impl OrderContract<'_> { let solution_orders = join_solution_with_orders(&self.orders, &solution.msg, &ctx)?; let a_total_from_orders_in_solution: u128 = solution_orders .iter() - .filter(|x| x.given().denom == a) + .filter(|x| x.given().denom == ab.a) .map(|x: &SolvedOrder| x.given().amount.u128()) .sum(); let b_total_from_orders_in_solution: u128 = solution_orders .iter() - .filter(|x: &&SolvedOrder| x.given().denom == b) + .filter(|x: &&SolvedOrder| x.given().denom == ab.b) .map(|x| x.given().amount.u128()) .sum(); @@ -359,13 +353,13 @@ impl OrderContract<'_> { let cross_chain_b: u128 = all_orders .iter() - .filter(|x| x.given().denom != ab.0) + .filter(|x| x.given().denom != ab.a) .map(|x| x.given_cross_chain().u128()) .sum(); let cross_chain_a: u128 = all_orders .iter() - .filter(|x| x.given().denom != ab.1) + .filter(|x| x.given().denom != ab.b) .map(|x| x.given_cross_chain().u128()) .sum(); @@ -555,7 +549,7 @@ impl OrderContract<'_> { } fn emit_solution_chosen( - ab: (String, String), + ab: DenomPair, ctx: ExecCtx<'_>, transfers: &Vec, volume: u128, diff --git a/contracts/cosmwasm/order/src/ordered_tuple.rs b/contracts/cosmwasm/order/src/ordered_tuple.rs index dcbcf359..d5539aae 100644 --- a/contracts/cosmwasm/order/src/ordered_tuple.rs +++ b/contracts/cosmwasm/order/src/ordered_tuple.rs @@ -1,12 +1,71 @@ +use std::vec; + use cosmwasm_schema::cw_serde; +use cosmwasm_std::{StdError, StdResult}; +use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey}; pub use tuples::*; #[cw_serde] +#[derive(Eq)] pub struct OrderedTuple2 { + // rust does not allows 'r#1' pub a: T, pub b: T, } +impl<'a, T: Clone + Prefixer<'a> + KeyDeserialize + PrimaryKey<'a>> PrimaryKey<'a> + for OrderedTuple2 +{ + type Prefix = T; + + type SubPrefix = T; + + type Suffix = (); + + type SuperSuffix = (T, T); + fn key(&self) -> Vec { + let mut keys = self.a.key(); + keys.extend(self.b.key()); + keys + } +} + +impl<'a, T: Clone + Prefixer<'a> + KeyDeserialize + PrimaryKey<'a>> Prefixer<'a> + for OrderedTuple2 +{ + fn prefix(&self) -> Vec { + let mut res = self.a.prefix(); + res.extend(self.b.prefix()); + res + } +} + +impl< + 'a, + T: Clone + Prefixer<'a> + KeyDeserialize + PrimaryKey<'a> + PartialEq + PartialOrd, + > KeyDeserialize for OrderedTuple2 +{ + type Output = OrderedTuple2; + + #[inline(always)] + fn from_vec(mut value: Vec) -> StdResult { + let mut tu = value.split_off(2); + let t_len = parse_length(&value)?; + let u = tu.split_off(t_len); + + Ok(Self::new(T::from_vec(tu)?, T::from_vec(u)?)) + } +} + +fn parse_length(value: &[u8]) -> StdResult { + Ok(u16::from_be_bytes( + value + .try_into() + .map_err(|_| StdError::generic_err("Could not read 2 byte length"))?, + ) + .into()) +} + impl Tuple for OrderedTuple2 { fn arity(&self) -> usize { 2 @@ -18,9 +77,9 @@ impl Tuple2 for OrderedTuple2 { type Item1 = T; } -impl From<(T, T)> for OrderedTuple2 { - fn from(pair: (T, T)) -> Self { - Self::new(pair.0, pair.1) +impl Into<(T, T)> for OrderedTuple2 { + fn into(self) -> (T, T) { + (self.a, self.b) } } diff --git a/contracts/cosmwasm/order/src/simulator.rs b/contracts/cosmwasm/order/src/simulator.rs index 8951f8da..333391cf 100644 --- a/contracts/cosmwasm/order/src/simulator.rs +++ b/contracts/cosmwasm/order/src/simulator.rs @@ -41,7 +41,7 @@ pub fn simulate_cows_via_bank( // so if not enough was deposited as was taken from original orders, it will fail - so // solver cannot rob the bank - if order.pair().0 == filled_wanted.denom { + if order.pair().a == filled_wanted.denom { a_total_from_orders = a_total_from_orders .checked_sub(cowed.u128()) diff --git a/contracts/cosmwasm/order/src/state.rs b/contracts/cosmwasm/order/src/state.rs index fb07b317..65a5be5d 100644 --- a/contracts/cosmwasm/order/src/state.rs +++ b/contracts/cosmwasm/order/src/state.rs @@ -6,12 +6,12 @@ use crate::*; /// so we need to have several solution per pair to pick one best pub struct SolutionIndexes<'a> { /// (token pair secondary index), (stored item), (stored item full key) - pub pair: MultiIndex<'a, DenomPair, SolutionItem, (Denom, Denom, SolverAddress)>, + pub pair: MultiIndex<'a, DenomPair, SolutionItem, (DenomPair, SolverAddress)>, } -/// (a,b,solver) +/// (DenomPair,SolverAddress) -> SolutionItem pub type SolutionMultiMap<'a> = - IndexedMap<'a, &'a (Denom, Denom, SolverAddress), SolutionItem, SolutionIndexes<'a>>; + IndexedMap<'a, &'a (DenomPair, SolverAddress), SolutionItem, SolutionIndexes<'a>>; impl<'a> IndexList for SolutionIndexes<'a> { fn get_indexes(&'_ self) -> Box> + '_> { @@ -20,8 +20,7 @@ impl<'a> IndexList for SolutionIndexes<'a> { } } -pub fn solutions<'a>( -) -> IndexedMap<'a, &'a (String, String, String), SolutionItem, SolutionIndexes<'a>> { +pub fn solutions<'a>() -> SolutionMultiMap<'a> { let indexes = SolutionIndexes { pair: MultiIndex::new( |_pk: &[u8], d: &SolutionItem| d.pair.clone(), diff --git a/contracts/cosmwasm/order/src/types.rs b/contracts/cosmwasm/order/src/types.rs index d1a518d5..e4afd8d6 100644 --- a/contracts/cosmwasm/order/src/types.rs +++ b/contracts/cosmwasm/order/src/types.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{ensure, BankMsg, Event, StdResult, Uint64, WasmMsg}; use cvm_runtime::{outpost::ExecuteProgramMsg, shared::CvmProgram, AssetId, ExchangeId, NetworkId}; -use crate::prelude::*; +use crate::{ordered_tuple::OrderedTuple2, prelude::*}; pub type OrderId = Uint128; @@ -175,7 +175,6 @@ pub struct SolutionItem { pub owner: Addr, } - impl SolutionItem { pub fn id(&self) -> Vec { solution_id(&(self.owner.to_string(), self.pair.clone(), self.block_added)) @@ -338,12 +337,10 @@ impl SolvedOrder { } pub fn pair(&self) -> DenomPair { - let mut pair = ( + DenomPair::new( self.order.given.denom.clone(), self.order.msg.wants.denom.clone(), - ); - pair.sort_selection(); - pair + ) } pub fn cross_chain(&self) -> u128 { @@ -408,7 +405,7 @@ impl CvmFillResult { } pub type Denom = String; -pub type DenomPair = (Denom, Denom); +pub type DenomPair = OrderedTuple2; pub type SolverAddress = String; pub type CrossChainSolutionKey = (SolverAddress, DenomPair, Block); @@ -419,8 +416,8 @@ pub fn solution_id(id: &CrossChainSolutionKey) -> SolutionHash { use sha2::*; let mut hash = Sha256::new(); hash.update(id.0.as_bytes()); - hash.update(id.1 .0.as_bytes()); - hash.update(id.1 .1.as_bytes()); + hash.update(id.1.a.as_bytes()); + hash.update(id.1.b.as_bytes()); hash.update(id.2.to_be_bytes()); hash.finalize().to_vec() }