Conversation
6de117f to
c6a0634
Compare
e55de25 to
48ef07b
Compare
| /// Tracks whether the underlying FlowALP position has been closed. Once true, | ||
| /// availableBalance() returns 0.0 to avoid panicking when the pool no longer | ||
| /// holds the position (e.g. during YieldVault burnCallback after close). | ||
| access(self) var positionClosed: Bool |
There was a problem hiding this comment.
this is removed to keep the contract updatable
|
Claude finished @nialexsan's task in 2m 38s —— View job Code Review
SummaryThis PR extracts the FUSDEV strategy from #183 for focused review. The changes are well-motivated and the code is generally clean. Below are my findings, ordered by severity.
|
this change fixes an issue with exceeding compute limit while reading through the dict and retrieving a big object (Swapper) from it. Also this approach makes the implementation more flexible, in case the swap paths are updated after a strategy is created. |
| access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} { | ||
| if maxAmount == 0.0 { | ||
| return <- DeFiActionsUtils.getEmptyVault(self.getSourceType()) | ||
| } | ||
| let availableIn = self.source.minimumAvailable() | ||
| if availableIn == 0.0 { | ||
| return <- DeFiActionsUtils.getEmptyVault(self.getSourceType()) | ||
| } | ||
| // Pull ALL available yield tokens (not quoteIn-limited) | ||
| let sourceLiquidity <- self.source.withdrawAvailable(maxAmount: availableIn) | ||
| if sourceLiquidity.balance == 0.0 { | ||
| Burner.burn(<-sourceLiquidity) | ||
| return <- DeFiActionsUtils.getEmptyVault(self.getSourceType()) | ||
| } | ||
| let swapped <- self.swapper.swap(quote: nil, inVault: <-sourceLiquidity) | ||
| assert(swapped.balance > 0.0, message: "BufferedSwapSource: swap returned zero despite available input") | ||
| return <- swapped | ||
| } |
There was a problem hiding this comment.
This is the same behaviour as a SwapSource, but ignores the maxAmount provided by the DeFiActions.Source interface.
Are we sure that this is necessary, and not masking an underlying bug/design flaw? I thought that DeFiActions used UFix64.max as a senteniel value for cases like this, or am I misunderstanding here?
| } else { | ||
| // @TODO implement swapping moet to collateral | ||
| destroy dustVault | ||
| Burner.burn(<-v) |
There was a problem hiding this comment.
Should we give us some visibility of the reason we burn the tokens? Because the decision depends on a 3rd party quote.
We have 2 (or more?) code paths that lead to token burning event: 1) vault has 0 balance 2) the token worth nothing.
Maybe it's useful to add a TokenBurned event with the amount and the quote as evidence of why we decided to burn it, in case we need to debug, since the swapper price might be either outdated or misbehaving?
| ): UniswapV3SwapConnectors.Swapper { | ||
| let yieldToCollPath = collateralConfig.yieldToCollateralUniV3AddressPath | ||
| let yieldToCollFees = collateralConfig.yieldToCollateralUniV3FeePath | ||
| assert(yieldToCollPath.length >= 2, message: "yieldToCollateral path must have at least 2 elements") |
There was a problem hiding this comment.
| assert(yieldToCollPath.length >= 2, message: "yieldToCollateral path must have at least 2 elements") | |
| assert(yieldToCollPath.length >= 2, message: "yieldToCollateral path requires at least yield and collateral tokens, got \(yieldToCollPath.length)") |
| uniqueID: DeFiActions.UniqueIdentifier | ||
| ): UniswapV3SwapConnectors.Swapper { | ||
| let yieldToCollPath = collateralConfig.yieldToCollateralUniV3AddressPath | ||
| let yieldToCollFees = collateralConfig.yieldToCollateralUniV3FeePath |
There was a problem hiding this comment.
Better validate this as well
assert(
yieldToCollFees.length == yieldToCollPath.length - 1,
message: "Fee array length (\(yieldToCollFees.length)) must equal path hops (\(yieldToCollPath.length - 1))"
)
| } | ||
| } else { | ||
| destroy dustVault | ||
| Burner.burn(<-v) |
There was a problem hiding this comment.
could we mention the burning token behavior (the cleanup) in the comments of closePosition as well?
| return self.swapper.quoteOut(forProvided: avail, reverse: false).outAmount | ||
| } | ||
| /// Pulls ALL available yield tokens from the source and swaps them to the debt token. | ||
| /// Ignores quoteIn — avoids ERC4626 rounding underestimates that would leave us short. |
There was a problem hiding this comment.
What is the rounding underestimate caused by? Is it bounded? If the source contains much more funds than is needed, we'll swap much more than we need to.
| collateralType: collateralType, | ||
| uniqueID: self.uniqueID! | ||
| ) | ||
| let shortfall = totalDebtAmount - expectedMOET |
There was a problem hiding this comment.
It seems like both these changes are related to the Swapper quotes being unreliable:
BufferedSwapSourceswaps all funds available in the source, ignoring the quote, because it might contain rounding errors- This "pre-supplement extra moet" step adds 1% to account for slippage/rounding unaccounted for in the quote from
collateralToMoetSwapper
But when we calculate the shortfall, we are using expectedMOET, which is the result of a swapper quote. Why do we trust this quote and not the others?
Closes: #???
Description
this PR contains bulk changes related only to FUSDEV strategy from #183
so it's easier to review just the strategy itself withoutt the second strategy
For contributor use:
masterbranchFiles changedin the Github PR explorer