refactor(substreams): Update ambient Substreams

This commit is contained in:
zizou
2024-10-14 18:09:17 +02:00
parent aff76f8cc7
commit 42f2f45aa7
31 changed files with 1567 additions and 724 deletions

View File

@@ -0,0 +1,83 @@
use anyhow::{anyhow, bail};
use crate::utils::{decode_flows_from_output, encode_pool_hash};
use ethabi::{decode, ParamType};
use hex_literal::hex;
use substreams_ethereum::pb::eth::v2::Call;
pub const AMBIENT_HOTPROXY_CONTRACT: [u8; 20] = hex!("37e00522Ce66507239d59b541940F99eA19fF81F");
pub const USER_CMD_HOTPROXY_FN_SIG: [u8; 4] = hex!("f96dc788");
pub const SWAP_ABI_HOTPROXY_INPUT: &[ParamType] = &[
ParamType::Address, // base
ParamType::Address, // quote
ParamType::Uint(256), // pool index
// isBuy - if true the direction of the swap is for the user to send base
// tokens and receive back quote tokens.
ParamType::Bool,
ParamType::Bool, // inBaseQty
ParamType::Uint(128), //qty
ParamType::Uint(16), // poolTip
ParamType::Uint(128), // limitPrice
ParamType::Uint(128), // minOut
ParamType::Uint(8), // reserveFlags
];
const USER_CMD_EXTERNAL_ABI: &[ParamType] = &[
ParamType::Bytes, // userCmd
];
pub fn decode_direct_swap_hotproxy_call(
call: &Call,
) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> {
if let Ok(external_cmd) = decode(USER_CMD_EXTERNAL_ABI, &call.input[4..]) {
let input_bytes = external_cmd[0]
.to_owned()
.into_bytes() // Convert Bytes32 to Vec<u8>
.ok_or_else(|| anyhow!("Failed to hotproxy userCmd input data.".to_string()))?;
if let Ok(input_params) = decode(SWAP_ABI_HOTPROXY_INPUT, &input_bytes) {
let base_token = input_params[0]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!(
"Failed to convert base token to address for direct swap hotproxy call: {:?}",
&input_params[0]
)
})?
.to_fixed_bytes()
.to_vec();
let quote_token = input_params[1]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!(
"Failed to convert quote token to address for direct swap hotproxy call: {:?}",
&input_params[1]
)
})?
.to_fixed_bytes()
.to_vec();
let mut pool_index_buf = [0u8; 32];
input_params[2]
.to_owned()
.into_uint()
.ok_or_else(|| {
anyhow!("Failed to convert pool index to u32 for direct swap hotproxy call"
.to_string())
})?
.to_big_endian(&mut pool_index_buf);
let pool_index = pool_index_buf.to_vec();
let (base_flow, quote_flow) = decode_flows_from_output(call)?;
let pool_hash = encode_pool_hash(base_token, quote_token, pool_index);
Ok((pool_hash, base_flow, quote_flow))
} else {
bail!("Failed to decode hotproxy swap call internap inputs.".to_string());
}
} else {
bail!("Failed to decode hotproxy swap call external input.".to_string());
}
}

View File

