-
Notifications
You must be signed in to change notification settings - Fork 5
ARM integration to Lending Markets #79
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
base: main
Are you sure you want to change the base?
Conversation
Added OriginARM deploy script for Sonic
Updated the Sonic deploy script
* Sonic ARM contract dependencies * update Code settings * Silo market contract * fix: handle zero shares case before redeeming in AbstractARM contract * Fet liquidity from the active lending market for claimRedeem if not enough liquidity in the ARM * Added transfer from ARM to SiloMarket contract for deposit
* feat: setup unit test for Origin ARM * fix: update requestWithdrawal function to return additional value and ensure MockERC20 burn call * feat: add unit tests for OriginARM deposit functionality and update DEFAULT_FEE constant * feat: add setArmBuffer function to manage liquidity asset buffer and emit event on update * feat: add setDefaultStrategy and deposit modifiers for unit tests in OriginARM * feat: replace MockStrategy with MockMarket and update related modifiers in tests * feat: add unit tests for OriginARM market management and enhance modifier functionality * feat: introduce MockERC4626Market and replace MockMarket in shared tests * feat: enhance market management by adding support for multiple markets and updating modifiers * feat: add new modifiers for withdrawal and token swap functionality in Modifiers contract * feat: add unit tests for total assets functionality in OriginARM contract * feat: add swapAllOETHForWETH modifier to facilitate token swapping in Modifiers contract * feat: add unit tests for fee collector and fee management in OriginARM contract * fix: prevent redeeming zero shares in AbstractARM contract * feat: add additional tests for fee management and address validation in OriginARM contract * fix: update maxWithdraw call to use contract address in AbstractARM * feat: enhance simulateMarketLoss modifier to include maxRedeem calculations * feat: add unit tests for allocate functionality in OriginARM contract * feat: add marketLoss modifier to simulate market loss in tests * feat: add unit tests for claim and deposit functionalities in OriginARM contract * fix: rename contract to reflect correct functionality for claim and redeem tests * feat: add unit tests for request redeem functionality in OriginARM contract * feat: add Sonic configuration and update AddressResolver for new market references * feat: add shared test setup and modifiers for OriginARM contract * feat: add Allocated event to track asset allocation in AbstractARM contract * fix: prevent redeem call with zero shares in AbstractARM contract * fix: handle zero shares case before redeeming in AbstractARM contract * feat: enhance testing framework with new modifiers and helper functions for OriginARM contract * fix: prevent redeem call with insufficient shares in AbstractARM contract * feat: add MIN_BALANCE constant and update allocation tests for edge cases in Fork_Concrete_OriginARM_Allocate_Test_ * feat: add new test cases for liquidity delta scenarios and update assertions in Allocate.sol * fix: update SiloMarket to use market address for deposit, withdraw, and redeem functions * refactor: simplify maxWithdraw and maxRedeem functions to return 0 for non-ARM owners * feat: add tests for allocation scenarios with SiloMarket integration and update shared setup * feat: add unit tests for SiloMarket with revert scenarios and setup adjustments * fmt * refactor: remove unused imports from unit test files * refactor: simplify maxWithdraw and maxRedeem functions for clarity
* Fix maxRedeem * FIx import breaking build * Renamed collectRewardTokens to collectRewards * First cut off the Harvester * Cleaned up Harvester * Added Harvester to deploy script * Updated Sonic contract diagram * Added magpieQuote Hardhat task * Added magpieTx Hardhat task * Remove function sig from Magpie swap data * only console log the swap data * wip: add first API test interaction * rename test function to reflect Magpie swap functionality * Fix parsing of Magpie data * Enable ffi * Got Magpie swap working * Clean up assembly validation * Update assembly comments --------- Co-authored-by: Clément <[email protected]>
* fix: assign maxShares in maxRedeem function in SiloMarket contract * fix: update label for siloMarket to Silo Market Adapter in Base_Test_ contract * test: add VaultInteractions tests for OriginARM contract * fix: update clean-all target to remove yarn.lock and lcov.info files * feat: migrate invariant for LidoARM in a separated folder * feat: add invariant tests and setup for OriginARM with helper contracts * feat: add ClaimRedeem fork tests for Fork_Concrete_OriginARM contract * feat: add requestRedeem and timejump modifiers to Modifiers contract * feat: implement handler_claimRedeem and update request management in TargetFunction and Helpers contracts * feat: add handler_setARMBuffer function and update ClaimRedeem and Allocate status in TargetFunction * feat: update SetARMBuffer status to implemented and adjust pct calculation in handler_setARMBuffer function * feat: update Allocate status to not implemented in TargetFunction contract * feat: add handler_setActiveMarket function and update market management in TargetFunction and Helpers contracts * feat: update Allocate status to implemented and add handler_allocate function in TargetFunction contract * feat: add handler_setPrices function to manage buy and sell prices in TargetFunction contract * feat: update price handling in TargetFunction contract to improve precision and mimic market behavior * feat: update price handling and add setCrossPrice function in TargetFunction contract * feat: add handler_swapExactTokensForTokens function and update liquidity calculations in TargetFunction contract * feat: add handler for `swapTokensForExactTokens()`. * feat: add handlers for `collectFees` and `setFee`. * feat: add logic and handler for `requestOriginWithdrawal`. * feat: add claimOriginWithdrawals handler and update related logic in TargetFunction and Helpers contracts * feat: add handler for `donateToARM` and update related logic in TargetFunction and FuzzerFoundry contracts * WIP * feat: enhance claimRedeem logic and implement fee collection in TargetFunction contract * feat: add funding logic for markets in FuzzerFoundry contract * feat: add console logging toggle in Setup and TargetFunction contracts * feat: update MockVault and related tests to support multiple tokens * feat: enhance FuzzerFoundry and Properties contracts with new swap properties and invariants * feat: add new LP invariants and update deposit/redeem logic in TargetFunction contract * feat: update funding logic and add assertions for LP balances in FuzzerFoundry and TargetFunction contracts * feat: update assertLpsAreUpOnly function to include tolerance parameter for balance checks * feat: enable fail_on_revert in fuzz configuration for improved error handling * feat: refactor getRandomMarket and getRandomLPsWithRequest functions for improved clarity and efficiency * feat: refactor random selection functions to use Fisher-Yates shuffle for improved randomness and efficiency
* feat: enhance Harvester tests with additional swap scenarios and helper functions * fix: correct variable name in allowed slippage validation * test: add comprehensive tests for Harvester setters and swaps * feat: enhance collectRewards function to return claimed reward tokens and amounts
* feat: enhance Harvester tests with additional swap scenarios and helper functions * fix: correct variable name in allowed slippage validation * test: add comprehensive tests for Harvester setters and swaps * feat: enhance collectRewards function to return claimed reward tokens and amounts * feat: integrate distribution manager and incentives controller in SiloMarket * fix: correct import statement for Math utility in Harvester contract * feat: add Fork_Concrete_Harvester_Collect_Test contract and enhance shared test setup
* Changed setActiveMarket to use balanceOf instead of maxRedeem * Change _availableAssets to use previewRedeem instead of maxWithdraw
* Add WOS token and update related tests for market utilization * fix: update assertions in allocation tests to use approximate equality * fix: update minimum shares to redeem in AbstractARM contract * fix: update minimum shares to redeem in AbstractARM contract * feat: add balanceOf and previewRedeem functions to SiloMarket contract * fix: update afterInvariant function to use dynamic minimum shares and improve property_swap_A logic * fix: update minimum shares to redeem in AbstractARM contract
RequirementsSee the PR description or Notion https://www.notion.so/originprotocol/ARM-Lending-Markets-Design-1d684d46f53c805984b3f9b4c1cbadc8 Easy ChecksAuthentication
Ethereum
Cryptographic code
Gas problems
Black magic
Overflow
License
Proxy
Events
Medium ChecksRounding and casts
Dependencies
External calls
Tests
Deploy
Downstream
ThinkingLogic
Deployment Considerations
Internal State
Does this code do that? AttackFor the initial deployment, the FlavorThe lending market integration was made a lot simpler by only having one active lending market. If there is a large amount of idle liquidity deposited into that active market, then that can diminish the returns as the utilization of the market will drop. It could be more profitable to split the idle liquidity across multiple lending markets but that adds a lot of complexity that isn't needed for now. |
* fix: adjust invariant test parameters and refactor setup logic in FuzzerFoundry and Setup contracts * fix: increase invariant test runs and depth for improved coverage * fix: update SiloMarket constructor to accept gauge address directly * fix: update minimum shares to redeem from 10,000 to 10,000,000 * feat: deploy new SiloMarket for Varlamore with gauge integration * fix: correct balance assertion in harvester test and update MIN_BALANCE constant in AllocateWithAdapter test * fix: update deposit and swap handler functions to use uint88 for amounts (#82) * Fix: Change MIN_SHARES_TO_REDEEM from constant to immutable. (#83) * fix: refactor minSharesToRedeem to be an immutable variable in AbstractARM contract * fix: add minSharesToRedeem parameter to OriginARM constructor and update related deployments * fix: update test target to use Fuzzer for improved testing coverage * fix: format * feat: add MathComparisons library and integrate it into Properties and TargetFunction contracts
… and update initialization logic
… nicka/liquidity-strategies
Added zero address check to _setMagpieRouter
…st related logic in Swap tests
* Refactor test workflow to include non-invariant and invariant tests for LidoARM * Refactor Makefile to use $(MAKE) for consistency in test commands * Comment out requirement for SONIC_URL in _createAndSelectFork function * Add SONIC_URL to environment variables in foundry-tests job * Uncomment requirement for SONIC_URL in _createAndSelectFork function * Increase tolerance in totalAssets assertions for allocation tests * Update totalAssets assertion to use assertApproxEqAbs for accuracy in LidoARM deposit test * Refactor totalAssets assertions to use assertApproxEqAbs for improved accuracy in deposit tests * Add debugging output to non-invariant tests step in CI workflow * Enhance non-invariant tests step with environment checks for better debugging * Fix installation command in CI workflow to use 'make install' instead of 'forge soldeer install' * Fix installation command in CI workflow to use 'make install' instead of 'forge soldeer install' * Simplify non-invariant tests step by removing unnecessary commands before 'make test' * Fix formatting in CI workflow for Install Dependencies step * Fix job name for foundry tests in CI workflow
* Deploy Origin ARM on Sonic * Changed depositLido to depositARM so it works with OriginARM on Sonic * Made requestRedeem and claimRedeem work with the origin ARM on Sonic * swap HH task now works with Origin ARM on Sonic * hh tasks to request and claim Vault withdrawals now work with Origin ARM on Sonic * HH snap task now works with Sonic ARM * Removed unused interfaces in SiloMarket * Allocate excess liquidity to the lending market after claiming OS withdrawal
src/contracts/AbstractARM.sol
Outdated
@@ -550,6 +574,14 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { | |||
// Store the updated claimed amount | |||
withdrawsClaimed += SafeCast.toUint128(assets); | |||
|
|||
// If there is not enough liquidity assets in the ARM, get from the active market |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block can be skipped if activeMarket is not set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in cc88ff5
… previous market (#94)
* Natspec updates * Update Natspec
(, amountClaimed) = IOriginVault(vault).claimWithdrawals(requestIds); | ||
|
||
// Store the reduced amount outstanding withdrawals from the Origin Vault | ||
vaultWithdrawalAmount -= amountClaimed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case a withdrawal request is transferred to the OriginARM contract, you won't be able to claim it because this line will underflow.
This issue also exists in the lido contract.
arm-oeth/src/contracts/LidoARM.sol
Line 155 in 2e4cffa
lidoWithdrawalQueueAmount -= totalAmountRequested; |
A simple fix could be
if (vaultWithdrawalAmount < amountClaimed) {
vaultWithdrawalAmount = 0;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot for claiming the Lido withdrawals. I've fixed the LidoARM with bb0b2cd
It won't be an issue for the Origin ARM as withdrawals from the Origin Vault are not transferrable. I'll put in a comment.
/// less the liquidity assets reserved for the ARM's withdrawal queue and accrued fees. | ||
/// @return The total amount of assets in the ARM | ||
function totalAssets() public view virtual returns (uint256) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟠 The intention of the conditional in this function is that totalAssets()
never returns a number lower than MIN_TOTAL_SUPPLY.
However, this function can return a number lower than MIN_TOTAL_SUPPLY, including returning a zero. The current code results in a discontinuity as the fee approaches and then passes newAvailableAssets. The returned assets goes down to zero and then jumps back up to the minimum total supply.
What we actually want here is a minimum function, taking the larger of two values, while also avoiding any subtraction before we know that the subtraction is safe. I think the correct if statement here might be this:
if (fees + MIN_TOTAL_SUPPLY >= newAvailableAssets) return MIN_TOTAL_SUPPLY;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent spot. Fixed in 5b1a9cf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OETH ARM
Requirements
This PR is adding the ability to auto-allocate liquidity to a 4626 lending market to earn extra yield on idle capital.
It does this via an intermediate wrapper 4626 contract that provides the ability to harvest rewards.
One open question is if anyone should be able to move liquidity back into the contract, or if it needs to be operator only so that the prices can be set before liquidity is re-added.
A portion of our volume is very large transactions. It would be helpful to know how many of these transactions are actually users wanting to transaction large amounts which is good or are bots looking the transaction large amounts which we don't want we would prefer to change our prices and raise them. Knowing this can help us determine how big of an allocation buffer we should set.
Having this lending platform option puts a threshold on how far we should change the prices. If we're going to be making 2% on a lending platform then we shouldn't set our sell price margins thin enough to the point where we will make less than the 2% going through a redeem cycle.
Authentication
- Never use tx.origin
- Every external/public function is supposed to be externally accessible
- Every external/public function has the correct authentication
Ethereum
🟣 Ethereum is only accepted for payable receives to receive WETH and for Zap contracts.
Cryptographic code
_ No crypto._
Gas problems
- Contracts with for loops must have either:
- A way to remove items
- Can be upgraded to get unstuck
- Size can only controlled by admins
- Contracts with for loops must not allow end users to add unlimited items to a loop that is used by others or admins.
Black magic
- Does not contain
selfdestruct
- Does not use
delegatecall
outside of proxying. If an implementation contract were to call delegatecall under attacker control, it could call selfdestruct the implementation contract, leading to calls through the proxy silently succeeding, even though they were failing. - Address.isContract should be treated as if could return anything at any time, because that's reality.
Overflow
- Code is solidity version >= 0.8.0
- All for loops use uint256
License
- The contract uses the appropriate limited BUSL-1.1 (Business) or the open MIT license
- If the contract license changes from MIT to BUSL-1.1 any contracts importing it need to also have their license set to BUSL-1.1
Proxy
- No storage variable initialized at definition when contract used as a proxy implementation.
Events
- All state changing functions emit events
- AMM swaps do not, and we are okay with that.
Rounding and casts
- Contract rounds in the protocols favor
- Contract does not have bugs from loosing rounding precision
- Code correctly multiplies before division
- Contract does not have bugs from zero or near zero amounts
- Safecast is aways used when casting
Dependencies
No dependency changes
External calls
- Contract addresses passed in are validated
- No unsafe external calls
- 🟠✅ No slippage attacks (we need to validate expected tokens received) Fixed
🟣 Harvesting requires correct minimum amounts to be passed in in order to avoid sandwich attacks. This is okay since this is not user funds.
Deploy
- Deployer permissions are removed after deploy
Strategy Specific
Strategy checks
- Check balance cannot be manipulated up AND down by an attacker
- As long as the 4626 lending market is well behaved.
- The operator could reduce the active assets by the size of the minimum redeem amount. See discussion under attacks
- No read only reentrancy on downstream protocols during checkBalance
- A read-only reentrancy in a downstream 4626 could result in a catastrophic loss of user funds. We must ensure that 4626 contracts used as markets are not subject to read-only reentry.
- All reward tokens are collected
- The harvester can sell all reward tokens
- No funds are left in the contract that should not be as a result of depositing or withdrawing
- All funds can be recovered from the strategy by some combination of depositAll, withdraw, or withdrawAll()
- WithdrawAll() cannot be MEV'd
Logic
Appears correct, outside of panda’s finding on extra withdraws
Attack
This new code holds our user’s funds. It also tracks how much funds we think we have. Bugs in this new could result in a total loss of user funds.
The harvester code could only result in a loss of some of our yield. Because these these stakes are much, much, much smaller. I'm going to spend less time examining them.
We want to ensure that our balances stay within a rounding error when moving funds into or out of the lending markets.
We need to make sure that any funds that go into a lending market can only come back to us.
The asset balance accounting should be correct and not subject to manipulation.
The intersection between withdrawals and balance checking should be correct.
What could the impacts of code failure in this code be.
What conditions could cause this code to fail if they were not true.
Does this code successfully block all attacks.
🟠✅ An operator can artificially reduce the supply of the token by making a small small amount of tokens fall under the minimum mint required when switching active strategies.
This minimum value is currently set to 1e7. Assuming a total supply of 1e22, this would give an attack a 1/1e15 privileged mint. Assuming an attacker was using a 100 million dollars, this would give the attacker a free $0.0000001 per loop. Current sonic gas prices are more than $0.01 for something basic. This is currently attack economically unfeasible.
This has been fixed.
Flavor
See comment on read only reentrancy check on silo.
* Added allocateThreshold to AbstractARM * Fixed allocate unit tests * Prettier * Fixed allocate fork tests * Fixed allocate fork tests --------- Co-authored-by: Daniel Von Fange <[email protected]>
* SonicHarvester changed so the Magpie swap is sent to the harvester before the recipient * Fix setActiveMarket unit test * Script to upgrade the SonicHarvester
* Renamed the upgrade script * Changed claimRedeem to check if the activeMarket has been configured * Added view functions convertToShares and convertToAssets to SiloMarket * Added upgrade of SiloMarket to deploy script * Fix handling of claiming Lido withdrawals if a withdrawal request is transferred to the LidoARM * Ensure totalAssets returns at least MIN_TOTAL_SUPPLY
@DanielVF looking at example |
Design Specification
The following spec is copied from https://www.notion.so/originprotocol/ARM-Lending-Markets-Design-1d684d46f53c805984b3f9b4c1cbadc8
The design decisions can be exported on request https://www.notion.so/originprotocol/ARM-Lending-Markets-Design-1d684d46f53c805984b3f9b4c1cbadc8?p=1cf84d46f53c80b8ad3ef07f6a45dd12&pm=s
Features
ARM Lending Market Use Cases
Harvester Use Cases
The Harvesters on Sonic will use the Magpie aggregator to get best execution of swapping Silo for wS.
Some key design decisions
collect
token rewards from the supported lending market strategies. For the Silo lending markets, that will be the Silo token. Rewards from multiple lending market strategies can be collected before being swapped to the ARM’s associated liquidity asset.swap
token rewards for the associated ARM’s liquidity asset. For the Silo lending markets, that’s swapping Silo for wrapped S (wS). The initial implementation will only support the Magpie DEX aggregator. The Owner or Operator is responsible for calling the Magpie APIs and passing the the Magpie specific swap data.Contracts
AbstractARM
to park idle liquidity in a lending marketOriginARM
that implementsAbstractARM
by integrating to an Origin VaultSiloMarket
contract that can harvest Silo rewards from a Silo lending marketHarvester
that can collect and swap Silo rewards to wS using the Magpie DEX aggregatorZapperARM
that integrates to multiple ARMs by swapping S to wS.Testing
To run all the tests
make test
To run the OriginARM fork tests
Deployment
The deployment scripts are
*
script/deploy/sonic/001_DeployOriginARMProxy.sol
*
script/deploy/sonic/002_DeployOriginARM.sol
In the
.env
file, set theDEPLOYER_PRIVATE_KEY
andSONIC_URL
variables