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
107 changes: 53 additions & 54 deletions crates/e2e/tests/e2e/ethflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,7 @@ use {
trade::Trade,
},
number::nonzero::U256 as NonZeroU256,
refunder::{
ethflow_order::EthflowOrder,
refund_service::{INVALIDATED_OWNER, NO_OWNER},
},
refunder::refund_service::{INVALIDATED_OWNER, NO_OWNER},
reqwest::Client,
shared::signature_validator::check_erc1271_result,
};
Expand Down Expand Up @@ -278,16 +275,16 @@ async fn eth_flow_without_quote(web3: Web3) {
.await
.unwrap()
+ 3600;
let ethflow_order = ExtendedEthFlowOrder(EthflowOrder {
buy_token: dai.address().into_legacy(),
sell_amount: to_wei(1),
buy_amount: 1.into(),
valid_to,
partially_fillable: false,
quote_id: 0,
fee_amount: 0.into(),
receiver: H160([0x42; 20]),
app_data: Default::default(),
let ethflow_order = ExtendedEthFlowOrder(CoWSwapEthFlow::EthFlowOrder::Data {
buyToken: *dai.address(),
sellAmount: eth(1),
buyAmount: alloy::primitives::U256::ONE,
validTo: valid_to,
partiallyFillable: false,
quoteId: 0,
feeAmount: alloy::primitives::U256::ZERO,
receiver: Address::from_slice(&[0x42; 20]),
appData: Default::default(),
});

let ethflow_contract = onchain.contracts().ethflows.first().unwrap();
Expand Down Expand Up @@ -447,7 +444,7 @@ async fn submit_order(
assert!(result.status()); // success
assert_eq!(
ethflow_order.status(contracts, ethflow_contract).await,
EthFlowOrderOnchainStatus::Created(user.address(), ethflow_order.0.valid_to)
EthFlowOrderOnchainStatus::Created(user.address(), ethflow_order.0.validTo)
);
}

Expand Down Expand Up @@ -511,17 +508,15 @@ async fn test_trade_availability_in_api(
async fn test_order_was_settled(ethflow_order: &ExtendedEthFlowOrder, onchain: &OnchainComponents) {
wait_for_condition(TIMEOUT, || async {
onchain.mint_block().await;
let buy_token = ERC20Mintable::Instance::new(
ethflow_order.0.buy_token.into_alloy(),
onchain.web3().alloy.clone(),
);
let buy_token =
ERC20Mintable::Instance::new(ethflow_order.0.buyToken, onchain.web3().alloy.clone());
let receiver_buy_token_balance = buy_token
.balanceOf(ethflow_order.0.receiver.into_alloy())
.balanceOf(ethflow_order.0.receiver)
.call()
.await
.expect("Unable to get token balance");

receiver_buy_token_balance >= ethflow_order.0.buy_amount.into_alloy()
receiver_buy_token_balance >= ethflow_order.0.buyAmount
})
.await
.unwrap();
Expand Down Expand Up @@ -617,7 +612,7 @@ async fn test_order_parameters(
assert_eq!(
response.metadata.ethflow_data,
Some(EthflowData {
user_valid_to: order.0.valid_to as i64,
user_valid_to: order.0.validTo as i64,
refund_tx_hash: None,
})
);
Expand Down Expand Up @@ -645,21 +640,24 @@ async fn test_order_parameters(
assert_eq!(response.interactions.pre[0].call_data, WRAP_ALL_SELECTOR);
}

pub struct ExtendedEthFlowOrder(pub EthflowOrder);
pub struct ExtendedEthFlowOrder(pub CoWSwapEthFlow::EthFlowOrder::Data);

impl ExtendedEthFlowOrder {
pub fn from_quote(quote_response: &OrderQuoteResponse, valid_to: u32) -> Self {
let quote = &quote_response.quote;
ExtendedEthFlowOrder(EthflowOrder {
buy_token: quote.buy_token,
receiver: quote.receiver.expect("eth-flow order without receiver"),
sell_amount: quote.sell_amount,
buy_amount: quote.buy_amount,
app_data: ethcontract::Bytes(quote.app_data.hash().0),
fee_amount: 0.into(),
valid_to, // note: valid to in the quote is always unlimited
partially_fillable: quote.partially_fillable,
quote_id: quote_response.id.expect("No quote id"),
ExtendedEthFlowOrder(CoWSwapEthFlow::EthFlowOrder::Data {
buyToken: quote.buy_token.into_alloy(),
receiver: quote
.receiver
.expect("eth-flow order without receiver")
.into_alloy(),
sellAmount: quote.sell_amount.into_alloy(),
buyAmount: quote.buy_amount.into_alloy(),
appData: quote.app_data.hash().0.into(),
feeAmount: alloy::primitives::U256::ZERO,
validTo: valid_to, // note: valid to in the quote is always unlimited
partiallyFillable: quote.partially_fillable,
quoteId: quote_response.id.expect("No quote id"),
})
}

Expand All @@ -673,13 +671,13 @@ impl ExtendedEthFlowOrder {
OrderBuilder::default()
.with_kind(OrderKind::Sell)
.with_sell_token(weth.address().into_legacy())
.with_sell_amount(self.0.sell_amount)
.with_fee_amount(self.0.fee_amount)
.with_receiver(Some(self.0.receiver))
.with_buy_token(self.0.buy_token)
.with_buy_amount(self.0.buy_amount)
.with_sell_amount(self.0.sellAmount.into_legacy())
.with_fee_amount(self.0.feeAmount.into_legacy())
.with_receiver(Some(self.0.receiver.into_legacy()))
.with_buy_token(self.0.buyToken.into_legacy())
.with_buy_amount(self.0.buyAmount.into_legacy())
.with_valid_to(u32::MAX)
.with_app_data(self.0.app_data.0)
.with_app_data(self.0.appData.0)
.with_class(OrderClass::Market) // Eth-flow orders only support market orders at this point in time
.with_eip1271(ethflow_contract.address().into_legacy(), hex!("").into())
.build()
Expand All @@ -690,8 +688,9 @@ impl ExtendedEthFlowOrder {
if slippage > MAX_BASE_POINT {
panic!("Slippage must be specified in base points");
}
ExtendedEthFlowOrder(EthflowOrder {
buy_amount: self.0.buy_amount * (MAX_BASE_POINT - slippage) / MAX_BASE_POINT,
ExtendedEthFlowOrder(CoWSwapEthFlow::EthFlowOrder::Data {
buyAmount: self.0.buyAmount * alloy::primitives::U256::from(MAX_BASE_POINT - slippage)
/ alloy::primitives::U256::from(MAX_BASE_POINT),
..self.0
})
}
Expand Down Expand Up @@ -746,8 +745,8 @@ impl ExtendedEthFlowOrder {
ethflow_contract: &CoWSwapEthFlow::Instance,
) -> TransactionReceipt {
ethflow_contract
.createOrder(self.0.clone().into())
.value((self.0.sell_amount + self.0.fee_amount).into_alloy())
.createOrder(self.0.clone())
.value(self.0.sellAmount + self.0.feeAmount)
.from(owner)
.send()
.await
Expand All @@ -763,7 +762,7 @@ impl ExtendedEthFlowOrder {
ethflow_contract: &CoWSwapEthFlow::Instance,
) {
ethflow_contract
.invalidateOrder(self.0.clone().into())
.invalidateOrder(self.0.clone())
.from(sender)
.send_and_watch()
.await
Expand Down Expand Up @@ -884,16 +883,16 @@ async fn eth_flow_zero_buy_amount(web3: Web3) {
.await
.unwrap()
+ 3600;
let ethflow_order = ExtendedEthFlowOrder(EthflowOrder {
buy_token: dai.address().into_legacy(),
sell_amount: to_wei(1),
buy_amount: buy_amount.into(),
valid_to,
partially_fillable: false,
quote_id: 0,
fee_amount: 0.into(),
receiver: H160([0x42; 20]),
app_data: Default::default(),
let ethflow_order = ExtendedEthFlowOrder(CoWSwapEthFlow::EthFlowOrder::Data {
buyToken: *dai.address(),
sellAmount: eth(1),
buyAmount: alloy::primitives::U256::from(buy_amount),
validTo: valid_to,
partiallyFillable: false,
quoteId: 0,
feeAmount: alloy::primitives::U256::ZERO,
receiver: Address::from_slice(&[0x42; 20]),
appData: Default::default(),
});

let ethflow_contract = onchain.contracts().ethflows.first().unwrap();
Expand Down
27 changes: 27 additions & 0 deletions crates/number/src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,33 @@ pub fn big_decimal_to_u256(big_decimal: &BigDecimal) -> Option<U256> {
big_int_to_u256(&big_int).ok()
}

pub mod alloy {
use {
alloy::primitives::U256,
anyhow::{Result, ensure},
bigdecimal::{BigDecimal, num_bigint::ToBigInt},
num::{BigInt, BigUint, bigint::Sign},
};
pub fn big_uint_to_u256(input: &BigUint) -> Result<U256> {
let bytes = input.to_bytes_be();
ensure!(bytes.len() <= 32, "too large");
Ok(U256::from_be_slice(&bytes))
}

pub fn big_int_to_u256(input: &BigInt) -> Result<U256> {
ensure!(input.sign() != Sign::Minus, "negative");
big_uint_to_u256(input.magnitude())
}

pub fn big_decimal_to_u256(big_decimal: &BigDecimal) -> Option<U256> {
if !big_decimal.is_integer() {
return None;
}
let big_int = big_decimal.to_bigint()?;
big_int_to_u256(&big_int).ok()
}
}

pub fn rational_to_big_decimal<T>(value: &Ratio<T>) -> BigDecimal
where
T: Clone,
Expand Down
53 changes: 0 additions & 53 deletions crates/refunder/src/ethflow_order.rs

This file was deleted.

1 change: 0 additions & 1 deletion crates/refunder/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
pub mod arguments;
pub mod ethflow_order;
pub mod refund_service;
pub mod submitter;

Expand Down
31 changes: 21 additions & 10 deletions crates/refunder/src/refund_service.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use {
super::ethflow_order::{EthflowOrder, order_to_ethflow_data},
crate::submitter::Submitter,
alloy::{
network::TxSigner,
Expand All @@ -15,6 +14,7 @@ use {
ethcontract::H256,
ethrpc::{Web3, block_stream::timestamp_of_current_block_in_seconds},
futures::{StreamExt, stream},
number::conversions::alloy::big_decimal_to_u256,
sqlx::PgPool,
std::collections::HashMap,
};
Expand Down Expand Up @@ -176,7 +176,10 @@ impl RefundService {
to_be_refunded_uids
}

async fn get_ethflow_data_from_db(&self, uid: &OrderUid) -> Result<EthflowOrder> {
async fn get_ethflow_data_from_db(
&self,
uid: &OrderUid,
) -> Result<CoWSwapEthFlow::EthFlowOrder::Data> {
let mut ex = self.db.acquire().await.context("acquire")?;
let order = read_db_order(&mut ex, uid)
.await
Expand All @@ -186,7 +189,19 @@ impl RefundService {
.await
.context("read ethflow order")?
.context("missing ethflow order")?;
Ok(order_to_ethflow_data(order, ethflow_order))

Ok(CoWSwapEthFlow::EthFlowOrder::Data {
buyToken: Address::from(order.buy_token.0),
// ethflow orders have always a receiver. It's enforced by the contract.
receiver: Address::from(order.receiver.unwrap().0),
sellAmount: big_decimal_to_u256(&order.sell_amount).unwrap(),
buyAmount: big_decimal_to_u256(&order.buy_amount).unwrap(),
appData: order.app_data.0.into(),
feeAmount: big_decimal_to_u256(&order.fee_amount).unwrap(),
validTo: ethflow_order.valid_to as u32,
partiallyFillable: order.partially_fillable,
quoteId: 0i64, // quoteId is not important for refunding and will be ignored
})
}

async fn send_out_refunding_tx(
Expand Down Expand Up @@ -217,13 +232,9 @@ impl RefundService {
let encoded_ethflow_orders: Vec<_> = stream::iter(futures)
.buffer_unordered(10)
.filter_map(|result| async {
match result {
Ok(order) => Some(CoWSwapEthFlow::EthFlowOrder::Data::from(order)),
Err(err) => {
tracing::error!(?err, "failed to get data from db");
None
}
}
result
.inspect_err(|err| tracing::error!(?err, "failed to get data from db"))
.ok()
})
.collect()
.await;
Expand Down
Loading