11"""Umami gmUSDC vault support."""
22
33import datetime
4+ from decimal import Decimal
45from functools import cached_property
56import logging
67
78from web3 .contract import Contract
9+
810from eth_typing import BlockIdentifier
11+ from eth_typing import HexAddress
912
1013from eth_defi .erc_4626 .core import get_deployed_erc_4626_contract
14+ from eth_defi .erc_4626 .deposit_redeem import ERC4626DepositManager , ERC4626DepositRequest
15+ from eth_defi .erc_4626 .flow import deposit_4626
1116from eth_defi .erc_4626 .vault import ERC4626Vault
1217from eth_defi .vault .base import VaultTechnicalRisk
1318
@@ -21,6 +26,8 @@ class UmamiVault(ERC4626Vault):
2126
2227 Umami vaults do not have open source Github repository, developer documentation or easy developer access for integrations,
2328 making it not recommended to deal with them.
29+
30+ - Vault smart contract code: https://arbiscan.io/address/0x959f3807f0aa7921e18c78b00b2819ba91e52fef#code
2431 """
2532
2633 def get_risk (self ) -> VaultTechnicalRisk | None :
@@ -63,3 +70,143 @@ def get_performance_fee(self, block_identifier: BlockIdentifier) -> float | None
6370
6471 def get_estimated_lock_up (self ) -> datetime .timedelta :
6572 return datetime .timedelta (days = 3 )
73+
74+ def get_deposit_manager (self ) -> "eth_defi.umami.vault.UmamiDepositManager" :
75+ return UmamiDepositManager (self )
76+
77+
78+ class UmamiDepositManager (ERC4626DepositManager ):
79+ """Umami deposit manager with custom logic."""
80+
81+ def create_deposit_request (
82+ self ,
83+ owner : HexAddress ,
84+ to : HexAddress = None ,
85+ amount : Decimal = None ,
86+ raw_amount : int = None ,
87+ check_max_deposit = True ,
88+ check_enough_token = True ,
89+ max_slippage = 0.01 ,
90+ gas = 30_000_000 ,
91+ ) -> ERC4626DepositRequest :
92+ """Umami has a slippage tolerance on deposits.
93+
94+ - Umami has a 0.15% deposit fee taken from the shares minted.
95+ - Umami deposit is gas hungry
96+ - Umami deposit must have ETH attached to the transaction as something spends it there
97+
98+ .. code-block:: solidity
99+
100+ // DEPOSIT & WITHDRAW
101+ // ------------------------------------------------------------------------------------------
102+
103+ /**
104+ * @notice Deposit a specified amount of assets and mint corresponding shares to the receiver
105+ * @param assets The amount of assets to deposit
106+ * @param minOutAfterFees Minimum amount out after fees
107+ * @param receiver The address to receive the minted shares
108+ * @return shares The estimate amount of shares minted for the deposited assets
109+ */
110+ function deposit(uint256 assets, uint256 minOutAfterFees, address receiver)
111+ public
112+ payable
113+ override
114+ whenDepositNotPaused
115+ nonReentrant
116+ returns (uint256 shares)
117+ {
118+ // Check for rounding error since we round down in previewDeposit.
119+ require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");
120+ require(
121+ totalAssets() + assets <= previewVaultCap() + asset.balanceOf(address(this)), "AssetVault: over vault cap"
122+ );
123+ // Transfer assets to aggregate vault, transfer before minting or ERC777s could reenter.
124+ asset.safeTransferFrom(msg.sender, address(this), assets);
125+ aggregateVault.handleDeposit{ value: msg.value }(assets, minOutAfterFees, receiver, msg.sender, address(0));
126+
127+ emit Deposit(msg.sender, receiver, assets, shares);
128+ }
129+
130+ ETH spend:
131+
132+ .. code-block:: solidity
133+
134+ /**
135+ * @notice Handles a deposit of a specified amount of an ERC20 asset into the AggregateVault from an account, with a deposit fee deducted.
136+ * @param assets The amount of the asset to be deposited.
137+ * @param account The address of the account from which the deposit will be made.
138+ */
139+ function handleDeposit(uint256 assets, uint256 minOutAfterFees, address account, address sender, address callback)
140+ external
141+ payable
142+ onlyAssetVault
143+ {
144+ if (assets == 0) revert AmountEqualsZero();
145+ if (account == address(0)) revert ZeroAddress();
146+ AVStorage storage stg = _getStorage();
147+ uint256 gas = _gasRequirement(callback != address(0));
148+ if (msg.value < gas * tx.gasprice) revert MinGasRequirement();
149+
150+ // store request data
151+ uint256 key = _saveRequest(sender, account, msg.sender, callback, true, assets, minOutAfterFees);
152+
153+ // send execution gas cost
154+ TransferUtils.transferNativeAsset(stg.rebalanceKeeper, msg.value);
155+
156+ _executeHook(HookType.DEPOSIT_HOOK, msg.data[4:]);
157+
158+ // emit request event
159+ Emitter(stg.emitter).emitDepositRequest(key, account, msg.sender);
160+ }
161+ """
162+
163+ if not raw_amount :
164+ raw_amount = self .vault .denomination_token .convert_to_raw (amount )
165+
166+ vault = self .vault
167+ from_ = owner
168+ receiver = owner
169+
170+ logger .info (
171+ "Depositing to vault %s, amount %s, raw amount %s, from %s" ,
172+ vault .address ,
173+ amount ,
174+ raw_amount ,
175+ from_ ,
176+ )
177+
178+ preview_amount = vault .vault_contract .functions .previewDeposit (raw_amount ).call ()
179+ estimated_shares = vault .share_token .convert_to_decimals (preview_amount )
180+
181+ min_shares = int (preview_amount * (1 - max_slippage ))
182+
183+ logger .info ("Estimated %s shares before slippage: %s, slippage set to %s, min amount out %s" , vault .share_token .symbol , estimated_shares , max_slippage , min_shares )
184+
185+ contract = vault .vault_contract
186+
187+ if not raw_amount :
188+ assert isinstance (amount , Decimal )
189+ assert amount > 0
190+ raw_amount = vault .denomination_token .convert_to_raw (amount )
191+
192+ if check_enough_token :
193+ actual_balance_raw = vault .denomination_token .fetch_raw_balance_of (from_ )
194+ assert actual_balance_raw >= raw_amount , f"Not enough token in { from_ } to deposit { amount } to { vault .address } , has { actual_balance_raw } , tries to deposit { raw_amount } "
195+
196+ if check_max_deposit :
197+ max_deposit = contract .functions .maxDeposit (receiver ).call ()
198+ if max_deposit != 0 :
199+ assert raw_amount <= max_deposit , f"Max deposit { max_deposit } is less than { raw_amount } "
200+
201+ call = contract .functions .deposit (raw_amount , min_shares , receiver )
202+
203+ return ERC4626DepositRequest (
204+ vault = self .vault ,
205+ owner = owner ,
206+ to = owner ,
207+ funcs = [call ],
208+ amount = amount ,
209+ raw_amount = raw_amount ,
210+ gas = gas ,
211+ value = Decimal (0.1 ),
212+ )
0 commit comments