Skip to content

Commit

Permalink
Merge pull request #45 from soroswap/feat/phoenix-adapter
Browse files Browse the repository at this point in the history
Feat/phoenix adapter
  • Loading branch information
esteblock authored Jul 19, 2024
2 parents ab19b31 + 735f88b commit ed4ce01
Show file tree
Hide file tree
Showing 14 changed files with 939 additions and 405 deletions.
34 changes: 24 additions & 10 deletions contracts/adapters/phoenix/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
#![no_std]
use soroban_sdk::{contract, contractimpl, Address, Env, Vec, String};

mod event;
mod storage;
mod protocol_interface;
mod test;

use storage::{
extend_instance_ttl,
set_initialized,
is_initialized,
extend_instance_ttl,
set_initialized,
is_initialized,
set_protocol_id,
get_protocol_id,
set_protocol_address,
get_protocol_address,
set_protocol_address,
get_protocol_address,
};
use soroswap_aggregator_adapter_interface::{
SoroswapAggregatorAdapterTrait, AdapterError
};
use protocol_interface::{
protocol_swap_exact_tokens_for_tokens,
protocol_swap_tokens_for_exact_tokens
};
use soroswap_aggregator_adapter_interface::{SoroswapAggregatorAdapterTrait, AdapterError};
use protocol_interface::{protocol_swap_exact_tokens_for_tokens,
protocol_swap_tokens_for_exact_tokens};

