Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Factory: enhance query_pool #128

Merged
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
Loading