@@ -0,0 +1,106 @@
use anyhow::{anyhow, bail};
use crate::utils::{decode_flows_from_output, encode_pool_hash};
use ethabi::{decode, ParamType};
use hex_literal::hex;
use substreams_ethereum::pb::eth::v2::Call;
pub const AMBIENT_KNOCKOUT_CONTRACT: [u8; 20] = hex!("7F5D75AdE75646919c923C98D53E9Cc7Be7ea794");
pub const USER_CMD_KNOCKOUT_FN_SIG: [u8; 4] = hex!("f96dc788");
// Represents the ABI of any cmd which is not mint or burn
const KNOCKOUT_INTERNAL_OTHER_CMD_ABI: &[ParamType] = &[
ParamType::Uint(8),
ParamType::Address, // base
ParamType::Address, // quote
ParamType::Uint(256), // poolIdx
ParamType::Int(24),
ParamType::Int(24),
ParamType::Bool,
ParamType::Uint(8),
ParamType::Uint(256),
ParamType::Uint(256),
ParamType::Uint(32),
];
const KNOCKOUT_INTERNAL_MINT_BURN_ABI: &[ParamType] = &[
ParamType::Uint(8),
ParamType::Address, // base
ParamType::Address, // quote
ParamType::Uint(256), // poolIdx
ParamType::Int(24),
ParamType::Int(24),
ParamType::Bool,
ParamType::Uint(8),
ParamType::Uint(256),
ParamType::Uint(256),
ParamType::Uint(128),
ParamType::Bool,
];
const KNOCKOUT_EXTERNAL_ABI: &[ParamType] = &[
ParamType::Bytes, // userCmd
];
pub fn decode_knockout_call(
call: &Call,
) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> {
if let Ok(external_cmd) = decode(KNOCKOUT_EXTERNAL_ABI, &call.input[4..]) {
let input_data = external_cmd[0]
.to_owned()
.into_bytes() // Convert Bytes32 to Vec<u8>
.ok_or_else(|| anyhow!("Failed to Knockout userCmd input data.".to_string()))?;
let code = input_data[31];
let is_mint = code == 91;
let is_burn = code == 92;
let abi = if is_mint || is_burn {
KNOCKOUT_INTERNAL_MINT_BURN_ABI
} else {
KNOCKOUT_INTERNAL_OTHER_CMD_ABI
};
if let Ok(mint_burn_inputs) = decode(abi, &input_data) {
let base_token = mint_burn_inputs[1]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!(
"Failed to convert base token to address for knockout call: {:?}",
&mint_burn_inputs[1]
)
})?
.to_fixed_bytes()
.to_vec();
let quote_token = mint_burn_inputs[2]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!(
"Failed to convert quote token to address for knockout call: {:?}",
&mint_burn_inputs[2]
)
})?
.to_fixed_bytes()
.to_vec();
let mut pool_index_buf = [0u8; 32];
mint_burn_inputs[3]
.to_owned()
.into_uint()
.ok_or_else(|| {
anyhow!("Failed to convert pool index to bytes for knockout call".to_string())
})?
.to_big_endian(&mut pool_index_buf);
let pool_index = pool_index_buf.to_vec();
let (base_flow, quote_flow) = decode_flows_from_output(call)?;
let pool_hash = encode_pool_hash(base_token, quote_token, pool_index);
Ok((pool_hash, base_flow, quote_flow))
} else {
bail!("Failed to decode knockout call outputs.".to_string());
}
} else {
bail!("Failed to decode inputs for knockout call.".to_string());
}
}

View File