pub fn check_nonnegative_amount(amount: i128) -> Result<(), AdapterError> {
if amount < 0 {
Expand Down Expand Up @@ -49,7 +52,18 @@ struct SoroswapAggregatorPhoenixAdapter;

#[contractimpl]
impl SoroswapAggregatorAdapterTrait for SoroswapAggregatorPhoenixAdapter {
/// Initializes the contract and sets the phoenix multihop address

/// Initializes the contract and sets the Phoenix multihop address.
///
/// # Arguments
///
/// * `e` - The contract environment.
/// * `protocol_id` - The identifier for the protocol.
/// * `protocol_address` - The address associated with the protocol.
///
/// # Errors
///
/// Returns an error if the contract is already initialized (`AdapterError::AlreadyInitialized`).
fn initialize(
e: Env,
protocol_id: String,
Expand Down
124 changes: 104 additions & 20 deletions contracts/adapters/phoenix/src/protocol_interface.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// based on https://github.com/Phoenix-Protocol-Group/phoenix_contracts/tree/v1.0.0

use soroban_sdk::{Env, Address, Vec, vec};
use soroban_sdk::{Env, Address, Vec, token::Client as TokenClient};
use crate::storage::{get_protocol_address};
use soroswap_aggregator_adapter_interface::{AdapterError};

Expand All @@ -9,44 +9,83 @@ soroban_sdk::contractimport!(
);
pub type PhoenixMultihopClient<'a> = Client<'a>;

fn convert_to_swaps(e: &Env, addresses: &Vec<Address>) -> Vec<Swap> {
fn convert_to_swaps(e: &Env, path: &Vec<Address>) -> Vec<Swap> {
let mut swaps = Vec::new(e);

// Iterate through the addresses, creating a Swap for each pair
// Skip the last address since it cannot be an offer_asset without a corresponding ask_asset
for i in 0..(addresses.len() - 1) {
let offer_asset = addresses.get(i).expect("Failed to get offer asset");
let ask_asset = addresses.get(i + 1).expect("Failed to get ask asset");
// Iterate through the addresses in the path, creating a Swap object for each pair
// If path is [token0, token1, token2, token3], swaps should be
// swap_0 = Swap{
// offer_asset: token0,
// ask_asset: token1,
// ask_asset_min_amount: None,
// },
// swap_1 = Swap{
// offer_asset: token1,
// ask_asset: token2,
// ask_asset_min_amount: None,
// },
// swap_2 = Swap{
// offer_asset: token2,
// ask_asset: token3,
// ask_asset_min_amount: None,
// }

for i in 0..(path.len() - 1) {
let offer_asset = path.get(i).expect("Failed to get offer asset");
let ask_asset = path.get(i + 1).expect("Failed to get ask a sset");

swaps.push_back(Swap {
ask_asset: ask_asset.clone(),
offer_asset: offer_asset.clone(),
offer_asset: offer_asset.clone(), // asset being sold (token_in)
ask_asset: ask_asset.clone(), // asset buying (token_out)
ask_asset_min_amount: None,
});
}

swaps
}


pub fn protocol_swap_exact_tokens_for_tokens(
e: &Env,
amount_in: &i128,
amount_out_min: &i128,
path: &Vec<Address>,
to: &Address,
deadline: &u64,
_deadline: &u64,
) -> Result<Vec<i128>, AdapterError> {

let phoenix_multihop_address = get_protocol_address(&e)?;
let phoenix_multihop_client = PhoenixMultihopClient::new(&e, &phoenix_multihop_address);
let operations = convert_to_swaps(e, path);

// TODO: Remove this checks if we want to reduce the number of total instructions
// TODO: Do benchmarking
let token_out_address = path.get(path.len() - 1).expect("Failed to get token out address");
let initial_token_out_balance = TokenClient::new(&e, &token_out_address).balance(&to);

// By using max_spread_bps = None, the Phoenix LP will use the maximum allowed slippage
// amount_in is the amount being sold of the first token in the operations.
phoenix_multihop_client.swap(
&to, // recipient: Address,
&operations, // operations: Vec<Swap>,
&None, // max_spread_bps: Option<i64>.
&amount_in); //amout: i128. Amount being sold. Input from the user,

let final_token_out_balance = TokenClient::new(&e, &token_out_address).balance(&to);

// check if the amount of token_out received is greater than the minimum amount expected
// TODO: Remove this checks if we want to reduce the number of total instructions
// TODO: Do benchmarking
let final_amount_out = final_token_out_balance.checked_sub(initial_token_out_balance).unwrap();
if final_amount_out < *amount_out_min {
// panic
panic!("Amount of token out received is less than the minimum amount expected");
}

// TODO: CHECK AND TEST
phoenix_multihop_client.swap(&to, &operations, &None, &amount_in);
let mut swap_amounts: Vec<i128> = Vec::new(e);
swap_amounts.push_back(amount_in.clone());
swap_amounts.push_back(final_amount_out);

// Returning empty array (should check phoenix response if it return amounts, apparently it doesnt)
Ok(vec![&e])
Ok(swap_amounts)
}

pub fn protocol_swap_tokens_for_exact_tokens(
Expand All @@ -55,16 +94,61 @@ pub fn protocol_swap_tokens_for_exact_tokens(
amount_in_max: &i128,
path: &Vec<Address>,
to: &Address,
deadline: &u64,
_deadline: &u64,
) -> Result<Vec<i128>, AdapterError> {

let phoenix_multihop_address = get_protocol_address(&e)?;
let phoenix_multihop_client = PhoenixMultihopClient::new(&e, &phoenix_multihop_address);
let operations = convert_to_swaps(e, path);

// TODO: CHECK AND TEST
phoenix_multihop_client.swap(&to, &operations, &None, &amount_in_max);
// We first need to get the "reverse_amount from phoenix.simulate_reverse_swap"
// however here, if the path is [t0, t1, t2, t3, t4], the operations should be
// swap_0 = Swap{
// offer_asset: t3,
// ask_asset: t4,
// ask_asset_min_amount: None,
// },
// swap_1 = Swap{
// offer_asset: t2,
// ask_asset: t3,
// ask_asset_min_amount: None,
// },
// swap_2 = Swap{
// offer_asset: t1,
// ask_asset: t2,
// ask_asset_min_amount: None,
// },
// swap_3 = Swap{
// offer_asset: t0,
// ask_asset: t1,
// ask_asset_min_amount: None,
// }

let mut operations_reversed = soroban_sdk::Vec::new(&e);
for op in operations.iter().rev() {
operations_reversed.push_back(op.clone());
}
let reverse_simulated_swap = phoenix_multihop_client.simulate_reverse_swap(
&operations_reversed, //operations: Vec<Swap>,
amount_out); //amount: i128,

// TODO: Eliminate this check. The overall in max is checked by the Aggregator
// Removing this check will reduce the amount of instructions/
// TODO: Do Benchmarking
if reverse_simulated_swap.offer_amount > *amount_in_max {
panic!("Amount of token in required is greater than the maximum amount expected");
}

phoenix_multihop_client.swap(
&to, // recipient: Address,
&operations, // operations: Vec<Swap>,
&None, // max_spread_bps: Option<i64>.
&reverse_simulated_swap.offer_amount); //amout: i128. Amount being sold. Input from the user,

// Here we trust in the amounts returned by Phoenix contracts
let mut swap_amounts: Vec<i128> = Vec::new(e);
swap_amounts.push_back(reverse_simulated_swap.offer_amount);
swap_amounts.push_back(*amount_out);

// Returning empty array (should check phoenix response if it return amounts, apparently it doesnt)
Ok(vec![&e])
Ok(swap_amounts)
}
10 changes: 6 additions & 4 deletions contracts/adapters/phoenix/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@ use soroban_sdk::{

};
use crate::{SoroswapAggregatorPhoenixAdapter, SoroswapAggregatorPhoenixAdapterClient};
use phoenix_setup::{PhoenixTest, MultihopClient, TokenClient};
use phoenix_setup::{PhoenixTest, MultihopClient, TokenClient, PhoenixFactory};
// use factory::SoroswapFactoryClient;
// use router::SoroswapRouterClient;

// PhoenixAggregatorAdapter Contract
fn create_soroswap_aggregator_adapter<'a>(e: &Env) -> SoroswapAggregatorPhoenixAdapterClient<'a> {
fn create_soroswap_aggregator_phoenix_adapter<'a>(e: &Env) -> SoroswapAggregatorPhoenixAdapterClient<'a> {
SoroswapAggregatorPhoenixAdapterClient::new(e, &e.register_contract(None, SoroswapAggregatorPhoenixAdapter {}))
}

pub struct PhoenixAggregatorAdapterTest<'a> {
env: Env,
adapter_client: SoroswapAggregatorPhoenixAdapterClient<'a>,
factory_client: PhoenixFactory<'a>,
multihop_client: MultihopClient<'a>,
token_0: TokenClient<'a>,
token_1: TokenClient<'a>,
Expand All @@ -33,11 +34,12 @@ impl<'a> PhoenixAggregatorAdapterTest<'a> {
fn setup() -> Self {
let test = PhoenixTest::phoenix_setup();

let adapter_client = create_soroswap_aggregator_adapter(&test.env);
let adapter_client = create_soroswap_aggregator_phoenix_adapter(&test.env);

PhoenixAggregatorAdapterTest {
env: test.env,
adapter_client,
factory_client: test.factory_client,
multihop_client: test.multihop_client,
token_0: test.token_0,
token_1: test.token_1,
Expand All @@ -51,4 +53,4 @@ impl<'a> PhoenixAggregatorAdapterTest<'a> {

pub mod initialize;
pub mod swap_exact_tokens_for_tokens;
// pub mod swap_tokens_for_exact_tokens;
pub mod swap_tokens_for_exact_tokens;
36 changes: 18 additions & 18 deletions contracts/adapters/phoenix/src/test/phoenix_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// extern crate std;
use soroban_sdk::{
vec,
IntoVal,
// IntoVal,
String,
Env,
Bytes,
Expand Down Expand Up @@ -32,7 +32,7 @@ pub fn deploy_factory_contract(e: &Env, admin: & Address) -> Address {
e.deployer().with_address(admin.clone(), salt).deploy(factory_wasm)
}

use factory::Client as PhoenixFactory;
pub use factory::Client as PhoenixFactory;

/* ************* MULTIHOP ************* */
#[allow(clippy::too_many_arguments)]
Expand Down Expand Up @@ -72,20 +72,20 @@ pub mod token_contract {

pub use token_contract::Client as TokenClient;

pub fn create_token_contract_with_metadata<'a>(
env: &Env,
admin: &Address,
decimals: u32,
name: String,
symbol: String,
amount: i128,
) -> TokenClient<'a> {
let token =
TokenClient::new(env, &env.register_contract_wasm(None, token_contract::WASM));
token.initialize(admin, &decimals, &name.into_val(env), &symbol.into_val(env));
token.mint(admin, &amount);
token
}
// pub fn create_token_contract_with_metadata<'a>(
// env: &Env,
// admin: &Address,
// decimals: u32,
// name: String,
// symbol: String,
// amount: i128,
// ) -> TokenClient<'a> {
// let token =
// TokenClient::new(env, &env.register_contract_wasm(None, token_contract::WASM));
// token.initialize(admin, &decimals, &name.into_val(env), &symbol.into_val(env));
// token.mint(admin, &amount);
// token
// }

pub fn install_token_wasm(env: &Env) -> BytesN<32> {
soroban_sdk::contractimport!(
Expand Down Expand Up @@ -280,11 +280,11 @@ impl<'a> PhoenixTest<'a> {

// Setup multihop
let multihop_client = deploy_multihop_contract(&env, admin.clone(), &factory_client.address);
token_0.mint(&user, &50i128);
token_0.mint(&user, &1000i128);

// Check initial user value of every token:

assert_eq!(token_0.balance(&user), 50i128);
assert_eq!(token_0.balance(&user), 1000i128);
assert_eq!(token_1.balance(&user), 0i128);
assert_eq!(token_2.balance(&user), 0i128);
assert_eq!(token_3.balance(&user), 0i128);
Expand Down
Loading

0 comments on commit ed4ce01

Please sign in to comment.