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, 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![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()); } }