@@ -0,0 +1,170 @@
use anyhow::{anyhow, bail};
use tycho_substreams::models::{
Attribute, ChangeType, FinancialType, ImplementationType, ProtocolComponent, ProtocolType,
Transaction,
};
use crate::utils::{decode_flows_from_output, encode_pool_hash};
use ethabi::{decode, ParamType};
use hex_literal::hex;
use substreams_ethereum::pb::eth::v2::Call;
pub const AMBIENT_CONTRACT: [u8; 20] = hex!("aaaaaaaaa24eeeb8d57d431224f73832bc34f688");
pub const USER_CMD_FN_SIG: [u8; 4] = hex!("a15112f9");
const USER_CMD_EXTERNAL_ABI: &[ParamType] = &[
// index of the proxy sidecar the command is being called on
ParamType::Uint(16),
// call data for internal UserCmd method
ParamType::Bytes,
];
const USER_CMD_INTERNAL_ABI: &[ParamType] = &[
ParamType::Uint(8), // command
ParamType::Address, // base
ParamType::Address, // quote
ParamType::Uint(256), // pool index
ParamType::Uint(128), // price
];
pub const INIT_POOL_CODE: u8 = 71;
pub const SWAP_ABI_INPUT: &[ParamType] = &[
ParamType::Address, // base
ParamType::Address, // quote
ParamType::Uint(256), // pool index
// isBuy - if true the direction of the swap is for the user to send base
// tokens and receive back quote tokens.
ParamType::Bool,
ParamType::Bool, // inBaseQty
ParamType::Uint(128), //qty
ParamType::Uint(16), // poolTip
ParamType::Uint(128), // limitPrice
ParamType::Uint(128), // minOut
ParamType::Uint(8), // reserveFlags
];
// MicroPaths fn sigs
pub const SWAP_FN_SIG: [u8; 4] = hex!("3d719cd9");
pub fn decode_direct_swap_call(
call: &Call,
) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> {
if let Ok(external_input_params) = decode(SWAP_ABI_INPUT, &call.input[4..]) {
let base_token = external_input_params[0]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!(
"Failed to convert base token to address for direct swap call: {:?}",
&external_input_params[0]
)
})?
.to_fixed_bytes()
.to_vec();
let quote_token = external_input_params[1]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!(
"Failed to convert quote token to address for direct swap call: {:?}",
&external_input_params[1]
)
})?
.to_fixed_bytes()
.to_vec();
let mut pool_index_buf = [0u8; 32];
external_input_params[2]
.to_owned()
.into_uint()
.ok_or_else(|| {
anyhow!("Failed to convert pool index to u32 for direct swap call".to_string())
})?
.to_big_endian(&mut pool_index_buf);
let pool_index = pool_index_buf.to_vec();
let (base_flow, quote_flow) = decode_flows_from_output(call)?;
let pool_hash = encode_pool_hash(base_token, quote_token, pool_index);
Ok((pool_hash, base_flow, quote_flow))
} else {
bail!("Failed to decode swap call inputs.".to_string());
}
}
pub fn decode_pool_init(
call: &Call,
tx: Transaction,
) -> Result<Option<ProtocolComponent>, anyhow::Error> {
// Decode external call to UserCmd
if let Ok(external_params) = decode(USER_CMD_EXTERNAL_ABI, &call.input[4..]) {
let cmd_bytes = external_params[1]
.to_owned()
.into_bytes()
.ok_or_else(|| anyhow!("Failed to convert to bytes: {:?}", &external_params[1]))?;
// Call data is structured differently depending on the cmd code, so only
// decode if this is an init pool code.
if cmd_bytes[31] == INIT_POOL_CODE {
// Decode internal call to UserCmd
if let Ok(internal_params) = decode(USER_CMD_INTERNAL_ABI, &cmd_bytes) {
let base = internal_params[1]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!("Failed to convert to address: {:?}", &internal_params[1])
})?
.to_fixed_bytes()
.to_vec();
let quote = internal_params[2]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!("Failed to convert to address: {:?}", &internal_params[2])
})?
.to_fixed_bytes()
.to_vec();
let mut pool_index_buf = [0u8; 32];
internal_params[3]
.to_owned()
.into_uint()
.ok_or_else(|| anyhow!("Failed to convert to u32".to_string()))?
.to_big_endian(&mut pool_index_buf);
let pool_index = pool_index_buf.to_vec();
let pool_hash = encode_pool_hash(base.clone(), quote.clone(), pool_index.clone());
let static_attribute = Attribute {
name: String::from("pool_index"),
value: pool_index,
change: ChangeType::Creation.into(),
};
let mut tokens: Vec<Vec<u8>> = vec![base.clone(), quote.clone()];
tokens.sort();
let new_component = ProtocolComponent {
id: hex::encode(pool_hash),
tokens,
contracts: vec![AMBIENT_CONTRACT.to_vec()],
static_att: vec![static_attribute],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "ambient_pool".to_string(),
attribute_schema: vec![],
financial_type: FinancialType::Swap.into(),
implementation_type: ImplementationType::Vm.into(),
}),
tx: Some(tx.clone()),
};
Ok(Some(new_component))
} else {
bail!("Failed to decode ABI internal call.".to_string());
}
} else {
Ok(None)
}
} else {
bail!("Failed to decode ABI external call.".to_string());
}
}

View File

