Merge branch 'refs/heads/main' into audit/dc/one-transfer-from-only
# Conflicts: # foundry/test/assets/calldata.txt Took 33 minutes
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
## [0.91.0](https://github.com/propeller-heads/tycho-execution/compare/0.90.0...0.91.0) (2025-05-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **curve:** Get coin indexes from static attributes ([fcaacf2](https://github.com/propeller-heads/tycho-execution/commit/fcaacf21fbf8a8bc08a395fbb10295bb00e6ab15))
|
||||
|
||||
## [0.90.0](https://github.com/propeller-heads/tycho-execution/compare/0.89.0...0.90.0) (2025-05-15)
|
||||
|
||||
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -4469,7 +4469,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tycho-execution"
|
||||
version = "0.90.0"
|
||||
version = "0.91.0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tycho-execution"
|
||||
version = "0.90.0"
|
||||
version = "0.91.0"
|
||||
edition = "2021"
|
||||
description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors."
|
||||
repository = "https://github.com/propeller-heads/tycho-execution"
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
"vault_address": "0xba12222222228d8ba445958a75a0704d566bf2c8"
|
||||
},
|
||||
"vm:curve": {
|
||||
"native_token_address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
||||
"meta_registry_address": "0xF98B45FA17DE75FB1aD0e7aFD971b0ca00e379fC"
|
||||
"native_token_address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
|
||||
}
|
||||
},
|
||||
"base": {},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::io;
|
||||
use std::{io, str::Utf8Error};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -33,3 +33,9 @@ impl From<serde_json::Error> for EncodingError {
|
||||
EncodingError::FatalError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for EncodingError {
|
||||
fn from(err: Utf8Error) -> Self {
|
||||
EncodingError::FatalError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1573,6 +1573,11 @@ mod tests {
|
||||
.to_vec(),
|
||||
),
|
||||
);
|
||||
attrs.insert(
|
||||
"coins".into(),
|
||||
Bytes::from_str("0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d")
|
||||
.unwrap(),
|
||||
);
|
||||
attrs
|
||||
},
|
||||
..Default::default()
|
||||
@@ -1737,6 +1742,11 @@ mod tests {
|
||||
.to_vec(),
|
||||
),
|
||||
);
|
||||
attrs.insert(
|
||||
"coins".into(),
|
||||
Bytes::from_str("0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d")
|
||||
.unwrap(),
|
||||
);
|
||||
attrs
|
||||
},
|
||||
..Default::default()
|
||||
@@ -2662,8 +2672,9 @@ mod tests {
|
||||
"0x98ee851a00abee0d95d08cf4ca2bdce32aeaaf7f"
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
),
|
||||
)]);
|
||||
)),
|
||||
("coins".to_string(), Bytes::from_str("0x5b22307863303261616133396232323366653864306130653563346632376561643930383363373536636332222c22307835356330386361353234393765326631353334623539653239313762663532346434373635323537225d").unwrap()),
|
||||
]);
|
||||
|
||||
let component = ProtocolComponent {
|
||||
id: String::from("0x77146B0a1d08B6844376dF6d9da99bA7F1b19e71"),
|
||||
@@ -2726,7 +2737,8 @@ mod tests {
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
),
|
||||
)]);
|
||||
),
|
||||
("coins".to_string(), Bytes::from_str("0x5b22307865656565656565656565656565656565656565656565656565656565656565656565656565656565222c22307861653761623936353230646533613138653565313131623565616162303935333132643766653834225d").unwrap()),]);
|
||||
|
||||
let component = ProtocolComponent {
|
||||
id: String::from("0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"),
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use alloy::{
|
||||
providers::Provider,
|
||||
rpc::types::{TransactionInput, TransactionRequest},
|
||||
};
|
||||
use alloy_primitives::{Address, Bytes as AlloyBytes, TxKind, U256, U8};
|
||||
use alloy_primitives::{Address, Bytes as AlloyBytes, U8};
|
||||
use alloy_sol_types::SolValue;
|
||||
use tokio::task::block_in_place;
|
||||
use serde_json::from_str;
|
||||
use tycho_common::Bytes;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::{
|
||||
approvals::protocol_approvals_manager::ProtocolApprovalsManager,
|
||||
utils,
|
||||
utils::{
|
||||
bytes_to_address, encode_input, get_runtime, get_static_attribute, pad_to_fixed_size,
|
||||
},
|
||||
utils::{bytes_to_address, get_static_attribute, pad_to_fixed_size},
|
||||
},
|
||||
models::{Chain, EncodingContext, Swap},
|
||||
swap_encoder::SwapEncoder,
|
||||
@@ -375,10 +368,8 @@ impl SwapEncoder for EkuboSwapEncoder {
|
||||
#[derive(Clone)]
|
||||
pub struct CurveSwapEncoder {
|
||||
executor_address: String,
|
||||
meta_registry_address: String,
|
||||
native_token_curve_address: String,
|
||||
native_token_address: Bytes,
|
||||
wrapped_native_token_address: Bytes,
|
||||
}
|
||||
|
||||
impl CurveSwapEncoder {
|
||||
@@ -416,68 +407,25 @@ impl CurveSwapEncoder {
|
||||
|
||||
fn get_coin_indexes(
|
||||
&self,
|
||||
pool_id: Address,
|
||||
swap: Swap,
|
||||
token_in: Address,
|
||||
token_out: Address,
|
||||
) -> Result<(U8, U8), EncodingError> {
|
||||
let (handle, _runtime) = get_runtime()?;
|
||||
let client = block_in_place(|| handle.block_on(utils::get_client()))?;
|
||||
let args = (pool_id, token_in, token_out);
|
||||
let data = encode_input("get_coin_indices(address,address,address)", args.abi_encode());
|
||||
let tx = TransactionRequest {
|
||||
to: Some(TxKind::from(Address::from_str(&self.meta_registry_address).map_err(
|
||||
|_| EncodingError::FatalError("Invalid Curve meta registry address".to_string()),
|
||||
)?)),
|
||||
input: TransactionInput {
|
||||
input: Some(alloy_primitives::Bytes::from(data)),
|
||||
data: None,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let output = block_in_place(|| handle.block_on(async { client.call(&tx).await }));
|
||||
type ResponseType = (U256, U256, bool);
|
||||
|
||||
match output {
|
||||
Ok(response) => {
|
||||
let (i_256, j_256, _): ResponseType = ResponseType::abi_decode(&response, true)
|
||||
.map_err(|_| {
|
||||
EncodingError::FatalError(
|
||||
"Failed to decode response when getting coin indexes on a curve pool"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
let i = U8::from(i_256);
|
||||
let j = U8::from(j_256);
|
||||
Ok((i, j))
|
||||
}
|
||||
Err(err) => {
|
||||
// Temporary until we get the coin indexes from the indexer
|
||||
// This is because some curve pools hold ETH but the coin is defined as WETH
|
||||
// Our indexer reports this pool as holding ETH but then here we need to use WETH
|
||||
// This is valid only for some pools, that's why we are doing the trial and error
|
||||
// approach
|
||||
let native_token_curve_address =
|
||||
Address::from_str(&self.native_token_curve_address).map_err(|_| {
|
||||
EncodingError::FatalError(
|
||||
"Invalid Curve native token curve address".to_string(),
|
||||
)
|
||||
})?;
|
||||
if token_in != native_token_curve_address && token_out != native_token_curve_address
|
||||
{
|
||||
Err(EncodingError::RecoverableError(format!(
|
||||
"Curve meta registry call failed with error: {err}"
|
||||
)))
|
||||
} else {
|
||||
let wrapped_token = bytes_to_address(&self.wrapped_native_token_address)?;
|
||||
let (i, j) = if token_in == native_token_curve_address {
|
||||
self.get_coin_indexes(pool_id, wrapped_token, token_out)?
|
||||
} else {
|
||||
self.get_coin_indexes(pool_id, token_in, wrapped_token)?
|
||||
};
|
||||
Ok((i, j))
|
||||
}
|
||||
}
|
||||
}
|
||||
let coins_bytes = get_static_attribute(&swap, "coins")?;
|
||||
let coins: Vec<Address> = from_str(std::str::from_utf8(&coins_bytes)?)?;
|
||||
let i = coins
|
||||
.iter()
|
||||
.position(|&addr| addr == token_in)
|
||||
.ok_or(EncodingError::FatalError(format!(
|
||||
"Token in address {token_in} not found in curve pool coins"
|
||||
)))?;
|
||||
let j = coins
|
||||
.iter()
|
||||
.position(|&addr| addr == token_out)
|
||||
.ok_or(EncodingError::FatalError(format!(
|
||||
"Token in address {token_in} not found in curve pool coins"
|
||||
)))?;
|
||||
Ok((U8::from(i), U8::from(j)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,17 +444,9 @@ impl SwapEncoder for CurveSwapEncoder {
|
||||
"Missing native token curve address in config".to_string(),
|
||||
))?
|
||||
.to_string();
|
||||
let meta_registry_address = config
|
||||
.get("meta_registry_address")
|
||||
.ok_or(EncodingError::FatalError(
|
||||
"Missing meta registry address in config".to_string(),
|
||||
))?
|
||||
.to_string();
|
||||
Ok(Self {
|
||||
executor_address,
|
||||
meta_registry_address,
|
||||
native_token_address: chain.native_token()?,
|
||||
wrapped_native_token_address: chain.wrapped_token()?,
|
||||
native_token_curve_address,
|
||||
})
|
||||
}
|
||||
@@ -565,7 +505,7 @@ impl SwapEncoder for CurveSwapEncoder {
|
||||
let pool_type =
|
||||
self.get_pool_type(&pool_address.to_string(), &factory_address.to_string())?;
|
||||
|
||||
let (i, j) = self.get_coin_indexes(component_address, token_in, token_out)?;
|
||||
let (i, j) = self.get_coin_indexes(swap, token_in, token_out)?;
|
||||
|
||||
let args = (
|
||||
token_in,
|
||||
@@ -1250,43 +1190,36 @@ mod tests {
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
"0x5500307Bcf134E5851FB4D7D8D1Dc556dCdB84B4",
|
||||
"0xdA16Cf041E2780618c49Dbae5d734B89a6Bac9b3",
|
||||
"0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
||||
1,
|
||||
0
|
||||
)]
|
||||
#[case(
|
||||
"0xef484de8C07B6e2d732A92B5F78e81B38f99f95E",
|
||||
"0x5b22307838363533373733363730353435313665313730313463636465643165376438313465646339636534222c22307861353538386637636466353630383131373130613264383264336339633939373639646231646362225d",
|
||||
"0x865377367054516e17014CcdED1e7d814EDC9ce4",
|
||||
"0xA5588F7cdf560811710A2D82D3C9c99769DB1Dcb",
|
||||
0,
|
||||
1
|
||||
)]
|
||||
#[case(
|
||||
"0xA5407eAE9Ba41422680e2e00537571bcC53efBfD",
|
||||
"0x5b22307836623137353437346538393039346334346461393862393534656564656163343935323731643066222c22307861306238363939316336323138623336633164313964346132653965623063653336303665623438222c22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307835376162316563323864313239373037303532646634646634313864353861326434366435663531225d",
|
||||
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"0x57Ab1ec28D129707052df4dF418D58a2D46d5f51",
|
||||
1,
|
||||
3
|
||||
)]
|
||||
#[case(
|
||||
"0xD51a44d3FaE010294C616388b506AcdA1bfAAE46",
|
||||
"0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d",
|
||||
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
|
||||
2,
|
||||
1
|
||||
)]
|
||||
#[case(
|
||||
"0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B",
|
||||
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"0x5b22307861306238363939316336323138623336633164313964346132653965623063653336303665623438222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307865656565656565656565656565656565656565656565656565656565656565656565656565656565225d",
|
||||
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
2,
|
||||
0
|
||||
)]
|
||||
// Pool that holds ETH but coin is WETH
|
||||
#[case(
|
||||
"0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B",
|
||||
"0x5b22307861306238363939316336323138623336633164313964346132653965623063653336303665623438222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307865656565656565656565656565656565656565656565656565656565656565656565656565656565225d",
|
||||
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
||||
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
2,
|
||||
@@ -1294,19 +1227,32 @@ mod tests {
|
||||
)]
|
||||
// Pool that holds ETH but coin is WETH
|
||||
#[case(
|
||||
"0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B",
|
||||
"0x5b22307861306238363939316336323138623336633164313964346132653965623063653336303665623438222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307865656565656565656565656565656565656565656565656565656565656565656565656565656565225d",
|
||||
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
||||
0,
|
||||
2
|
||||
)]
|
||||
fn test_curve_get_coin_indexes(
|
||||
#[case] pool: &str,
|
||||
#[case] coins: &str,
|
||||
#[case] token_in: &str,
|
||||
#[case] token_out: &str,
|
||||
#[case] expected_i: u64,
|
||||
#[case] expected_j: u64,
|
||||
) {
|
||||
let mut static_attributes: HashMap<String, Bytes> = HashMap::new();
|
||||
static_attributes.insert("coins".into(), Bytes::from_str(coins).unwrap());
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
id: "pool-id".into(),
|
||||
protocol_system: String::from("vm:curve"),
|
||||
static_attributes,
|
||||
..Default::default()
|
||||
},
|
||||
token_in: Bytes::from(token_in),
|
||||
token_out: Bytes::from(token_out),
|
||||
split: 0f64,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::default(),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
@@ -1315,7 +1261,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let (i, j) = encoder
|
||||
.get_coin_indexes(
|
||||
Address::from_str(pool).unwrap(),
|
||||
swap,
|
||||
Address::from_str(token_in).unwrap(),
|
||||
Address::from_str(token_out).unwrap(),
|
||||
)
|
||||
@@ -1335,6 +1281,7 @@ mod tests {
|
||||
.to_vec(),
|
||||
),
|
||||
);
|
||||
static_attributes.insert("coins".into(), Bytes::from_str("0x5b22307836623137353437346538393039346334346461393862393534656564656163343935323731643066222c22307861306238363939316336323138623336633164313964346132653965623063653336303665623438222c22307864616331376639353864326565353233613232303632303639393435393763313364383331656337225d").unwrap());
|
||||
let curve_tri_pool = ProtocolComponent {
|
||||
id: String::from("0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7"),
|
||||
protocol_system: String::from("vm:curve"),
|
||||
@@ -1405,6 +1352,7 @@ mod tests {
|
||||
.to_vec(),
|
||||
),
|
||||
);
|
||||
static_attributes.insert("coins".into(), Bytes::from_str("0x5b22307834633965646435383532636439303566303836633735396538333833653039626666316536386233222c22307861306238363939316336323138623336633164313964346132653965623063653336303665623438225d").unwrap());
|
||||
let curve_pool = ProtocolComponent {
|
||||
id: String::from("0x02950460E2b9529D0E00284A5fA2d7bDF3fA4d72"),
|
||||
protocol_system: String::from("vm:curve"),
|
||||
@@ -1476,6 +1424,7 @@ mod tests {
|
||||
.to_vec(),
|
||||
),
|
||||
);
|
||||
static_attributes.insert("coins".into(), Bytes::from_str("0x5b22307865656565656565656565656565656565656565656565656565656565656565656565656565656565222c22307861653761623936353230646533613138653565313131623565616162303935333132643766653834225d").unwrap());
|
||||
let curve_pool = ProtocolComponent {
|
||||
id: String::from("0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"),
|
||||
protocol_system: String::from("vm:curve"),
|
||||
|
||||
Reference in New Issue
Block a user