|
1 | 1 | #![no_std] |
2 | 2 |
|
| 3 | +#[cfg(test)] |
3 | 4 | mod test; |
4 | 5 | #[cfg(test)] |
5 | 6 | mod fuzz_math; |
@@ -104,8 +105,15 @@ impl YieldVault { |
104 | 105 | return Err(VaultError::AlreadyInitialized); |
105 | 106 | } |
106 | 107 |
|
| 108 | + let state = VaultState { |
| 109 | + total_shares: 0, |
| 110 | + total_assets: 0, |
| 111 | + is_paused: false, |
| 112 | + }; |
| 113 | + |
107 | 114 | env.storage().instance().set(&DataKey::Admin, &admin); |
108 | 115 | env.storage().instance().set(&DataKey::TokenAsset, &token); |
| 116 | + env.storage().instance().set(&DataKey::State, &state); |
109 | 117 | env.storage().instance().set(&DataKey::TotalAssets, &0i128); |
110 | 118 | env.storage().instance().set(&DataKey::TotalShares, &0i128); |
111 | 119 |
|
@@ -165,8 +173,9 @@ impl YieldVault { |
165 | 173 | Self::get_state(&env).total_shares |
166 | 174 | } |
167 | 175 |
|
168 | | - /// Read the total underlying assets (idle in vault + invested in strategy). |
| 176 | + /// Read the total underlying assets represented by the vault. |
169 | 177 | pub fn total_assets(env: Env) -> i128 { |
| 178 | + Self::get_state(&env).total_assets |
170 | 179 | let idle_assets = env.storage().instance().get::<_, i128>(&DataKey::TotalAssets).unwrap_or(0); |
171 | 180 |
|
172 | 181 | let strategy_assets = if let Some(strategy_addr) = Self::strategy(env.clone()) { |
@@ -723,6 +732,77 @@ impl YieldVault { |
723 | 732 | pub fn accrue_yield(env: Env, amount: i128) -> Result<(), VaultError> { |
724 | 733 | let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap(); |
725 | 734 | admin.require_auth(); |
| 735 | + |
| 736 | + let strategy_addr = Self::strategy(env.clone()).expect("no strategy set"); |
| 737 | + let strategy_client = StrategyClient::new(&env, &strategy_addr); |
| 738 | + |
| 739 | + let mut idle_ta = env.storage().instance().get::<_, i128>(&DataKey::TotalAssets).unwrap_or(0); |
| 740 | + if idle_ta < amount { panic!("insufficient idle assets"); } |
| 741 | + |
| 742 | + // Approve and deposit to strategy |
| 743 | + let token_addr = Self::token(env.clone()); |
| 744 | + let token_client = token::Client::new(&env, &token_addr); |
| 745 | + token_client.approve(&env.current_contract_address(), &strategy_addr, &amount, &env.ledger().sequence()); |
| 746 | + |
| 747 | + strategy_client.deposit(&amount); |
| 748 | + |
| 749 | + // Update idle assets |
| 750 | + env.storage().instance().set(&DataKey::TotalAssets, &(idle_ta - amount)); |
| 751 | + } |
| 752 | + |
| 753 | + /// Recall funds from the strategy. |
| 754 | + pub fn divest(env: Env, amount: i128) { |
| 755 | + // Can be called by admin or internally by withdraw |
| 756 | + let strategy_addr = Self::strategy(env.clone()).expect("no strategy set"); |
| 757 | + let strategy_client = StrategyClient::new(&env, &strategy_addr); |
| 758 | + |
| 759 | + strategy_client.withdraw(&amount); |
| 760 | + |
| 761 | + // The strategy contract should have transferred funds back to the vault |
| 762 | + let idle_ta = env.storage().instance().get::<_, i128>(&DataKey::TotalAssets).unwrap_or(0); |
| 763 | + env.storage().instance().set(&DataKey::TotalAssets, &(idle_ta + amount)); |
| 764 | + } |
| 765 | + |
| 766 | + /// Admin function to distribute realized yield into the vault. |
| 767 | + /// |
| 768 | + /// Yield increases `total_assets` without minting any new shares, which |
| 769 | + /// raises the share price for existing holders. |
| 770 | + pub fn distribute_yield(env: Env, amount: i128) { |
| 771 | + let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap(); |
| 772 | + admin.require_auth(); |
| 773 | + if amount <= 0 { |
| 774 | + panic!("yield amount must be > 0"); |
| 775 | + } |
| 776 | + |
| 777 | + let token_addr = Self::token(env.clone()); |
| 778 | + let token_client = token::Client::new(&env, &token_addr); |
| 779 | + |
| 780 | + token_client.transfer(&admin, &env.current_contract_address(), &amount); |
| 781 | + |
| 782 | + let ta = env |
| 783 | + .storage() |
| 784 | + .instance() |
| 785 | + .get::<_, i128>(&DataKey::TotalAssets) |
| 786 | + .unwrap_or(0); |
| 787 | + env.storage().instance().set(&DataKey::TotalAssets, &(ta + amount)); |
| 788 | + |
| 789 | + let mut state = Self::get_state(&env); |
| 790 | + state.total_assets += amount; |
| 791 | + env.storage().instance().set(&DataKey::State, &state); |
| 792 | + |
| 793 | + env.events().publish( |
| 794 | + (symbol_short!("yielddist"), admin), |
| 795 | + (amount, state.total_assets, state.total_shares), |
| 796 | + ); |
| 797 | + } |
| 798 | + |
| 799 | + /// Legacy admin function retained for compatibility. |
| 800 | + pub fn accrue_yield(env: Env, amount: i128) { |
| 801 | + Self::distribute_yield(env, amount); |
| 802 | + } |
| 803 | + |
| 804 | + pub fn report_benji_yield(env: Env, strategy: Address, amount: i128) { |
| 805 | + strategy.require_auth(); |
726 | 806 | if amount <= 0 { |
727 | 807 | return Err(VaultError::InvalidAmount); |
728 | 808 | } |
|
0 commit comments