Skip to content

Improved Priority bidding strategy #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/uniswapx-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ alloy-primitives = "0.8.20"
alloy-sol-types = "0.8.20"
alloy-dyn-abi = "0.8.20"
anyhow = "1.0.70"
hex = "0.4.3"
hex = "0.4.3"
serde = "1.0.168"
44 changes: 42 additions & 2 deletions crates/uniswapx-rs/src/order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use alloy_primitives::I256;
use alloy_primitives::U256;
use alloy_sol_types::sol;
use anyhow::Result;
use serde::Serialize;

use crate::sol_math::MulDiv;

Expand Down Expand Up @@ -151,6 +152,15 @@ pub enum Order {
V3DutchOrder(V3DutchOrder),
}

#[derive(Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(dead_code)]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this removable?

pub enum TradeType {
#[serde(rename = "exactIn")]
ExactIn,
#[serde(rename = "exactOut")]
ExactOut,
}

impl Order {
pub fn encode(&self) -> Vec<u8> {
match self {
Expand All @@ -159,6 +169,36 @@ impl Order {
Order::V3DutchOrder(order) => order.encode_inner(),
}
}

pub fn trade_type(&self) -> TradeType {
match self {
Order::V2DutchOrder(order) => {
if order.baseOutputs.iter().any(|o| o.startAmount == o.endAmount) {
TradeType::ExactOut
} else {
TradeType::ExactIn
}
}
Order::PriorityOrder(order) => {
if order.outputs.iter().any(|o| o.mpsPerPriorityFeeWei == U256::from(0)) {
TradeType::ExactOut
} else {
TradeType::ExactIn
}
}
Order::V3DutchOrder(order) => {
if order.baseOutputs.iter().any(|o| o.curve.relativeAmounts.len() == 0 || *o.curve.relativeAmounts.last().unwrap() == I256::ZERO) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there might be an edge case when there's a series of relative amounts (non-linear) and the last one happens to be 0

TradeType::ExactOut
} else {
TradeType::ExactIn
}
}
}
}

pub fn is_exact_output(&self) -> bool {
matches!(self.trade_type(), TradeType::ExactOut)
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -268,7 +308,7 @@ impl PriorityOrder {
PriorityOrder::abi_encode(self)
}

pub fn resolve(&self, block_number: u64, block_timestamp: u64, block_time_ms: u64, priority_fee: U256, ) -> OrderResolution {
pub fn resolve(&self, block_number: u64, block_timestamp: u64, block_time_ms: u64, priority_fee: U256, min_block_percentage_buffer: u64) -> OrderResolution {
let block_time = block_time_ms / 1000;
let next_block_timestamp = U256::from(block_timestamp) + U256::from(block_time);

Expand All @@ -294,7 +334,7 @@ impl PriorityOrder {
block_timestamp,
block_time_ms
);
let time_buffer_ms = block_time_ms * 1300 / 1000; // TODO: fine tune
let time_buffer_ms = block_time_ms * min_block_percentage_buffer / 100;
if U256::from(current_timestamp_ms() + time_buffer_ms).lt(&target_block_ms) {
return OrderResolution::NotFillableYet(ResolvedOrder { input, outputs });
}
Expand Down
12 changes: 12 additions & 0 deletions src/aws_utils/cloudwatch_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub const EXECUTION_ATTEMPTED_METRIC: &str = "ExecutionAttempted";
pub const EXECUTION_SKIPPED_ALREADY_FILLED_METRIC: &str = "ExecutionSkippedAlreadyFilled";
pub const EXECUTION_SKIPPED_PAST_DEADLINE_METRIC: &str = "ExecutionSkippedPastDeadline";
pub const UNPROFITABLE_METRIC: &str = "Unprofitable";
pub const TARGET_BLOCK_DELTA: &str = "TargetBlockDelta";
pub const REVERT_CODE_METRIC: &str = "RevertCode";

pub enum DimensionName {
Service,
}
Expand Down Expand Up @@ -85,9 +88,12 @@ pub enum CwMetrics {
TxSubmitted(u64),
TxStatusUnknown(u64),
LatestBlock(u64),
RevertCode(u64, String), // chain_id and revert code string

/// Balance for individual address
Balance(String),
// negative is too early, positive is too late
TargetBlockDelta(u64),
}
impl From<CwMetrics> for String {
fn from(metric: CwMetrics) -> Self {
Expand All @@ -111,6 +117,8 @@ impl From<CwMetrics> for String {
}
CwMetrics::Balance(val) => format!("Bal-{}", val),
CwMetrics::LatestBlock(chain_id) => format!("{}-{}", chain_id, LATEST_BLOCK),
CwMetrics::TargetBlockDelta(chain_id) => format!("{}-{}", chain_id, TARGET_BLOCK_DELTA),
CwMetrics::RevertCode(chain_id, code) => format!("{}-{}-{}", chain_id, REVERT_CODE_METRIC, code),
}
}
}
Expand Down Expand Up @@ -167,6 +175,10 @@ pub fn receipt_status_to_metric(status: bool, chain_id: u64) -> CwMetrics {
}
}

pub fn revert_code_to_metric(chain_id: u64, revert_code: String) -> CwMetrics {
CwMetrics::RevertCode(chain_id, revert_code)
}

pub fn build_metric_future(
cloudwatch_client: Option<Arc<CloudWatchClient>>,
dimension_value: DimensionValue,
Expand Down
66 changes: 40 additions & 26 deletions src/collectors/uniswapx_order_collector.rs

Large diffs are not rendered by default.

59 changes: 36 additions & 23 deletions src/collectors/uniswapx_route_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use reqwest::header::ORIGIN;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::{error, info};
use uniswapx_rs::order::{Order, ResolvedOrder};
use uniswapx_rs::order::{Order, ResolvedOrder, TradeType};

use artemis_core::types::{Collector, CollectorStream};
use async_trait::async_trait;
Expand Down Expand Up @@ -40,20 +40,12 @@ pub struct OrderBatchData {
pub orders: Vec<OrderData>,
pub chain_id: u64,
pub amount_in: Uint<256, 4>,
pub amount_out_required: Uint<256, 4>,
pub amount_out: Uint<256, 4>,
pub amount_required: Uint<256, 4>,
pub token_in: String,
pub token_out: String,
}

#[derive(Serialize, Debug)]
#[allow(dead_code)]
enum TradeType {
#[serde(rename = "exactIn")]
ExactIn,
#[serde(rename = "exactOut")]
ExactOut,
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct RoutingApiQuery {
Expand Down Expand Up @@ -82,6 +74,16 @@ pub struct TokenInRoute {
decimals: String,
}

#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
pub struct V4Route {
address: String,
token_in: TokenInRoute,
token_out: TokenInRoute,
fee: String,
}

#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
Expand All @@ -104,6 +106,8 @@ pub struct V2Route {
#[derive(Clone, Debug, Deserialize)]
#[serde(tag = "type")]
pub enum Route {
#[serde(rename = "v4-pool")]
V4(V4Route),
#[serde(rename = "v3-pool")]
V3(V3Route),
#[serde(rename = "v2-pool")]
Expand All @@ -128,6 +132,7 @@ pub struct RouteOrderParams {
pub token_out: String,
pub amount: String,
pub recipient: String,
pub trade_type: TradeType,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -177,13 +182,12 @@ impl UniswapXRouteCollector {
params: RouteOrderParams,
order_hash: String,
) -> Result<OrderRoute> {
// TODO: support exactOutput
let query = RoutingApiQuery {
token_in_address: resolve_address(params.token_in),
token_out_address: resolve_address(params.token_out),
token_in_chain_id: params.chain_id,
token_out_chain_id: params.chain_id,
trade_type: TradeType::ExactIn,
trade_type: params.trade_type,
amount: params.amount,
recipient: params.recipient,
slippage_tolerance: SLIPPAGE_TOLERANCE.to_string(),
Expand All @@ -192,7 +196,7 @@ impl UniswapXRouteCollector {
protocols: "v2,v3,v4,mixed".to_string(),
};

let query_string = serde_qs::to_string(&query).unwrap();
let query_string = serde_qs::to_string(&query)?;
let full_query = format!("{}?{}", ROUTING_API, query_string);
info!("{} - full query: {}", order_hash, full_query);
let client = reqwest::Client::new();
Expand All @@ -202,6 +206,7 @@ impl UniswapXRouteCollector {
.get(format!("{}?{}", ROUTING_API, query_string))
.header(ORIGIN, "https://app.uniswap.org")
.header("x-request-source", "uniswap-web")
.header("x-universal-router-version", "2.0")
.send()
.await
.map_err(|e| anyhow!("Quote request failed with error: {}", e))?;
Expand All @@ -218,10 +223,14 @@ impl UniswapXRouteCollector {
}

match response.status() {
StatusCode::OK => Ok(response
.json::<OrderRoute>()
.await
.map_err(|e| anyhow!("{} - Failed to parse response: {}", order_hash, e))?),
StatusCode::OK => {
let order_route = response
.json::<OrderRoute>()
.await
.map_err(|e| anyhow!("{} - Failed to parse response: {}", order_hash, e))?;
info!("{} - Received route: {:?}", order_hash, order_route);
Ok(order_route)
}
StatusCode::BAD_REQUEST => Err(anyhow!(
"{} - Bad request: {}",
order_hash,
Expand Down Expand Up @@ -292,20 +301,24 @@ impl Collector<RoutedOrder> for UniswapXRouteCollector {

for batch in all_requests {
let order_hash = batch.orders[0].hash.clone();
let OrderBatchData { token_in, token_out, amount_in, .. } = batch.clone();
let OrderBatchData { token_in, token_out, amount_in, amount_out, .. } = batch.clone();
info!(
"{} - Routing order, token in: {}, token out: {}",
"{} - Routing order, token in: {}, token out: {}, amount in: {}, amount out: {}",
order_hash,
token_in, token_out
token_in, token_out, amount_in, amount_out
);

let future = async move {
let route_result = self.route_order(RouteOrderParams {
chain_id: self.chain_id,
token_in: token_in.clone(),
token_out: token_out.clone(),
amount: amount_in.to_string(),
amount: if batch.orders[0].order.is_exact_output() {
amount_out.to_string()
} else {
amount_in.to_string()
},
recipient: self.executor_address.clone(),
trade_type: batch.orders[0].order.trade_type(),
}, order_hash).await;
(batch, route_result)
};
Expand Down
Loading
Loading