@@ -0,0 +1,307 @@
use anyhow::{anyhow, bail};
use ethabi::{decode, ParamType};
use hex_literal::hex;
use substreams_ethereum::pb::eth::v2::Call;
pub const AMBIENT_MICROPATHS_CONTRACT: [u8; 20] = hex!("f241bEf0Ea64020655C70963ef81Fea333752367");
pub const SWEEP_SWAP_FN_SIG: [u8; 4] = hex!("7b370fc2");
pub const MINT_AMBIENT_FN_SIG: [u8; 4] = hex!("2ee11587");
pub const MINT_RANGE_FN_SIG: [u8; 4] = hex!("2370632b");
pub const BURN_AMBIENT_FN_SIG: [u8; 4] = hex!("2a6f0864");
pub const BURN_RANGE_FN_SIG: [u8; 4] = hex!("7c6dfe3d");
// ABI for the mintAmbient function with return values
pub const MINT_AMBIENT_RETURN_ABI: &[ParamType] = &[
ParamType::Int(128), // int128 baseFlow
ParamType::Int(128), // int128 quoteFlow
ParamType::Uint(128), // uint128 seedOut
];
// ABI for the mintAmbient function parameters
const MINT_AMBIENT_ABI: &[ParamType] = &[
ParamType::Uint(128), // uint128 price
ParamType::Uint(128), // uint128 seed
ParamType::Uint(128), // uint128 conc
ParamType::Uint(64), // uint64 seedGrowth
ParamType::Uint(64), // uint64 concGrowth
ParamType::Uint(128), // uint128 liq
ParamType::FixedBytes(32), // bytes32 poolHash
];
// ABI for the burnRange function
const BURN_RANGE_ABI: &[ParamType] = &[
ParamType::Uint(128), // price
ParamType::Int(24), // priceTick
ParamType::Uint(128), // seed
ParamType::Uint(128), // conc
ParamType::Uint(64), // seedGrowth
ParamType::Uint(64), // concGrowth
ParamType::Int(24), // lowTick
ParamType::Int(24), // highTick
ParamType::Uint(128), // liq
ParamType::FixedBytes(32), // poolHash
];
const BURN_RANGE_RETURN_ABI: &[ParamType] = &[
ParamType::Int(128), // baseFlow
ParamType::Int(128), // quoteFlow
ParamType::Uint(128), // seedOut
ParamType::Uint(128), // concOut
];
// ABI for the burnAmbient function with return values
const BURN_AMBIENT_RETURN_ABI: &[ParamType] = &[
ParamType::Int(128), // int128 baseFlow
ParamType::Int(128), // int128 quoteFlow
ParamType::Uint(128), // uint128 seedOut
];
// ABI for the burnAmbient function
const BURN_AMBIENT_ABI: &[ParamType] = &[
ParamType::Uint(128), // uint128 price
ParamType::Uint(128), // uint128 seed
ParamType::Uint(128), // uint128 conc
ParamType::Uint(64), // uint64 seedGrowth
ParamType::Uint(64), // uint64 concGrowth
ParamType::Uint(128), // uint128 liq
ParamType::FixedBytes(32), // bytes32 poolHash
];
// ABI for the mintRange function parameters
const MINT_RANGE_ABI: &[ParamType] = &[
ParamType::Uint(128), // price
ParamType::Int(24), // priceTick
ParamType::Uint(128), // seed
ParamType::Uint(128), // conc
ParamType::Uint(64), // seedGrowth
ParamType::Uint(64), // concGrowth
ParamType::Int(24), // lowTick
ParamType::Int(24), // highTick
ParamType::Uint(128), // liq
ParamType::FixedBytes(32), // poolHash
];
// ABI for the mintRange function with return values
const MINT_RANGE_RETURN_ABI: &[ParamType] = &[
ParamType::Int(128), // baseFlow
ParamType::Int(128), // quoteFlow
ParamType::Uint(128), // seedOut
ParamType::Uint(128), // concOut
];
pub fn decode_mint_range_call(
call: &Call,
) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> {
if let Ok(mint_range) = decode(MINT_RANGE_ABI, &call.input[4..]) {
let pool_hash: [u8; 32] = mint_range[9]
.to_owned()
.into_fixed_bytes()
.ok_or_else(|| anyhow!("Failed to convert pool hash to fixed bytes".to_string()))?
.try_into()
.unwrap();
if let Ok(external_outputs) = decode(MINT_RANGE_RETURN_ABI, &call.return_data) {
let base_flow = external_outputs[0]
.to_owned()
.into_int() // Needs conversion into bytes for next step
.ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?;
let quote_flow = external_outputs[1]
.to_owned()
.into_int() // Needs conversion into bytes for next step
.ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?;
Ok((pool_hash, base_flow, quote_flow))
} else {
bail!("Failed to decode swap call outputs.".to_string());
}
} else {
bail!("Failed to decode inputs for WarmPath userCmd call.".to_string());
}
}
pub fn decode_burn_ambient_call(
call: &Call,
) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> {
if let Ok(burn_ambient) = decode(BURN_AMBIENT_ABI, &call.input[4..]) {
let pool_hash: [u8; 32] = burn_ambient[6]
.to_owned()
.into_fixed_bytes()
.ok_or_else(|| anyhow!("Failed to convert pool hash to bytes".to_string()))?
.try_into()
.unwrap();
if let Ok(external_outputs) = decode(BURN_AMBIENT_RETURN_ABI, &call.return_data) {
let base_flow = external_outputs[0]
.to_owned()
.into_int()
.ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?;
let quote_flow = external_outputs[1]
.to_owned()
.into_int()
.ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?;
Ok((pool_hash, base_flow, quote_flow))
} else {
bail!("Failed to decode burnAmbient call outputs.".to_string());
}
} else {
bail!("Failed to decode inputs for burnAmbient call.".to_string());
}
}
pub fn decode_mint_ambient_call(
call: &Call,
) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> {
if let Ok(mint_ambient) = decode(MINT_AMBIENT_ABI, &call.input[4..]) {
let pool_hash: [u8; 32] = mint_ambient[6]
.to_owned()
.into_fixed_bytes()
.ok_or_else(|| anyhow!("Failed to convert pool hash to bytes".to_string()))?
.try_into()
.unwrap();
if let Ok(external_outputs) = decode(MINT_AMBIENT_RETURN_ABI, &call.return_data) {
let base_flow = external_outputs[0]
.to_owned()
.into_int()
.ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?;
let quote_flow = external_outputs[1]
.to_owned()
.into_int()
.ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?;
Ok((pool_hash, base_flow, quote_flow))
} else {
bail!("Failed to decode mintAmbient call outputs.".to_string());
}
} else {
bail!("Failed to decode inputs for mintAmbient call.".to_string());
}
}
pub fn decode_burn_range_call(
call: &Call,
) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> {
if let Ok(burn_range) = decode(BURN_RANGE_ABI, &call.input[4..]) {
let pool_hash: [u8; 32] = burn_range[9]
.to_owned()
.into_fixed_bytes() // Convert Bytes32 to Vec<u8>
.ok_or_else(|| anyhow!("Failed to convert pool hash to bytes".to_string()))?
.try_into()
.unwrap();
if let Ok(external_outputs) = decode(BURN_RANGE_RETURN_ABI, &call.return_data) {
let base_flow = external_outputs[0]
.to_owned()
.into_int()
.ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?;
let quote_flow = external_outputs[1]
.to_owned()
.into_int()
.ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?;
Ok((pool_hash, base_flow, quote_flow))
} else {
bail!("Failed to decode burnRange call outputs.".to_string());
}
} else {
bail!("Failed to decode inputs for burnRange call.".to_string());
}
}
pub fn decode_sweep_swap_call(
call: &Call,
) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> {
let sweep_swap_abi: &[ParamType] = &[
ParamType::Tuple(vec![
ParamType::Uint(128),
ParamType::Uint(128),
ParamType::Uint(128),
ParamType::Uint(64),
ParamType::Uint(64),
]), // CurveState
ParamType::Int(24), // midTick
ParamType::Tuple(vec![
ParamType::Bool,
ParamType::Bool,
ParamType::Uint(8),
ParamType::Uint(128),
ParamType::Uint(128),
]), // SwapDirective
ParamType::Tuple(vec![
ParamType::Tuple(vec![
ParamType::Uint(8), // schema
ParamType::Uint(16), // feeRate
ParamType::Uint(8), // protocolTake
ParamType::Uint(16), // tickSize
ParamType::Uint(8), // jitThresh
ParamType::Uint(8), // knockoutBits
ParamType::Uint(8), // oracleFlags
]),
ParamType::FixedBytes(32), // poolHash
ParamType::Address,
]), // PoolCursor
];
let sweep_swap_abi_output: &[ParamType] = &[
ParamType::Tuple(vec![
ParamType::Int(128), // baseFlow
ParamType::Int(128), // quoteFlow
ParamType::Uint(128),
ParamType::Uint(128),
]), // Chaining.PairFlow memory accum
ParamType::Uint(128), // priceOut
ParamType::Uint(128), // seedOut
ParamType::Uint(128), // concOut
ParamType::Uint(64), // ambientOut
ParamType::Uint(64), // concGrowthOut
];
if let Ok(sweep_swap_input) = decode(sweep_swap_abi, &call.input[4..]) {
let pool_cursor = sweep_swap_input[3]
.to_owned()
.into_tuple()
.ok_or_else(|| {
anyhow!("Failed to convert pool cursor to tuple for sweepSwap call".to_string())
})?;
let pool_hash: [u8; 32] = pool_cursor[1]
.to_owned()
.into_fixed_bytes()
.ok_or_else(|| {
anyhow!("Failed to convert pool hash to fixed bytes for sweepSwap call".to_string())
})?
.try_into()
.unwrap();
if let Ok(sweep_swap_output) = decode(sweep_swap_abi_output, &call.return_data) {
let pair_flow = sweep_swap_output[0]
.to_owned()
.into_tuple()
.ok_or_else(|| {
anyhow!("Failed to convert pair flow to tuple for sweepSwap call".to_string())
})?;
let base_flow = pair_flow[0]
.to_owned()
.into_int() // Needs conversion into bytes for next step
.ok_or_else(|| {
anyhow!("Failed to convert base flow to i128 for sweepSwap call".to_string())
})?;
let quote_flow = pair_flow[1]
.to_owned()
.into_int() // Needs conversion into bytes for next step
.ok_or_else(|| {
anyhow!("Failed to convert quote flow to i128 for sweepSwap call".to_string())
})?;
Ok((pool_hash, base_flow, quote_flow))
} else {
bail!("Failed to decode sweepSwap outputs.".to_string());
}
} else {
bail!("Failed to decode sweepSwap inputs.".to_string());
}
}

