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

blend strategy #171

Merged
merged 3 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/contracts/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions apps/contracts/strategies/blend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "blend_strategy"
version = { workspace = true }
authors = ["coderipper <[email protected]>"]
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"] }
17 changes: 17 additions & 0 deletions apps/contracts/strategies/blend/Makefile
Original file line number Diff line number Diff line change
@@ -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
63 changes: 63 additions & 0 deletions apps/contracts/strategies/blend/src/blend_pool.rs
Original file line number Diff line number Diff line change
@@ -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<Request> = 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)
}
125 changes: 125 additions & 0 deletions apps/contracts/strategies/blend/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<Val>,
) -> 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<Address, StrategyError> {
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<i128, StrategyError> {
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<i128, StrategyError> {
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;
47 changes: 47 additions & 0 deletions apps/contracts/strategies/blend/src/storage.rs
Original file line number Diff line number Diff line change
@@ -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()
}
79 changes: 79 additions & 0 deletions apps/contracts/strategies/blend/src/test.rs
Original file line number Diff line number Diff line change
@@ -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<Val>) -> 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<Address> {
// let mut users = vec![];
// for _c in 0..users_count {
// users.push(Address::generate(e));
// }
// users
// }
}

mod initialize;
mod deposit;
mod events;
mod withdraw;
Loading
Loading