From da6185ce71ba5f57a2e1aea9ada9b3f349a87903 Mon Sep 17 00:00:00 2001 From: coderipper Date: Wed, 6 Nov 2024 16:04:15 -0300 Subject: [PATCH] blend strategy --- apps/contracts/Cargo.lock | 8 ++ apps/contracts/strategies/blend/Cargo.toml | 18 +++ apps/contracts/strategies/blend/Makefile | 17 +++ .../strategies/blend/src/blend_pool.rs | 63 +++++++++ apps/contracts/strategies/blend/src/lib.rs | 125 ++++++++++++++++++ .../contracts/strategies/blend/src/storage.rs | 47 +++++++ apps/contracts/strategies/blend/src/test.rs | 79 +++++++++++ .../strategies/blend/src/test/deposit.rs | 79 +++++++++++ .../strategies/blend/src/test/events.rs | 6 + .../strategies/blend/src/test/initialize.rs | 21 +++ .../strategies/blend/src/test/withdraw.rs | 5 + .../external_wasms/blend/blend_pool.wasm | Bin 0 -> 49929 bytes 12 files changed, 468 insertions(+) create mode 100644 apps/contracts/strategies/blend/Cargo.toml create mode 100644 apps/contracts/strategies/blend/Makefile create mode 100644 apps/contracts/strategies/blend/src/blend_pool.rs create mode 100644 apps/contracts/strategies/blend/src/lib.rs create mode 100644 apps/contracts/strategies/blend/src/storage.rs create mode 100644 apps/contracts/strategies/blend/src/test.rs create mode 100644 apps/contracts/strategies/blend/src/test/deposit.rs create mode 100644 apps/contracts/strategies/blend/src/test/events.rs create mode 100644 apps/contracts/strategies/blend/src/test/initialize.rs create mode 100644 apps/contracts/strategies/blend/src/test/withdraw.rs create mode 100644 apps/contracts/strategies/external_wasms/blend/blend_pool.wasm diff --git a/apps/contracts/Cargo.lock b/apps/contracts/Cargo.lock index 889ab1d3..ae896cbe 100644 --- a/apps/contracts/Cargo.lock +++ b/apps/contracts/Cargo.lock @@ -92,6 +92,14 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "blend_strategy" +version = "1.0.0" +dependencies = [ + "defindex-strategy-core", + "soroban-sdk", +] + [[package]] name = "block-buffer" version = "0.10.4" diff --git a/apps/contracts/strategies/blend/Cargo.toml b/apps/contracts/strategies/blend/Cargo.toml new file mode 100644 index 00000000..cf1a7b29 --- /dev/null +++ b/apps/contracts/strategies/blend/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "blend_strategy" +version = { workspace = true } +authors = ["coderipper "] +license = { workspace = true } +edition = { workspace = true } +publish = false +repository = { workspace = true } + +[lib] +crate-type = ["cdylib"] + +[dependencies] +soroban-sdk = { workspace = true } +defindex-strategy-core = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/apps/contracts/strategies/blend/Makefile b/apps/contracts/strategies/blend/Makefile new file mode 100644 index 00000000..4dd2581a --- /dev/null +++ b/apps/contracts/strategies/blend/Makefile @@ -0,0 +1,17 @@ +default: build + +all: test + +test: build + cargo test + +build: + cargo build --target wasm32-unknown-unknown --release + soroban contract optimize --wasm ../../target/wasm32-unknown-unknown/release/blend_strategy.wasm + @rm ../../target/wasm32-unknown-unknown/release/blend_strategy.wasm + +fmt: + cargo fmt --all --check + +clean: + cargo clean \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/blend_pool.rs b/apps/contracts/strategies/blend/src/blend_pool.rs new file mode 100644 index 00000000..69e25274 --- /dev/null +++ b/apps/contracts/strategies/blend/src/blend_pool.rs @@ -0,0 +1,63 @@ +use soroban_sdk::{vec, Address, Env, Vec}; + +use crate::storage::{get_blend_pool, get_underlying_asset}; + +soroban_sdk::contractimport!( + file = "../external_wasms/blend/blend_pool.wasm" +); +pub type BlendPoolClient<'a> = Client<'a>; + +// Define the RequestType enum with explicit u32 values +#[derive(Clone, PartialEq)] +#[repr(u32)] +pub enum RequestType { + // Supply = 0, + // Withdraw = 1, + SupplyCollateral = 2, + WithdrawCollateral = 3, + // Borrow = 4, + // Repay = 5, + // FillUserLiquidationAuction = 6, + // FillBadDebtAuction = 7, + // FillInterestAuction = 8, + // DeleteLiquidationAuction = 9, +} + +// Implement a method to convert RequestType to u32 +impl RequestType { + fn to_u32(self) -> u32 { + self as u32 + } +} + +pub fn submit(e: &Env, from: &Address, amount: i128, request_type: RequestType) -> Positions { + // Setting up Blend Pool client + let blend_pool_address = get_blend_pool(e); + let blend_pool_client = BlendPoolClient::new(e, &blend_pool_address); + + let underlying_asset = get_underlying_asset(&e); + + let requests: Vec = vec![&e, Request { + address: underlying_asset, + amount: amount, + request_type: request_type.to_u32(), + }]; + + blend_pool_client.submit(from, from, from, &requests) +} + +pub fn claim(e: &Env, from: &Address) -> i128 { + // Setting up Blend Pool client + let blend_pool_address = get_blend_pool(e); + let blend_pool_client = BlendPoolClient::new(e, &blend_pool_address); + + blend_pool_client.claim(from, &vec![&e, 3u32], from) +} + +pub fn get_positions(e: &Env, from: &Address) -> Positions { + // Setting up Blend Pool client + let blend_pool_address = get_blend_pool(e); + let blend_pool_client = BlendPoolClient::new(e, &blend_pool_address); + + blend_pool_client.get_positions(from) +} \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/lib.rs b/apps/contracts/strategies/blend/src/lib.rs new file mode 100644 index 00000000..1cf33d72 --- /dev/null +++ b/apps/contracts/strategies/blend/src/lib.rs @@ -0,0 +1,125 @@ +#![no_std] +use blend_pool::RequestType; +use soroban_sdk::{ + contract, contractimpl, Address, Env, IntoVal, String, Val, Vec}; + +mod blend_pool; +mod storage; + +use storage::{ + extend_instance_ttl, get_underlying_asset, is_initialized, set_blend_pool, set_initialized, set_underlying_asset +}; + +pub use defindex_strategy_core::{ + DeFindexStrategyTrait, + StrategyError, + event}; + +pub fn check_nonnegative_amount(amount: i128) -> Result<(), StrategyError> { + if amount < 0 { + Err(StrategyError::NegativeNotAllowed) + } else { + Ok(()) + } +} + +fn check_initialized(e: &Env) -> Result<(), StrategyError> { + if is_initialized(e) { + Ok(()) + } else { + Err(StrategyError::NotInitialized) + } +} + +const STARETEGY_NAME: &str = "BlendStrategy"; + +#[contract] +struct BlendStrategy; + +#[contractimpl] +impl DeFindexStrategyTrait for BlendStrategy { + fn initialize( + e: Env, + asset: Address, + init_args: Vec, + ) -> Result<(), StrategyError> { + if is_initialized(&e) { + return Err(StrategyError::AlreadyInitialized); + } + + let blend_pool_address = init_args.get(0).ok_or(StrategyError::InvalidArgument)?.into_val(&e); + + set_initialized(&e); + set_blend_pool(&e, blend_pool_address); + set_underlying_asset(&e, &asset); + + event::emit_initialize(&e, String::from_str(&e, STARETEGY_NAME), asset); + extend_instance_ttl(&e); + Ok(()) + } + + fn asset(e: Env) -> Result { + check_initialized(&e)?; + extend_instance_ttl(&e); + + Ok(get_underlying_asset(&e)) + } + + fn deposit( + e: Env, + amount: i128, + from: Address, + ) -> Result<(), StrategyError> { + check_initialized(&e)?; + check_nonnegative_amount(amount)?; + extend_instance_ttl(&e); + from.require_auth(); + + blend_pool::submit(&e, &from, amount, RequestType::SupplyCollateral); + + event::emit_deposit(&e, String::from_str(&e, STARETEGY_NAME), amount, from); + Ok(()) + } + + fn harvest(e: Env, from: Address) -> Result<(), StrategyError> { + check_initialized(&e)?; + extend_instance_ttl(&e); + + blend_pool::claim(&e, &from); + + event::emit_harvest(&e, String::from_str(&e, STARETEGY_NAME), 0i128, from); + Ok(()) + } + + fn withdraw( + e: Env, + amount: i128, + from: Address, + ) -> Result { + from.require_auth(); + check_initialized(&e)?; + check_nonnegative_amount(amount)?; + extend_instance_ttl(&e); + + blend_pool::submit(&e, &from, amount, RequestType::WithdrawCollateral); + + event::emit_withdraw(&e, String::from_str(&e, STARETEGY_NAME), amount, from); + + Ok(amount) + } + + fn balance( + e: Env, + from: Address, + ) -> Result { + check_initialized(&e)?; + extend_instance_ttl(&e); + + let positions = blend_pool::get_positions(&e, &from); + + let collateral = positions.collateral.get(1u32).unwrap_or(0i128); + Ok(collateral) + } +} + +mod test; \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/storage.rs b/apps/contracts/strategies/blend/src/storage.rs new file mode 100644 index 00000000..dbdc3239 --- /dev/null +++ b/apps/contracts/strategies/blend/src/storage.rs @@ -0,0 +1,47 @@ +use soroban_sdk::{contracttype, Address, Env}; + +#[derive(Clone)] +#[contracttype] + +pub enum DataKey { + Initialized, + UnderlyingAsset, + BlendPool, + Balance(Address) +} + +const DAY_IN_LEDGERS: u32 = 17280; +pub const INSTANCE_BUMP_AMOUNT: u32 = 30 * DAY_IN_LEDGERS; +pub const INSTANCE_LIFETIME_THRESHOLD: u32 = INSTANCE_BUMP_AMOUNT - DAY_IN_LEDGERS; + +pub fn extend_instance_ttl(e: &Env) { + e.storage() + .instance() + .extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); +} + +pub fn set_initialized(e: &Env) { + e.storage().instance().set(&DataKey::Initialized, &true); +} + +pub fn is_initialized(e: &Env) -> bool { + e.storage().instance().has(&DataKey::Initialized) +} + +// Underlying asset +pub fn set_underlying_asset(e: &Env, address: &Address) { + e.storage().instance().set(&DataKey::UnderlyingAsset, &address); +} + +pub fn get_underlying_asset(e: &Env) -> Address { + e.storage().instance().get(&DataKey::UnderlyingAsset).unwrap() +} + +// Blend Pool Address +pub fn set_blend_pool(e: &Env, address: Address) { + e.storage().instance().set(&DataKey::BlendPool, &address); +} + +pub fn get_blend_pool(e: &Env) -> Address { + e.storage().instance().get(&DataKey::BlendPool).unwrap() +} \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test.rs b/apps/contracts/strategies/blend/src/test.rs new file mode 100644 index 00000000..ba5282eb --- /dev/null +++ b/apps/contracts/strategies/blend/src/test.rs @@ -0,0 +1,79 @@ +#![cfg(test)] +use crate::{BlendStrategy, BlendStrategyClient, StrategyError}; + +use soroban_sdk::token::{TokenClient, StellarAssetClient}; + +use soroban_sdk::{ + Env, + Address, + testutils::Address as _, +}; + +// mod blend_pool_module { +// soroban_sdk::contractimport!(file = "../external_wasms/blend/blend_pool.wasm"); +// pub type BlendPoolContractClient<'a> = Client<'a>; +// } + +// use blend_pool_module::BlendPoolContractClient; + +// // fn initialize(admin: address, name: string, oracle: address, bstop_rate: u32, max_postions: u32, backstop_id: address, blnd_id: address) + +// fn create_blend_pool_contract<'a>(e: &Env, asset: &Address, init_args: &Vec) -> BlendPoolContractClient<'a> { +// let address = &e.register_contract_wasm(None, hodl_strategy::WASM); +// let strategy = BlendPoolContractClient::new(e, address); +// strategy.initialize(asset, init_args); +// strategy +// } + +// Blend Strategy Contract +fn create_blend_strategy<'a>(e: &Env) -> BlendStrategyClient<'a> { + BlendStrategyClient::new(e, &e.register_contract(None, BlendStrategy {})) +} + +// Create Test Token +pub(crate) fn create_token_contract<'a>(e: &Env, admin: &Address) -> TokenClient<'a> { + TokenClient::new(e, &e.register_stellar_asset_contract_v2(admin.clone()).address()) +} + +pub struct HodlStrategyTest<'a> { + env: Env, + strategy: BlendStrategyClient<'a>, + token: TokenClient<'a>, + user: Address, +} + +impl<'a> HodlStrategyTest<'a> { + fn setup() -> Self { + + let env = Env::default(); + env.mock_all_auths(); + + let strategy = create_blend_strategy(&env); + let admin = Address::generate(&env); + let token = create_token_contract(&env, &admin); + let user = Address::generate(&env); + + // Mint 1,000,000,000 to user + StellarAssetClient::new(&env, &token.address).mint(&user, &1_000_000_000); + + HodlStrategyTest { + env, + strategy, + token, + user + } + } + + // pub(crate) fn generate_random_users(e: &Env, users_count: u32) -> vec::Vec
{ + // let mut users = vec![]; + // for _c in 0..users_count { + // users.push(Address::generate(e)); + // } + // users + // } +} + +mod initialize; +mod deposit; +mod events; +mod withdraw; \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test/deposit.rs b/apps/contracts/strategies/blend/src/test/deposit.rs new file mode 100644 index 00000000..3267f974 --- /dev/null +++ b/apps/contracts/strategies/blend/src/test/deposit.rs @@ -0,0 +1,79 @@ +use crate::test::HodlStrategyTest; +use crate::test::StrategyError; +use soroban_sdk::{IntoVal, Vec, Val}; + +// test deposit with negative amount +#[test] +fn deposit_with_negative_amount() { + let test = HodlStrategyTest::setup(); + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + + let amount = -123456; + + let result = test.strategy.try_deposit(&amount, &test.user); + assert_eq!(result, Err(Ok(StrategyError::NegativeNotAllowed))); +} + +// check auth +#[test] +fn deposit_mock_auths() { + todo!() +} + +#[test] +fn deposit_and_withdrawal_flow() { + let test = HodlStrategyTest::setup(); + // let users = HodlStrategyTest::generate_random_users(&test.env, 1); + + // try deposit should return NotInitialized error before being initialize + + let result = test.strategy.try_deposit(&10_000_000, &test.user); + assert_eq!(result, Err(Ok(StrategyError::NotInitialized))); + + // initialize + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + + // Initial user token balance + let balance = test.token.balance(&test.user); + + let amount = 123456; + + // Deposit amount of token from the user to the strategy + test.strategy.deposit(&amount, &test.user); + + let balance_after_deposit = test.token.balance(&test.user); + assert_eq!(balance_after_deposit, balance - amount); + + // Reading strategy balance + let strategy_balance_after_deposit = test.token.balance(&test.strategy.address); + assert_eq!(strategy_balance_after_deposit, amount); + + // Reading user balance on strategy contract + let user_balance_on_strategy = test.strategy.balance(&test.user); + assert_eq!(user_balance_on_strategy, amount); + + + let amount_to_withdraw = 100_000; + // Withdrawing token from the strategy to user + test.strategy.withdraw(&amount_to_withdraw, &test.user); + + // Reading user balance in token + let balance = test.token.balance(&test.user); + assert_eq!(balance, balance_after_deposit + amount_to_withdraw); + + // Reading strategy balance in token + let balance = test.token.balance(&test.strategy.address); + assert_eq!(balance, amount - amount_to_withdraw); + + // Reading user balance on strategy contract + let user_balance = test.strategy.balance(&test.user); + assert_eq!(user_balance, amount - amount_to_withdraw); + + // now we will want to withdraw more of the remaining balance + let amount_to_withdraw = 200_000; + let result = test.strategy.try_withdraw(&amount_to_withdraw, &test.user); + assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance))); + +} \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test/events.rs b/apps/contracts/strategies/blend/src/test/events.rs new file mode 100644 index 00000000..239a9bd1 --- /dev/null +++ b/apps/contracts/strategies/blend/src/test/events.rs @@ -0,0 +1,6 @@ +// TODO: Write tests for events + +#[test] +fn test_events() { + todo!() +} \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test/initialize.rs b/apps/contracts/strategies/blend/src/test/initialize.rs new file mode 100644 index 00000000..41037473 --- /dev/null +++ b/apps/contracts/strategies/blend/src/test/initialize.rs @@ -0,0 +1,21 @@ +// Cannot Initialize twice +extern crate std; +use soroban_sdk::{IntoVal, Vec, Val}; +use crate::test::HodlStrategyTest; +use crate::test::StrategyError; + +#[test] +fn cannot_initialize_twice() { + let test = HodlStrategyTest::setup(); + + let init_fn_args: Vec = (0,).into_val(&test.env); + + test.strategy.initialize(&test.token.address, &init_fn_args); + let result = test.strategy.try_initialize(&test.token.address , &init_fn_args); + assert_eq!(result, Err(Ok(StrategyError::AlreadyInitialized))); + + // get asset should return underlying asset + + let underlying_asset = test.strategy.asset(); + assert_eq!(underlying_asset, test.token.address); +} \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test/withdraw.rs b/apps/contracts/strategies/blend/src/test/withdraw.rs new file mode 100644 index 00000000..dd8aa9d5 --- /dev/null +++ b/apps/contracts/strategies/blend/src/test/withdraw.rs @@ -0,0 +1,5 @@ + +#[test] +fn withdraw() { + todo!() +} \ No newline at end of file diff --git a/apps/contracts/strategies/external_wasms/blend/blend_pool.wasm b/apps/contracts/strategies/external_wasms/blend/blend_pool.wasm new file mode 100644 index 0000000000000000000000000000000000000000..522dcccb48e3dcf3e8a835605f8c36062e37e756 GIT binary patch literal 49929 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 literal 0 HcmV?d00001