11#![ no_std]
22
33mod test;
4+ pub mod strategy;
5+ pub mod benji_strategy;
46
57use soroban_sdk:: {
68 contract, contractclient, contracterror, contractimpl, contracttype, symbol_short, token,
79 Address , Env , Vec ,
810} ;
11+ use crate :: strategy:: StrategyClient ;
912
1013const MAX_PAGE_SIZE : u32 = 50 ;
1114
@@ -40,6 +43,7 @@ pub enum DataKey {
4043 TotalShares ,
4144 TotalAssets ,
4245 Admin ,
46+ Strategy ,
4347 State ,
4448 DaoThreshold ,
4549 ProposalNonce ,
@@ -97,6 +101,21 @@ impl YieldVault {
97101
98102 env. storage ( ) . instance ( ) . set ( & DataKey :: Admin , & admin) ;
99103 env. storage ( ) . instance ( ) . set ( & DataKey :: TokenAsset , & token) ;
104+ env. storage ( ) . instance ( ) . set ( & DataKey :: TotalAssets , & 0i128 ) ;
105+ env. storage ( ) . instance ( ) . set ( & DataKey :: DaoThreshold , & 1i128 ) ;
106+ env. storage ( ) . instance ( ) . set ( & DataKey :: ProposalNonce , & 0u32 ) ;
107+ }
108+
109+ /// Set or update the active strategy connector.
110+ pub fn set_strategy ( env : Env , strategy : Address ) {
111+ let admin: Address = env. storage ( ) . instance ( ) . get ( & DataKey :: Admin ) . unwrap ( ) ;
112+ admin. require_auth ( ) ;
113+ env. storage ( ) . instance ( ) . set ( & DataKey :: Strategy , & strategy) ;
114+ }
115+
116+ /// Read the active strategy address.
117+ pub fn strategy ( env : Env ) -> Option < Address > {
118+ env. storage ( ) . instance ( ) . get ( & DataKey :: Strategy )
100119
101120 // Initialize the unified state
102121 let state = VaultState {
@@ -144,6 +163,18 @@ impl YieldVault {
144163 Self :: get_state ( & env) . total_shares
145164 }
146165
166+ /// Read the total underlying assets (idle in vault + invested in strategy).
167+ pub fn total_assets ( env : Env ) -> i128 {
168+ let idle_assets = env. storage ( ) . instance ( ) . get :: < _ , i128 > ( & DataKey :: TotalAssets ) . unwrap_or ( 0 ) ;
169+
170+ let strategy_assets = if let Some ( strategy_addr) = Self :: strategy ( env. clone ( ) ) {
171+ let strategy_client = StrategyClient :: new ( & env, & strategy_addr) ;
172+ strategy_client. total_value ( )
173+ } else {
174+ 0
175+ } ;
176+
177+ idle_assets + strategy_assets
147178 pub fn total_assets ( env : Env ) -> i128 {
148179 Self :: get_state ( & env) . total_assets
149180 }
@@ -483,6 +514,12 @@ impl YieldVault {
483514
484515 token_client. transfer ( & user, & env. current_contract_address ( ) , & amount) ;
485516
517+ // Update idle state
518+ let ta = env. storage ( ) . instance ( ) . get :: < _ , i128 > ( & DataKey :: TotalAssets ) . unwrap_or ( 0 ) ;
519+ env. storage ( ) . instance ( ) . set ( & DataKey :: TotalAssets , & ( ta + amount) ) ;
520+
521+ let ts = Self :: total_shares ( env. clone ( ) ) ;
522+ env. storage ( ) . instance ( ) . set ( & DataKey :: TotalShares , & ( ts + shares_to_mint) ) ;
486523 state. total_assets += amount;
487524 state. total_shares += shares_to_mint;
488525 env. storage ( ) . instance ( ) . set ( & DataKey :: State , & state) ;
@@ -532,6 +569,26 @@ impl YieldVault {
532569
533570 let token_addr = Self :: token ( env. clone ( ) ) ;
534571 let token_client = token:: Client :: new ( & env, & token_addr) ;
572+
573+ // Check if vault has enough idle assets, otherwise divest from strategy
574+ let mut idle_ta = env. storage ( ) . instance ( ) . get :: < _ , i128 > ( & DataKey :: TotalAssets ) . unwrap_or ( 0 ) ;
575+ if idle_ta < assets_to_return {
576+ let needed = assets_to_return - idle_ta;
577+ Self :: divest ( env. clone ( ) , needed) ;
578+ idle_ta = env. storage ( ) . instance ( ) . get :: < _ , i128 > ( & DataKey :: TotalAssets ) . unwrap_or ( 0 ) ;
579+ }
580+
581+ // Transfer assets from vault to user
582+ token_client. transfer ( & env. current_contract_address ( ) , & user, & assets_to_return) ;
583+
584+ // Update state
585+ env. storage ( ) . instance ( ) . set ( & DataKey :: TotalAssets , & ( idle_ta - assets_to_return) ) ;
586+
587+ let ts = Self :: total_shares ( env. clone ( ) ) ;
588+ env. storage ( ) . instance ( ) . set ( & DataKey :: TotalShares , & ( ts - shares) ) ;
589+
590+ let vault_balance = Self :: balance ( env. clone ( ) , user. clone ( ) ) ;
591+ env. storage ( ) . instance ( ) . set ( & DataKey :: ShareBalance ( user. clone ( ) ) , & ( vault_balance - shares) ) ;
535592 token_client. transfer ( & env. current_contract_address ( ) , & user, & assets_to_return) ;
536593
537594 state. total_assets -= assets_to_return;
@@ -549,12 +606,53 @@ impl YieldVault {
549606 Ok ( assets_to_return)
550607 }
551608
609+ /// Move idle funds to the strategy.
610+ pub fn invest ( env : Env , amount : i128 ) {
611+ let admin: Address = env. storage ( ) . instance ( ) . get ( & DataKey :: Admin ) . unwrap ( ) ;
612+ admin. require_auth ( ) ;
613+
614+ let strategy_addr = Self :: strategy ( env. clone ( ) ) . expect ( "no strategy set" ) ;
615+ let strategy_client = StrategyClient :: new ( & env, & strategy_addr) ;
616+
617+ let mut idle_ta = env. storage ( ) . instance ( ) . get :: < _ , i128 > ( & DataKey :: TotalAssets ) . unwrap_or ( 0 ) ;
618+ if idle_ta < amount { panic ! ( "insufficient idle assets" ) ; }
619+
620+ // Approve and deposit to strategy
621+ let token_addr = Self :: token ( env. clone ( ) ) ;
622+ let token_client = token:: Client :: new ( & env, & token_addr) ;
623+ token_client. approve ( & env. current_contract_address ( ) , & strategy_addr, & amount, & env. ledger ( ) . sequence ( ) ) ;
624+
625+ strategy_client. deposit ( & amount) ;
626+
627+ // Update idle assets
628+ env. storage ( ) . instance ( ) . set ( & DataKey :: TotalAssets , & ( idle_ta - amount) ) ;
629+ }
630+
631+ /// Recall funds from the strategy.
632+ pub fn divest ( env : Env , amount : i128 ) {
633+ // Can be called by admin or internally by withdraw
634+ let strategy_addr = Self :: strategy ( env. clone ( ) ) . expect ( "no strategy set" ) ;
635+ let strategy_client = StrategyClient :: new ( & env, & strategy_addr) ;
636+
637+ strategy_client. withdraw ( & amount) ;
638+
639+ // The strategy contract should have transferred funds back to the vault
640+ let idle_ta = env. storage ( ) . instance ( ) . get :: < _ , i128 > ( & DataKey :: TotalAssets ) . unwrap_or ( 0 ) ;
641+ env. storage ( ) . instance ( ) . set ( & DataKey :: TotalAssets , & ( idle_ta + amount) ) ;
642+ }
643+
644+ /// Admin function to artificially accrue yield (legacy, but updated for strategy).
552645 pub fn accrue_yield ( env : Env , amount : i128 ) {
553646 let admin: Address = env. storage ( ) . instance ( ) . get ( & DataKey :: Admin ) . unwrap ( ) ;
554647 admin. require_auth ( ) ;
555648
556649 let token_addr = Self :: token ( env. clone ( ) ) ;
557650 let token_client = token:: Client :: new ( & env, & token_addr) ;
651+
652+ token_client. transfer ( & admin, & env. current_contract_address ( ) , & amount) ;
653+
654+ let ta = env. storage ( ) . instance ( ) . get :: < _ , i128 > ( & DataKey :: TotalAssets ) . unwrap_or ( 0 ) ;
655+ env. storage ( ) . instance ( ) . set ( & DataKey :: TotalAssets , & ( ta + amount) ) ;
558656 token_client. transfer ( & admin, & env. current_contract_address ( ) , & amount) ;
559657
560658 let mut state = Self :: get_state ( & env) ;
0 commit comments