diff --git a/.snfoundry_cache/.prev_tests_failed b/.snfoundry_cache/.prev_tests_failed index 263a73d..c5bab9c 100644 --- a/.snfoundry_cache/.prev_tests_failed +++ b/.snfoundry_cache/.prev_tests_failed @@ -1 +1 @@ -auto_swappr_tests::test_swap::test_swap +auto_swappr_tests::test_fibrous_swap::test_fibrous_swap diff --git a/src/autoswappr.cairo b/src/autoswappr.cairo index 0eaa8fc..0035d1e 100644 --- a/src/autoswappr.cairo +++ b/src/autoswappr.cairo @@ -4,7 +4,7 @@ // @dev Implements upgradeable pattern and ownership control pub mod AutoSwappr { use crate::interfaces::iautoswappr::{IAutoSwappr, ContractInfo}; - use crate::base::types::{Route, Assets}; + use crate::base::types::{Route, Assets, RouteParams, SwapParams}; use openzeppelin_upgrades::UpgradeableComponent; use openzeppelin_upgrades::interface::IUpgradeable; use core::starknet::storage::{ @@ -19,6 +19,7 @@ pub mod AutoSwappr { use openzeppelin::access::ownable::OwnableComponent; use crate::interfaces::iavnu_exchange::{IExchangeDispatcher, IExchangeDispatcherTrait}; + use crate::interfaces::fibrous_exchange::{IFibrousExchangeDispatcher, IFibrousExchangeDispatcherTrait}; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use core::integer::{u256, u128}; @@ -47,6 +48,7 @@ pub mod AutoSwappr { #[substorage(v0)] upgradeable: UpgradeableComponent::Storage, avnu_exchange_address: ContractAddress, + fibrous_exchange_address: ContractAddress, } // @notice Events emitted by the contract @@ -101,6 +103,7 @@ pub mod AutoSwappr { ref self: ContractState, fees_collector: ContractAddress, avnu_exchange_address: ContractAddress, + fibrous_exchange_address: ContractAddress, _strk_token: ContractAddress, _eth_token: ContractAddress, owner: ContractAddress, @@ -109,6 +112,7 @@ pub mod AutoSwappr { self.strk_token.write(_strk_token); self.eth_token.write(_eth_token); self.avnu_exchange_address.write(avnu_exchange_address); + self.fibrous_exchange_address.write(fibrous_exchange_address); self.ownable.initializer(owner); self.supported_assets.write(_strk_token, true); self.supported_assets.write(_eth_token, true); @@ -201,6 +205,35 @@ pub mod AutoSwappr { ); } + fn fibrous_swap( + ref self: ContractState, + routeParams: RouteParams, + swapParams: Array, + ){ + let caller_address = get_caller_address(); + + assert( + self.supported_assets.entry(routeParams.token_in).read(), Errors::UNSUPPORTED_TOKEN, + ); + assert(!routeParams.amount_in.is_zero(), Errors::ZERO_AMOUNT); + + let token = IERC20Dispatcher { contract_address: routeParams.token_in }; + + assert( + token.balance_of(caller_address) >= routeParams.amount_in, Errors::INSUFFICIENT_BALANCE, + ); + // assert( + // token.allowance(caller_address, this_contract) >= routeParams.amount_in, + // Errors::INSUFFICIENT_ALLOWANCE, + // ); + + self + ._fibrous_swap( + routeParams, + swapParams, + ); + } + // @notice Returns the contract's current parameters // @return ContractInfo struct containing current contract parameters @@ -248,6 +281,20 @@ pub mod AutoSwappr { ) } + fn _fibrous_swap( + ref self: ContractState, + routeParams: RouteParams, + swapParams: Array, + ) { + let fibrous = IFibrousExchangeDispatcher { contract_address: self.fibrous_exchange_address.read() }; + + fibrous + .swap( + routeParams, + swapParams + ); + } + fn collect_fees(ref self: ContractState) {} // @notice Returns the zero address constant diff --git a/src/base/types.cairo b/src/base/types.cairo index 0da82d7..653f943 100644 --- a/src/base/types.cairo +++ b/src/base/types.cairo @@ -14,3 +14,23 @@ pub struct Assets { pub strk: bool, pub eth: bool, } + +// Fibrous exchange +#[derive(Drop, Serde, Clone)] +pub struct RouteParams { + pub token_in: ContractAddress, + pub token_out: ContractAddress, + pub amount_in: u256, + pub min_received: u256, + pub destination: ContractAddress, +} + +#[derive(Drop, Serde, Clone)] +pub struct SwapParams { + pub token_in: ContractAddress, + pub token_out: ContractAddress, + pub rate: u32, + pub protocol_id: u32, + pub pool_address: ContractAddress, + pub extra_data: Array, +} diff --git a/src/interfaces/fibrous_exchange.cairo b/src/interfaces/fibrous_exchange.cairo new file mode 100644 index 0000000..02812f5 --- /dev/null +++ b/src/interfaces/fibrous_exchange.cairo @@ -0,0 +1,8 @@ +use starknet::ContractAddress; +use crate::base::types::{RouteParams, SwapParams}; + +#[starknet::interface] +pub trait IFibrousExchange { + #[external(v0)] + fn swap(ref self: TContractState, route: RouteParams, swap_parameters: Array); +} diff --git a/src/interfaces/iautoswappr.cairo b/src/interfaces/iautoswappr.cairo index bfcc61d..c4521a0 100644 --- a/src/interfaces/iautoswappr.cairo +++ b/src/interfaces/iautoswappr.cairo @@ -1,5 +1,6 @@ use core::starknet::ContractAddress; use crate::base::types::Route; +use crate::base::types::{RouteParams, SwapParams}; // @title Contract Information Structure // @notice Holds the essential addresses and parameters for the AutoSwappr contract @@ -30,6 +31,11 @@ pub trait IAutoSwappr { integrator_fee_recipient: ContractAddress, routes: Array, ); + fn fibrous_swap( + ref self: TContractState, + routeParams: RouteParams, + swapParams: Array, + ); // @notice Retrieves the current contract parameters // @return ContractInfo struct containing the contract's current configuration diff --git a/src/lib.cairo b/src/lib.cairo index b6f9fa3..32b9036 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -6,6 +6,7 @@ pub mod base { pub mod interfaces { pub mod iautoswappr; pub mod iavnu_exchange; + pub mod fibrous_exchange; } pub mod autoswappr; diff --git a/tests/lib.cairo b/tests/lib.cairo index ef5228c..fd4bf63 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -1,4 +1,5 @@ -mod test_autoswapper; +// mod test_autoswapper; +mod test_fibrous_swap; // mod test_swap; diff --git a/tests/test_autoswapper.cairo b/tests/test_autoswapper.cairo index dee3f48..4c5e9aa 100644 --- a/tests/test_autoswapper.cairo +++ b/tests/test_autoswapper.cairo @@ -31,6 +31,9 @@ pub fn FEE_COLLECTOR_ADDR() -> ContractAddress { pub fn AVNU_ADDR() -> ContractAddress { contract_address_const::<'AVNU_ADDR'>() } +pub fn FIBROUS_ADDR() -> ContractAddress { + contract_address_const::<'FIBROUS_ADDR'>() +} pub fn OWNER() -> ContractAddress { contract_address_const::<'OWNER'>() } @@ -73,6 +76,7 @@ fn __setup__() -> (ContractAddress, IERC20Dispatcher, IERC20Dispatcher) { let mut autoSwappr_constructor_calldata: Array = array![]; FEE_COLLECTOR_ADDR().serialize(ref autoSwappr_constructor_calldata); AVNU_ADDR().serialize(ref autoSwappr_constructor_calldata); + FIBROUS_ADDR().serialize(ref autoSwappr_constructor_calldata); strk_contract_address.serialize(ref autoSwappr_constructor_calldata); eth_contract_address.serialize(ref autoSwappr_constructor_calldata); OWNER().serialize(ref autoSwappr_constructor_calldata); diff --git a/tests/test_fibrous_swap.cairo b/tests/test_fibrous_swap.cairo new file mode 100644 index 0000000..7ff54a6 --- /dev/null +++ b/tests/test_fibrous_swap.cairo @@ -0,0 +1,253 @@ +// ************************************************************************* +// Events TEST +// ************************************************************************* +use core::result::ResultTrait; +use starknet::{ContractAddress, contract_address_const}; +use starknet::syscalls::call_contract_syscall; + +use snforge_std::{ + declare, start_cheat_caller_address, stop_cheat_caller_address, ContractClassTrait, + DeclareResultTrait, +}; + +use auto_swappr::interfaces::iautoswappr::{IAutoSwapprDispatcher, IAutoSwapprDispatcherTrait}; +use auto_swappr::base::types::{RouteParams, SwapParams}; +use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; + + +const USER_ONE: felt252 = 'JOE'; +const USER_TWO: felt252 = 'DOE'; +const OWNER: felt252 = 'OWNER'; +const ONE_E18: u256 = 1000000000000000000_u256; +const ONE_E6: u256 = 1000000_u256; + +const FEE_COLLECTOR: felt252 = 0x0114B0b4A160bCC34320835aEFe7f01A2a3885e4340Be0Bc1A63194469984a06; +const AVNU_EXCHANGE_ADDRESS: felt252 = + 0x04270219d365d6b017231b52e92b3fb5d7c8378b05e9abc97724537a80e93b0f; +const FIBROUS_EXCHANGE_ADDRESS: felt252 = + 0x00f6f4CF62E3C010E0aC2451cC7807b5eEc19a40b0FaaCd00CCA3914280FDf5a; +const STRK_TOKEN_ADDRESS: felt252 = + 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d; +const ETH_TOKEN_ADDRESS: felt252 = + 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7; +const USDC_TOKEN_ADDRESS: felt252 = + 0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8; + +const STK_MINTER_ADDRESS: felt252 = + 0x0594c1582459ea03f77deaf9eb7e3917d6994a03c13405ba42867f83d85f085d; +const SWAP_CALLER_ADDRESS: felt252 = + 0x0114B0b4A160bCC34320835aEFe7f01A2a3885e4340Be0Bc1A63194469984a06; + +const EKUBO_EXCHANGE_ADDRESS: felt252 = + 0x00000005dd3D2F4429AF886cD1a3b08289DBcEa99A294197E9eB43b0e0325b4b; + +const JEDISWAP_ROUTER_ADDRESS: felt252 = + 0x041fd22b238fa21cfcf5dd45a8548974d8263b3a531a60388411c5e230f97023; + +const ROUTE_PERCENT_FACTOR: u128 = 10000000000; + + +// ************************************************************************* +// SETUP +// ************************************************************************* +fn __setup__() -> ContractAddress { + // deploy events + let auto_swappr_class_hash = declare("AutoSwappr").unwrap().contract_class(); + + let mut auto_swappr_constructor_calldata: Array = array![ + FEE_COLLECTOR, AVNU_EXCHANGE_ADDRESS, FIBROUS_EXCHANGE_ADDRESS, STRK_TOKEN_ADDRESS, ETH_TOKEN_ADDRESS, OWNER, + ]; + + let (auto_swappr_contract_address, _) = auto_swappr_class_hash + .deploy(@auto_swappr_constructor_calldata) + .unwrap(); + + auto_swappr_contract_address +} + +#[test] +#[fork(url: "https://starknet-mainnet.public.blastapi.io/rpc/v0_7", block_number: 979167)] +fn test_fibrous_swap() { + let autoSwappr_contract_address = __setup__(); + let autoSwappr_dispatcher = IAutoSwapprDispatcher { + contract_address: autoSwappr_contract_address.clone(), + }; + + let strk_token = IERC20Dispatcher { contract_address: contract_address_const::() }; + + let routeParams = RouteParams { + token_in: contract_address_const::(), + token_out: contract_address_const::(), + amount_in: 15, + min_received:10, + destination: contract_address_const::<123>(), + // contract_address_const::(), + }; + + let swapParamsItem = SwapParams { + token_in: contract_address_const::(), + token_out: contract_address_const::(), + pool_address: contract_address_const::<123>(), + rate: 1, + protocol_id: 1, + extra_data: array![1], + }; + let swapParams = array![swapParamsItem]; + + let address_with_funds = contract_address_const::<0x0298a9d0d82aabfd7e2463bb5ec3590c4e86d03b2ece868d06bbe43475f2d3e6>(); + + start_cheat_caller_address(strk_token.contract_address, address_with_funds); + strk_token + .approve( + autoSwappr_dispatcher.contract_address, + 10 + ); + stop_cheat_caller_address(strk_token.contract_address); + + start_cheat_caller_address(autoSwappr_dispatcher.contract_address, address_with_funds); + + autoSwappr_dispatcher + .fibrous_swap( + routeParams, + swapParams, + ); +} + + +#[test] +#[should_panic(expected: 'Insufficient Balance')] +#[fork(url: "https://starknet-mainnet.public.blastapi.io/rpc/v0_7", block_number: 979167)] +fn test_fibrous_swap_should_fail_for_inssuficient_balance() { + let autoSwappr_contract_address = __setup__(); + let autoSwappr_dispatcher = IAutoSwapprDispatcher { + contract_address: autoSwappr_contract_address.clone(), + }; + + let routeParams = RouteParams { + token_in: contract_address_const::(), + token_out: contract_address_const::(), + amount_in: 10000, + min_received: 900, + destination: contract_address_const::<123>(), + }; + + let swapParamsItem = SwapParams { + token_in: contract_address_const::<123>(), + token_out: contract_address_const::<456>(), + pool_address: contract_address_const::<456>(), + rate: 2, + protocol_id: 3, + extra_data: array![1], + }; + let swapParams = array![swapParamsItem]; + + autoSwappr_dispatcher + .fibrous_swap( + routeParams, + swapParams, + ); +} + +#[test] +#[should_panic(expected: 'Token not supported')] +// #[fork(url: "https://starknet-mainnet.public.blastapi.io/rpc/v0_7", block_tag: latest)] +fn test_fibrous_swap_should_fail_for_token_not_supported() { + let autoSwappr_contract_address = __setup__(); + let autoSwappr_dispatcher = IAutoSwapprDispatcher { + contract_address: autoSwappr_contract_address.clone(), + }; + + let routeParams = RouteParams { + token_in: contract_address_const::<123>(), + token_out: contract_address_const::<456>(), + amount_in: 15, + min_received: 10, + destination: contract_address_const::<789>(), + }; + + let swapParamsItem = SwapParams { + token_in: contract_address_const::<123>(), + token_out: contract_address_const::<456>(), + pool_address: contract_address_const::<789>(), + rate: 2, + protocol_id: 3, + extra_data: array![1,2,3,4], + }; + let swapParams = array![swapParamsItem]; + + autoSwappr_dispatcher + .fibrous_swap( + routeParams, + swapParams, + ); +} + +// #[test] +// #[fork("Mainnet")] +// fn test_swap() { +// let autoswappr_contract_address = __setup__(); +// let autoswappr_contract = IAutoSwapprDispatcher { +// contract_address: autoswappr_contract_address, +// }; + +// let strk_token_address = contract_address_const::(); +// let minter_address = contract_address_const::(); +// let caller = contract_address_const::(); + +// let strk_token = IERC20Dispatcher { contract_address: strk_token_address }; +// let mint_amount: u256 = 500 * ONE_E18; + +// // Mint STRK to caller +// start_cheat_caller_address(strk_token_address, minter_address); +// let mut calldata: Array = ArrayTrait::new(); +// caller.serialize(ref calldata); +// mint_amount.serialize(ref calldata); +// call_contract_syscall(strk_token_address, selector!("permissioned_mint"), calldata.span()) +// .unwrap(); +// stop_cheat_caller_address(strk_token_address); +// assert(strk_token.balance_of(caller) == mint_amount, 'invalid balance'); + +// // Prank caller to approve auto_swapper +// start_cheat_caller_address(strk_token_address, caller); +// strk_token.approve(autoswappr_contract_address, mint_amount); +// stop_cheat_caller_address(strk_token_address); +// assert( +// strk_token.allowance(caller, autoswappr_contract_address) == mint_amount, +// 'invalid allowance', +// ); + +// // Prank caller to and call swap() function in auto_swapper +// start_cheat_caller_address(autoswappr_contract_address, caller); +// let token_from_address = strk_token_address.clone(); +// let token_from_amount: u256 = 5 * ONE_E18; +// let token_to_address = contract_address_const::(); +// let token_to_amount: u256 = 2 * ONE_E6; +// let token_to_min_amount: u256 = 2 * ONE_E6; +// let beneficiary = autoswappr_contract_address; +// let mut routes = ArrayTrait::new(); + +// routes +// .append( +// Route { +// token_from: token_from_address, +// token_to: token_to_address, +// exchange_address: contract_address_const::(), +// percent: 100, +// additional_swap_params: ArrayTrait::new(), +// }, +// ); + +// autoswappr_contract +// .swap( +// token_from_address, +// token_from_amount, +// token_to_address, +// token_to_amount, +// token_to_min_amount, +// beneficiary, +// 0, +// contract_address_const::(), +// routes, +// ); +// stop_cheat_caller_address(autoswappr_contract_address); +// } diff --git a/tests/test_swap.cairo b/tests/test_swap.cairo index e019a62..f625d3b 100644 --- a/tests/test_swap.cairo +++ b/tests/test_swap.cairo @@ -24,6 +24,8 @@ const ONE_E6: u256 = 1000000_u256; const FEE_COLLECTOR: felt252 = 0x0114B0b4A160bCC34320835aEFe7f01A2a3885e4340Be0Bc1A63194469984a06; const AVNU_EXCHANGE_ADDRESS: felt252 = 0x04270219d365d6b017231b52e92b3fb5d7c8378b05e9abc97724537a80e93b0f; +const FIBROUS_EXCHANGE_ADDRESS: felt252 = + 0x00f6f4CF62E3C010E0aC2451cC7807b5eEc19a40b0FaaCd00CCA3914280FDf5a; const STRK_TOKEN_ADDRESS: felt252 = 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d; const ETH_TOKEN_ADDRESS: felt252 = @@ -53,7 +55,7 @@ fn __setup__() -> ContractAddress { let auto_swappr_class_hash = declare("AutoSwappr").unwrap().contract_class(); let mut auto_swappr_constructor_calldata: Array = array![ - FEE_COLLECTOR, AVNU_EXCHANGE_ADDRESS, STRK_TOKEN_ADDRESS, ETH_TOKEN_ADDRESS, OWNER, + FEE_COLLECTOR, AVNU_EXCHANGE_ADDRESS, FIBROUS_EXCHANGE_ADDRESS, STRK_TOKEN_ADDRESS, ETH_TOKEN_ADDRESS, OWNER, ]; let (auto_swappr_contract_address, _) = auto_swappr_class_hash