View File

@@ -0,0 +1,16 @@
// @generated
pub mod warmpath {
include!("warmpath.rs");
}
pub mod micropaths {
include!("micropaths.rs");
}
pub mod knockout {
include!("knockout.rs");
}
pub mod hotproxy {
include!("hotproxy.rs");
}
pub mod main {
include!("main.rs");
}

View File

@@ -0,0 +1,87 @@
use anyhow::{anyhow, bail};
use crate::utils::{decode_flows_from_output, encode_pool_hash};
use ethabi::{decode, ParamType};
use hex_literal::hex;
use substreams_ethereum::pb::eth::v2::Call;
pub const AMBIENT_WARMPATH_CONTRACT: [u8; 20] = hex!("d268767BE4597151Ce2BB4a70A9E368ff26cB195");
pub const USER_CMD_WARMPATH_FN_SIG: [u8; 4] = hex!("f96dc788");
const USER_CMD_EXTERNAL_ABI: &[ParamType] = &[
ParamType::Bytes, // userCmd
];
const LIQUIDITY_CHANGE_ABI: &[ParamType] = &[
ParamType::Uint(8),
ParamType::Address, // base
ParamType::Address, // quote
ParamType::Uint(256), // pool index
ParamType::Int(256),
ParamType::Uint(128),
ParamType::Uint(128),
ParamType::Uint(128),
ParamType::Uint(8),
ParamType::Address,
];
pub fn decode_warm_path_user_cmd_call(
call: &Call,
) -> Result<Option<([u8; 32], ethabi::Int, ethabi::Int)>, anyhow::Error> {
if let Ok(external_cmd) = decode(USER_CMD_EXTERNAL_ABI, &call.input[4..]) {
let input_bytes = external_cmd[0]
.to_owned()
.into_bytes() // Convert Bytes32 to Vec<u8>
.ok_or_else(|| anyhow!("Failed to hotproxy userCmd input data.".to_string()))?;
let code = input_bytes[31];
let is_mint = [1, 11, 12, 3, 31, 32].contains(&code);
let is_burn = [2, 21, 22, 4, 41, 42].contains(&code);
let is_harvest = code == 5;
if is_mint || is_burn || is_harvest {
if let Ok(liquidity_change_calldata) = decode(LIQUIDITY_CHANGE_ABI, &input_bytes) {
let base_token = liquidity_change_calldata[1]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!(
"Failed to convert base token to address for WarmPath userCmd call: {:?}",
&liquidity_change_calldata[1]
)
})?
.to_fixed_bytes()
.to_vec();
let quote_token = liquidity_change_calldata[2]
.to_owned()
.into_address()
.ok_or_else(|| {
anyhow!(
"Failed to convert quote token to address for WarmPath userCmd call: {:?}",
&liquidity_change_calldata[2]
)
})?
.to_fixed_bytes()
.to_vec();
let mut pool_index_buf = [0u8; 32];
liquidity_change_calldata[3]
.to_owned()
.into_uint()
.ok_or_else(|| {
anyhow!("Failed to convert pool index to bytes for WarmPath userCmd call"
.to_string())
})?
.to_big_endian(&mut pool_index_buf);
let pool_index = pool_index_buf.to_vec();
let (base_flow, quote_flow) = decode_flows_from_output(call)?;
let pool_hash = encode_pool_hash(base_token, quote_token, pool_index);
Ok(Some((pool_hash, base_flow, quote_flow)))
} else {
bail!("Failed to decode inputs for WarmPath userCmd call.".to_string());
}
} else {
Ok(None)
}
} else {
bail!("Failed to decode WarmPath call external input.".to_string());
}
}