Skip to content

Commit

Permalink
Merge pull request #128 from Phoenix-Protocol-Group/124-factory-enhan…
Browse files Browse the repository at this point in the history
…ce-query_pools-to-include-more-information

Factory: enhance query_pool
  • Loading branch information
gangov authored Sep 14, 2023
2 parents 1563813 + 24bcaaf commit b4eb70b
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to
- Decimal: Implement `from_atomics` and `to_string` ([#115])
- Curve: Implement `end` helper ([#115])
- Phoenix: Helper library for commonly used functions, structs, etc... ([#116])
- Factory: Adds functionality that provides more detailed information about a single pool or a vector of pools ([#128])

## Changed

Expand Down Expand Up @@ -48,6 +49,7 @@ and this project adheres to
[#116]: https://github.com/Phoenix-Protocol-Group/phoenix-contracts/pull/116
[#118]: https://github.com/Phoenix-Protocol-Group/phoenix-contracts/pull/118
[#122]: https://github.com/Phoenix-Protocol-Group/phoenix-contracts/pull/122
[#128]: https://github.com/Phoenix-Protocol-Group/phoenix-contracts/pull/128

## [0.5.0] - 2023-08-04

Expand Down
45 changes: 44 additions & 1 deletion contracts/factory/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use soroban_sdk::{
contract, contractimpl, contractmeta, log, Address, Env, IntoVal, Symbol, Val, Vec,
};

use crate::storage::LiquidityPoolInfo;
use crate::{
error::ContractError,
storage::{get_admin, get_lp_vec, save_admin, save_lp_vec},
Expand All @@ -25,6 +26,13 @@ pub trait FactoryTrait {

fn query_pools(env: Env) -> Result<Vec<Address>, ContractError>;

fn query_pool_details(
env: Env,
pool_address: Address,
) -> Result<LiquidityPoolInfo, ContractError>;

fn query_all_pools_details(env: Env) -> Result<Vec<LiquidityPoolInfo>, ContractError>;

fn get_admin(env: Env) -> Result<Address, ContractError>;
}

Expand All @@ -50,7 +58,12 @@ impl FactoryTrait for Factory {
&lp_init_info.stake_init_info,
)?;

let lp_contract_address = deploy_lp_contract(&env, lp_init_info.lp_wasm_hash);
let lp_contract_address = deploy_lp_contract(
&env,
lp_init_info.lp_wasm_hash,
&lp_init_info.token_init_info.token_a,
&lp_init_info.token_init_info.token_b,
);

let init_fn: Symbol = Symbol::new(&env, "initialize");
let init_fn_args: Vec<Val> = (
Expand All @@ -64,6 +77,7 @@ impl FactoryTrait for Factory {
lp_init_info.stake_init_info,
)
.into_val(&env);

let _res: Val = env.invoke_contract(&lp_contract_address, &init_fn, init_fn_args);

let mut lp_vec = get_lp_vec(&env)?;
Expand All @@ -82,6 +96,35 @@ impl FactoryTrait for Factory {
get_lp_vec(&env)
}

fn query_pool_details(
env: Env,
pool_address: Address,
) -> Result<LiquidityPoolInfo, ContractError> {
let pool_response: LiquidityPoolInfo = env.invoke_contract(
&pool_address,
&Symbol::new(&env, "query_pool_info_for_factory"),
Vec::new(&env),
);

Ok(pool_response)
}

fn query_all_pools_details(env: Env) -> Result<Vec<LiquidityPoolInfo>, ContractError> {
let all_lp_vec_addresses = get_lp_vec(&env)?;
let mut result = Vec::new(&env);
for address in all_lp_vec_addresses {
let pool_response: LiquidityPoolInfo = env.invoke_contract(
&address,
&Symbol::new(&env, "query_pool_info_for_factory"),
Vec::new(&env),
);

result.push_back(pool_response);
}

Ok(result)
}

fn get_admin(env: Env) -> Result<Address, ContractError> {
get_admin(&env)
}
Expand Down
1 change: 1 addition & 0 deletions contracts/factory/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ pub enum ContractError {
LiquidityPoolVectorNotFound = 4,
MinStakeLessOrEqualZero = 5,
MinRewardTooSmall = 6,
ContractNotDeployed = 7,
}
35 changes: 32 additions & 3 deletions contracts/factory/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::ContractError;
use soroban_sdk::{Address, ConversionError, Env, TryFromVal, Val, Vec};
use soroban_sdk::{contracttype, Address, ConversionError, Env, TryFromVal, Val, Vec};

#[derive(Clone, Copy)]
#[repr(u32)]
Expand All @@ -17,6 +17,35 @@ impl TryFromVal<Env, DataKey> for Val {
}
}

#[contracttype]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Asset {
/// Address of the asset
pub address: Address,
/// The total amount of those tokens in the pool
pub amount: i128,
}

/// This struct is used to return a query result with the total amount of LP tokens and assets in a specific pool.
#[contracttype]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PoolResponse {
/// The asset A in the pool together with asset amounts
pub asset_a: Asset,
/// The asset B in the pool together with asset amounts
pub asset_b: Asset,
/// The total amount of LP tokens currently issued
pub asset_lp_share: Asset,
}

#[contracttype]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LiquidityPoolInfo {
pub pool_address: Address,
pub pool_response: PoolResponse,
pub total_fee_bps: i64,
}

pub fn save_admin(env: &Env, address: Address) {
env.storage().instance().set(&DataKey::Admin, &address);
}
Expand All @@ -35,8 +64,8 @@ pub fn get_lp_vec(env: &Env) -> Result<Vec<Address>, ContractError> {
.ok_or(ContractError::LiquidityPoolVectorNotFound)
}

pub fn save_lp_vec(env: &Env, lp_vec: Vec<Address>) {
env.storage().instance().set(&DataKey::LpVec, &lp_vec);
pub fn save_lp_vec(env: &Env, lp_info: Vec<Address>) {
env.storage().instance().set(&DataKey::LpVec, &lp_info);
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions contracts/factory/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
mod config;
mod setup;

mod queries;
224 changes: 224 additions & 0 deletions contracts/factory/src/tests/queries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
use super::setup::{
deploy_factory_contract, install_lp_contract, install_stake_wasm, install_token_wasm,
};
use phoenix::utils::{LiquidityPoolInitInfo, StakeInitInfo, TokenInitInfo};

use soroban_sdk::arbitrary::std;
use soroban_sdk::{contracttype, testutils::Address as _, Address, Env, Symbol, Vec};

#[contracttype]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum PairType {
Xyk = 0,
}
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Config {
pub token_a: Address,
pub token_b: Address,
pub share_token: Address,
pub stake_contract: Address,
pub pair_type: PairType,
/// The total fees (in bps) charged by a pair of this type.
/// In relation to the returned amount of tokens
pub total_fee_bps: i64,
pub fee_recipient: Address,
/// The maximum amount of slippage (in bps) that is tolerated during providing liquidity
pub max_allowed_slippage_bps: i64,
/// The maximum amount of spread (in bps) that is tolerated during swap
pub max_allowed_spread_bps: i64,
}

#[test]
fn test_deploy_multiple_liquidity_pools() {
let env = Env::default();
let admin = Address::random(&env);
let user = Address::random(&env);

let mut token1 = Address::random(&env);
let mut token2 = Address::random(&env);
let mut token3 = Address::random(&env);
let mut token4 = Address::random(&env);
let mut token5 = Address::random(&env);
let mut token6 = Address::random(&env);

env.mock_all_auths();
env.budget().reset_unlimited();

if token2 < token1 {
std::mem::swap(&mut token1, &mut token2);
}

if token4 < token3 {
std::mem::swap(&mut token3, &mut token4);
}

if token6 < token5 {
std::mem::swap(&mut token5, &mut token6);
}

let factory = deploy_factory_contract(&env, Some(admin.clone()));

let first_token_init_info = TokenInitInfo {
token_wasm_hash: install_token_wasm(&env),
token_a: token1.clone(),
token_b: token2.clone(),
};
let first_stake_init_info = StakeInitInfo {
stake_wasm_hash: install_stake_wasm(&env),
min_bond: 10i128,
max_distributions: 10u32,
min_reward: 5i128,
};

let second_token_init_info = TokenInitInfo {
token_wasm_hash: install_token_wasm(&env),
token_a: token3.clone(),
token_b: token4.clone(),
};
let second_stake_init_info = StakeInitInfo {
stake_wasm_hash: install_stake_wasm(&env),
min_bond: 5i128,
max_distributions: 5u32,
min_reward: 2i128,
};

let third_token_init_info = TokenInitInfo {
token_wasm_hash: install_token_wasm(&env),
token_a: token5.clone(),
token_b: token6.clone(),
};
let third_stake_init_info = StakeInitInfo {
stake_wasm_hash: install_stake_wasm(&env),
min_bond: 6i128,
max_distributions: 6u32,
min_reward: 3i128,
};

let lp_wasm_hash = install_lp_contract(&env);

let first_lp_init_info = LiquidityPoolInitInfo {
admin: admin.clone(),
fee_recipient: user.clone(),
lp_wasm_hash: lp_wasm_hash.clone(),
max_allowed_slippage_bps: 5_000,
max_allowed_spread_bps: 500,
share_token_decimals: 7,
swap_fee_bps: 0,
token_init_info: first_token_init_info.clone(),
stake_init_info: first_stake_init_info,
};

let second_lp_init_info = LiquidityPoolInitInfo {
admin: admin.clone(),
fee_recipient: user.clone(),
lp_wasm_hash: lp_wasm_hash.clone(),
max_allowed_slippage_bps: 4_000,
max_allowed_spread_bps: 400,
share_token_decimals: 6,
swap_fee_bps: 0,
token_init_info: second_token_init_info,
stake_init_info: second_stake_init_info,
};

let third_lp_init_info = LiquidityPoolInitInfo {
admin: admin.clone(),
fee_recipient: user.clone(),
lp_wasm_hash,
max_allowed_slippage_bps: 4_000,
max_allowed_spread_bps: 400,
share_token_decimals: 6,
swap_fee_bps: 0,
token_init_info: third_token_init_info,
stake_init_info: third_stake_init_info,
};

factory.create_liquidity_pool(&first_lp_init_info);
factory.create_liquidity_pool(&second_lp_init_info);
factory.create_liquidity_pool(&third_lp_init_info);

let lp_contract_addr = factory.query_pools().get(0).unwrap();
let first_result = factory.query_pool_details(&lp_contract_addr);
let share_token_addr: Address = env.invoke_contract(
&lp_contract_addr,
&Symbol::new(&env, "query_share_token_address"),
Vec::new(&env),
);
let first_lp_config: Config = env.invoke_contract(
&lp_contract_addr,
&Symbol::new(&env, "query_config"),
Vec::new(&env),
);

assert_eq!(
first_lp_init_info.max_allowed_spread_bps,
first_lp_config.max_allowed_spread_bps
);

assert_eq!(token1, first_result.pool_response.asset_a.address);
assert_eq!(token2, first_result.pool_response.asset_b.address);
assert_eq!(
share_token_addr,
first_result.pool_response.asset_lp_share.address
);
assert_eq!(lp_contract_addr, first_result.pool_address);

let second_lp_contract_addr = factory.query_pools().get(1).unwrap();
let second_result = factory.query_pool_details(&second_lp_contract_addr);
let second_share_token_addr: Address = env.invoke_contract(
&second_lp_contract_addr,
&Symbol::new(&env, "query_share_token_address"),
Vec::new(&env),
);
let second_lp_config: Config = env.invoke_contract(
&second_lp_contract_addr,
&Symbol::new(&env, "query_config"),
Vec::new(&env),
);

assert_eq!(
second_lp_init_info.max_allowed_spread_bps,
second_lp_config.max_allowed_spread_bps
);

assert_eq!(token3, second_result.pool_response.asset_a.address);
assert_eq!(token4, second_result.pool_response.asset_b.address);
assert_eq!(
second_share_token_addr,
second_result.pool_response.asset_lp_share.address
);
assert_eq!(second_lp_contract_addr, second_result.pool_address);

let third_lp_contract_addr = factory.query_pools().get(2).unwrap();
let third_result = factory.query_pool_details(&third_lp_contract_addr);
let third_share_token_addr: Address = env.invoke_contract(
&third_lp_contract_addr,
&Symbol::new(&env, "query_share_token_address"),
Vec::new(&env),
);
let third_lp_config: Config = env.invoke_contract(
&third_lp_contract_addr,
&Symbol::new(&env, "query_config"),
Vec::new(&env),
);

assert_eq!(
third_lp_init_info.max_allowed_spread_bps,
third_lp_config.max_allowed_spread_bps
);

assert_eq!(token5, third_result.pool_response.asset_a.address);
assert_eq!(token6, third_result.pool_response.asset_b.address);
assert_eq!(
third_share_token_addr,
third_result.pool_response.asset_lp_share.address
);
assert_eq!(third_lp_contract_addr, third_result.pool_address);

let all_pools = factory.query_all_pools_details();
assert_eq!(all_pools.len(), 3);
all_pools.iter().for_each(|pool| {
assert!(all_pools.contains(pool));
});
}
Loading

0 comments on commit b4eb70b

Please sign in to comment.