use alloy_primitives::{Address, U256, keccak256};
use crate::CowError;
use super::types::{FlashLoanParams, FlashLoanProvider};
pub const AAVE_POOL_ADDRESS_MAINNET: Address = Address::new([
0x87, 0x87, 0x0b, 0xca, 0x3f, 0x3f, 0xd6, 0x33, 0x5c, 0x3f, 0x4c, 0xe8, 0x39, 0x2d, 0x69, 0x35,
0x0b, 0x4f, 0xa4, 0xe2,
]);
pub const AAVE_ADAPTER_FACTORY: Address = Address::new([
0x43, 0xc6, 0x58, 0xea, 0x38, 0xbb, 0xfd, 0x89, 0x77, 0x06, 0xfd, 0xb3, 0x5e, 0x24, 0x68, 0xef,
0x5d, 0x8f, 0x69, 0x27,
]);
pub const HASH_ZERO: &str = "0x0000000000000000000000000000000000000000000000000000000000000000";
pub const PERCENT_SCALE: u32 = 10_000;
pub const BASIS_POINTS_SCALE: u64 = 1_000_000;
pub const HALF_BASIS_POINTS_SCALE: u64 = BASIS_POINTS_SCALE / 2;
pub const DEFAULT_VALIDITY: u32 = 10 * 60;
pub const GAS_ESTIMATION_ADDITION_PERCENT: u32 = 10;
pub const ADAPTER_DOMAIN_NAME: &str = "AaveV3AdapterFactory";
pub const ADAPTER_DOMAIN_VERSION: &str = "1";
#[derive(Debug, Clone, Copy)]
pub struct FlashLoanSdk;
impl FlashLoanSdk {
#[must_use]
pub const fn provider_address(provider: FlashLoanProvider, chain_id: u64) -> Option<Address> {
provider.contract_address(chain_id)
}
#[must_use]
pub fn encode_balancer_flash_loan(
receiver: Address,
token: Address,
amount: U256,
user_data: &[u8],
) -> Vec<u8> {
let sig = b"flashLoan(address,address[],uint256[],bytes)";
let h = keccak256(sig);
let sel = [h[0], h[1], h[2], h[3]];
let data_padded_len = user_data.len().div_ceil(32) * 32;
let head_size: u64 = 128; let tokens_offset: u64 = head_size; let amounts_offset: u64 = tokens_offset + 64; let data_offset: u64 = amounts_offset + 64;
fn u64_word(v: u64) -> [u8; 32] {
U256::from(v).to_be_bytes()
}
fn abi_addr(a: Address) -> [u8; 32] {
let mut buf = [0u8; 32];
buf[12..].copy_from_slice(a.as_slice());
buf
}
let capacity = 4 + 128 + 64 + 64 + 32 + data_padded_len;
let mut buf = Vec::with_capacity(capacity);
buf.extend_from_slice(&sel);
buf.extend_from_slice(&abi_addr(receiver));
buf.extend_from_slice(&u64_word(tokens_offset));
buf.extend_from_slice(&u64_word(amounts_offset));
buf.extend_from_slice(&u64_word(data_offset));
buf.extend_from_slice(&u64_word(1));
buf.extend_from_slice(&abi_addr(token));
buf.extend_from_slice(&u64_word(1));
buf.extend_from_slice(&amount.to_be_bytes::<32>());
buf.extend_from_slice(&u64_word(user_data.len() as u64));
buf.extend_from_slice(user_data);
let pad = data_padded_len - user_data.len();
buf.extend(std::iter::repeat_n(0u8, pad));
buf
}
pub fn build_flash_loan_hook(
params: &FlashLoanParams,
receiver: Address,
user_data: &[u8],
) -> Result<crate::app_data::CowHook, CowError> {
let contract = params.provider.contract_address(params.chain_id).ok_or_else(|| {
CowError::Unsupported {
message: format!(
"{} flash loans not supported on chain {}",
params.provider.name(),
params.chain_id
),
}
})?;
let calldata = match params.provider {
FlashLoanProvider::Balancer => {
Self::encode_balancer_flash_loan(receiver, params.token, params.amount, user_data)
}
FlashLoanProvider::MakerDao | FlashLoanProvider::AaveV3 => {
return Err(CowError::Unsupported {
message: format!(
"{} flash loan encoding not yet implemented",
params.provider.name()
),
});
}
};
Ok(crate::app_data::CowHook {
target: format!("{contract:#x}"),
call_data: alloy_primitives::hex::encode(&calldata),
gas_limit: "500000".to_owned(),
dapp_id: None,
})
}
}