diff --git a/script/deploy/DeployManager.sol b/script/deploy/DeployManager.sol index 2a16521..afe68ea 100644 --- a/script/deploy/DeployManager.sol +++ b/script/deploy/DeployManager.sol @@ -15,7 +15,7 @@ import {DeployCoreHoleskyScript} from "./holesky/001_DeployCoreScript.sol"; import {UpgradeHoleskyScript} from "./holesky/002_UpgradeScript.sol"; import {DeployOriginARMProxyScript} from "./sonic/001_DeployOriginARMProxy.sol"; import {DeployOriginARMScript} from "./sonic/002_DeployOriginARM.sol"; -import {UpgradeSonicHarvesterScript} from "./sonic/003_UpgradeSonicHarvester.sol"; +import {UpgradeOriginARMScript} from "./sonic/003_UpgradeOriginARM.sol"; contract DeployManager is Script { using stdJson for string; @@ -79,7 +79,13 @@ contract DeployManager is Script { console.log("Deploying Origin ARM"); _runDeployFile(new DeployOriginARMProxyScript()); _runDeployFile(new DeployOriginARMScript(getDeployedAddressInBuild("ORIGIN_ARM"))); - _runDeployFile(new UpgradeSonicHarvesterScript(getDeployedAddressInBuild("HARVESTER"))); + _runDeployFile( + new UpgradeOriginARMScript( + getDeployedAddressInBuild("HARVESTER"), + getDeployedAddressInBuild("ORIGIN_ARM"), + getDeployedAddressInBuild("SILO_VARLAMORE_S_MARKET") + ) + ); } else { console.log("Skipping deployment (not mainnet)"); } diff --git a/script/deploy/sonic/003_UpgradeOriginARM.sol b/script/deploy/sonic/003_UpgradeOriginARM.sol new file mode 100644 index 0000000..e92ff7c --- /dev/null +++ b/script/deploy/sonic/003_UpgradeOriginARM.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.23; + +import "forge-std/console.sol"; + +import {SonicHarvester} from "contracts/SonicHarvester.sol"; +import {OriginARM} from "contracts/OriginARM.sol"; +import {SiloMarket} from "contracts/markets/SiloMarket.sol"; +import {Proxy} from "contracts/Proxy.sol"; +import {Sonic} from "contracts/utils/Addresses.sol"; +import {AbstractDeployScript} from "../AbstractDeployScript.sol"; + +contract UpgradeOriginARMScript is AbstractDeployScript { + string public constant override DEPLOY_NAME = "003_UpgradeOriginARMScriptScript"; + bool public constant override proposalExecuted = false; + + Proxy public harvesterProxy; + SonicHarvester public harvesterImpl; + Proxy public originARMProxy; + OriginARM public originARMImpl; + Proxy public silo_Varlamore_S_MarketProxy; + SiloMarket public silo_Varlamore_S_MarketImpl; + + constructor(address _harvesterProxy, address _originARMProxy, address _silo_Varlamore_S_MarketProxy) { + require(_harvesterProxy != address(0), "Invalid proxy address"); + harvesterProxy = Proxy(payable(_harvesterProxy)); + + require(_originARMProxy != address(0), "Invalid OriginARM proxy address"); + originARMProxy = Proxy(payable(_originARMProxy)); + + require(_silo_Varlamore_S_MarketProxy != address(0), "Invalid Silo Varlamore S proxy address"); + silo_Varlamore_S_MarketProxy = Proxy(payable(_silo_Varlamore_S_MarketProxy)); + } + + function _execute() internal override { + console.log("Deploy:", DEPLOY_NAME); + console.log("------------"); + + // 1. Deploy the SonicHarvester implementation + harvesterImpl = new SonicHarvester(Sonic.WS); + _recordDeploy("HARVESTER_IMPL", address(harvesterImpl)); + + // 2. Deploy new Origin ARM implementation + uint256 claimDelay = tenderlyTestnet ? 1 minutes : 10 minutes; + uint256 minSharesToRedeem = 1e7; + int256 allocateThreshold = 1e18; + originARMImpl = + new OriginARM(Sonic.OS, Sonic.WS, Sonic.OS_VAULT, claimDelay, minSharesToRedeem, allocateThreshold); + _recordDeploy("ORIGIN_ARM_IMPL", address(originARMImpl)); + + // 3. Deploy the Silo market implementation for the Varlamore S Vault + silo_Varlamore_S_MarketImpl = + new SiloMarket(address(originARMProxy), Sonic.SILO_VARLAMORE_S_VAULT, Sonic.SILO_VARLAMORE_S_GAUGE); + _recordDeploy("SILO_VARLAMORE_S_MARKET_IMPL", address(silo_Varlamore_S_MarketImpl)); + + console.log("Finished deploying", DEPLOY_NAME); + } + + function _buildGovernanceProposal() internal override {} + + function _fork() internal override { + if (this.isForked()) { + vm.startPrank(Sonic.ADMIN); + + // 1. Upgrade SonicHarvester Proxy to the new implementation + harvesterProxy.upgradeTo(address(harvesterImpl)); + + // 2. Upgrade OriginARM Proxy to the new implementation + originARMProxy.upgradeTo(address(originARMImpl)); + + // 3. Upgrade SiloMarket Proxy to the new implementation + silo_Varlamore_S_MarketProxy.upgradeTo(address(silo_Varlamore_S_MarketImpl)); + + vm.stopPrank(); + } + } +} diff --git a/script/deploy/sonic/003_UpgradeSonicHarvester.sol b/script/deploy/sonic/003_UpgradeSonicHarvester.sol deleted file mode 100644 index 5d7aa1b..0000000 --- a/script/deploy/sonic/003_UpgradeSonicHarvester.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.23; - -import "forge-std/console.sol"; - -import {CapManager} from "contracts/CapManager.sol"; -import {SonicHarvester} from "contracts/SonicHarvester.sol"; -import {OriginARM} from "contracts/OriginARM.sol"; -import {Proxy} from "contracts/Proxy.sol"; -import {ZapperARM} from "contracts/ZapperARM.sol"; -import {SiloMarket} from "contracts/markets/SiloMarket.sol"; -import {Sonic} from "contracts/utils/Addresses.sol"; -import {IERC20} from "contracts/Interfaces.sol"; -import {AbstractDeployScript} from "../AbstractDeployScript.sol"; - -contract UpgradeSonicHarvesterScript is AbstractDeployScript { - string public constant override DEPLOY_NAME = "003_UpgradeSonicHarvesterScript"; - bool public constant override proposalExecuted = false; - - Proxy public harvesterProxy; - - constructor(address _harvesterProxy) { - require(_harvesterProxy != address(0), "Invalid proxy address"); - harvesterProxy = Proxy(payable(_harvesterProxy)); - } - - function _execute() internal override { - console.log("Deploy:", DEPLOY_NAME); - console.log("------------"); - - // 1. Deploy the SonicHarvester implementation - SonicHarvester harvesterImpl = new SonicHarvester(Sonic.WS); - _recordDeploy("HARVESTER_IMPL", address(harvesterImpl)); - - // 17. Upgrade Proxy to the new SonicHarvester implementation - harvesterProxy.upgradeTo(address(harvesterImpl)); - - console.log("Finished deploying", DEPLOY_NAME); - } - - function _buildGovernanceProposal() internal override {} - - function _fork() internal view override { - if (this.isForked()) {} - } -} diff --git a/src/contracts/AbstractARM.sol b/src/contracts/AbstractARM.sol index ec6a996..c31c46d 100644 --- a/src/contracts/AbstractARM.sol +++ b/src/contracts/AbstractARM.sol @@ -583,12 +583,17 @@ 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 - uint256 liquidityInARM = IERC20(liquidityAsset).balanceOf(address(this)); - if (assets > liquidityInARM) { - uint256 liquidityFromMarket = assets - liquidityInARM; - // This should work as we have checked earlier the claimable() amount which includes the active market - IERC4626(activeMarket).withdraw(liquidityFromMarket, address(this), address(this)); + // If there is not enough liquidity assets in the ARM, get from the active market if one is configured. + // Read the active market address from storage once to save gas. + address activeMarketMem = activeMarket; + if (activeMarketMem != address(0)) { + uint256 liquidityInARM = IERC20(liquidityAsset).balanceOf(address(this)); + + if (assets > liquidityInARM) { + uint256 liquidityFromMarket = assets - liquidityInARM; + // This should work as we have checked earlier the claimable() amount which includes the active market + IERC4626(activeMarketMem).withdraw(liquidityFromMarket, address(this), address(this)); + } } // transfer the liquidity asset to the withdrawer @@ -644,9 +649,8 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { (uint256 fees, uint256 newAvailableAssets) = _feesAccrued(); // total assets should only go up from the initial deposit amount that is burnt - // but in case of something unforeseen, return MIN_TOTAL_SUPPLY if fees is - // greater than or equal the available assets - if (fees >= newAvailableAssets) return MIN_TOTAL_SUPPLY; + // but in case of something unforeseen, return at least MIN_TOTAL_SUPPLY. + if (fees + MIN_TOTAL_SUPPLY >= newAvailableAssets) return MIN_TOTAL_SUPPLY; // Remove the performance fee from the available assets return newAvailableAssets - fees; diff --git a/src/contracts/LidoARM.sol b/src/contracts/LidoARM.sol index 73c12c2..62b0a48 100644 --- a/src/contracts/LidoARM.sol +++ b/src/contracts/LidoARM.sol @@ -151,8 +151,13 @@ contract LidoARM is Initializable, AbstractARM { totalAmountRequested += requestAmount; } - // Store the reduced amount outstanding from the Lido Withdrawal Queue - lidoWithdrawalQueueAmount -= totalAmountRequested; + // Store the reduced outstanding withdrawals from the Lido Withdrawal Queue + if (lidoWithdrawalQueueAmount < totalAmountRequested) { + // This can happen if a Lido withdrawal request was transferred to the ARM contract + lidoWithdrawalQueueAmount = 0; + } else { + lidoWithdrawalQueueAmount -= totalAmountRequested; + } // Wrap all the received ETH to WETH. weth.deposit{value: address(this).balance}(); diff --git a/src/contracts/OriginARM.sol b/src/contracts/OriginARM.sol index a3219e3..46c5963 100644 --- a/src/contracts/OriginARM.sol +++ b/src/contracts/OriginARM.sol @@ -91,7 +91,8 @@ contract OriginARM is Initializable, AbstractARM { // Claim the previously requested withdrawals from the Origin Vault. (, amountClaimed) = IOriginVault(vault).claimWithdrawals(requestIds); - // Store the reduced amount outstanding withdrawals from the Origin Vault + // Store the reduced outstanding withdrawals from the Origin Vault. + // Origin Vault withdrawals are not transferrable so its safe to reduce the amount. vaultWithdrawalAmount -= amountClaimed; emit ClaimOriginWithdrawals(requestIds, amountClaimed); diff --git a/src/contracts/markets/SiloMarket.sol b/src/contracts/markets/SiloMarket.sol index 6db9e38..fcbbbfb 100644 --- a/src/contracts/markets/SiloMarket.sol +++ b/src/contracts/markets/SiloMarket.sol @@ -52,16 +52,6 @@ contract SiloMarket is Initializable, Ownable { _setHarvester(_harvester); } - /// @notice Get the amount of Silo Market shares owned by this contract. - /// @param owner The owner has to be the address of the ARM contract. - /// @return shares The amount of Silo lending market shares owned by this contract. - function balanceOf(address owner) external view returns (uint256) { - if (owner != arm) return 0; - - // Get the balance of shares in the lending market - return IERC4626(market).balanceOf(address(this)); - } - /// @notice Deposit an exact amount of asset tokens to the Silo lending market /// and mint a variable amount of Silo lending market shares to this contract. /// @param assets The exact amount of asset tokens to deposit. @@ -164,6 +154,34 @@ contract SiloMarket is Initializable, Ownable { return (tokens, amounts); } + //////////////////////////////////////////////////// + /// View Functions + //////////////////////////////////////////////////// + + /// @notice Get the amount of Silo Market shares owned by this contract. + /// @param owner The owner has to be the address of the ARM contract. + /// @return shares The amount of Silo lending market shares owned by this contract. + function balanceOf(address owner) external view returns (uint256) { + if (owner != arm) return 0; + + // Get the balance of shares in the lending market + return IERC4626(market).balanceOf(address(this)); + } + + /// @notice The amount of shares that would exchanged for the amount of assets provided. + /// @param assets The amount of asset tokens to convert to shares. + /// @return shares The amount of Silo lending market shares that would be received. + function convertToShares(uint256 assets) external view returns (uint256 shares) { + shares = IERC4626(market).convertToShares(assets); + } + + /// @notice The amount of assets that would be exchanged for the amount of shares provided. + /// @param shares The amount of Silo lending market shares to convert to assets. + /// @return assets The amount of asset tokens that would be received. + function convertToAssets(uint256 shares) external view returns (uint256 assets) { + assets = IERC4626(market).convertToAssets(shares); + } + //////////////////////////////////////////////////// /// Admin Functions ////////////////////////////////////////////////////