From ceebea12fc3fcbf312ae78b29e01d1208d246f62 Mon Sep 17 00:00:00 2001 From: Pablo Veyrat Date: Wed, 29 May 2024 15:00:16 +0200 Subject: [PATCH] fix: harvester code --- contracts/helpers/Harvester.sol | 9 +++++-- scripts/DeployHarvester.s.sol | 26 +++++++++++++++++++ scripts/DeploySavingsImplem.s.sol | 3 +-- test/scripts/HarvesterUSDATest.t.sol | 2 ++ .../upgrade/SavingsNameableUpgradeTest.t.sol | 23 ++++++++-------- 5 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 scripts/DeployHarvester.s.sol diff --git a/contracts/helpers/Harvester.sol b/contracts/helpers/Harvester.sol index 0f62da42..96788642 100644 --- a/contracts/helpers/Harvester.sol +++ b/contracts/helpers/Harvester.sol @@ -71,11 +71,14 @@ contract Harvester is AccessControl { HARVEST //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ - /// @notice Invests or divests from the yield asset associated to `collateral` + /// @notice Invests or divests from the yield asset associated to `collateral` based on the current exposure to this + /// collateral /// @dev This transaction either reduces the exposure to `collateral` in the Transmuter or frees up some collateral - /// that can then be used for people looking to burn + /// that can then be used for people looking to burn stablecoins /// @dev Due to potential transaction fees within the Transmuter, this function doesn't exactly bring `collateral` /// to the target exposure + /// @dev The `harvest` possibility shouldn't be implemented for assets with a manipulable price (like ERC4626) + /// contracts on which the `previewRedeem` values can be easily moved by creating a loss or a profit function harvest(address collateral) external { (uint256 stablecoinsFromCollateral, uint256 stablecoinsIssued) = TRANSMUTER.getIssuedByCollateral(collateral); CollatParams memory collatInfo = collateralData[collateral]; @@ -124,6 +127,8 @@ contract Harvester is AccessControl { rebalancer = RebalancerFlashloan(_newRebalancer); } + /// @dev This function shouldn't be called for a vault (e.g an ERC4626 token) which price can be easily moved + /// by creating a loss or a profit, at the risk of depleting the reserves available in the Rebalancer function setCollateralData( address vault, uint64 targetExposure, diff --git a/scripts/DeployHarvester.s.sol b/scripts/DeployHarvester.s.sol new file mode 100644 index 00000000..ea5a0a1f --- /dev/null +++ b/scripts/DeployHarvester.s.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import "./utils/Utils.s.sol"; +import { console } from "forge-std/console.sol"; +import { Harvester } from "contracts/helpers/Harvester.sol"; +import "./Constants.s.sol"; + +contract DeployHarvester is Utils { + function run() external { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + address deployer = vm.addr(deployerPrivateKey); + console.log("Deployer address: ", deployer); + address rebalancer = 0x22604C0E5633A9810E01c9cb469B23Eee17AC411; + address vault = STEAK_USDC; + uint64 targetExposure = (13 * 1e9) / 100; + uint64 overrideExposures = 0; + uint96 maxSlippage = 1e9 / 100; + Harvester harvester = new Harvester(rebalancer, vault, targetExposure, overrideExposures, 0, 0, maxSlippage); + console.log("Harvester deployed at: ", address(harvester)); + + vm.stopBroadcast(); + } +} diff --git a/scripts/DeploySavingsImplem.s.sol b/scripts/DeploySavingsImplem.s.sol index 363d33d5..d69a8b9d 100644 --- a/scripts/DeploySavingsImplem.s.sol +++ b/scripts/DeploySavingsImplem.s.sol @@ -35,10 +35,9 @@ contract DeploySavingsImplem is Utils { address computedAddress = create2Factory.findCreate2Address(salt, initCode); console.log("Supposed to deploy: %s", computedAddress); if (computedAddress != 0x2C28Bd22aB59341892e85aD76d159d127c4B03FA) revert(); - /* + address saving = create2Factory.safeCreate2(salt, initCode); console.log("Savings implementation deployed at: ", address(saving)); - */ vm.stopBroadcast(); } diff --git a/test/scripts/HarvesterUSDATest.t.sol b/test/scripts/HarvesterUSDATest.t.sol index ac7ddff7..d665f3ac 100644 --- a/test/scripts/HarvesterUSDATest.t.sol +++ b/test/scripts/HarvesterUSDATest.t.sol @@ -82,6 +82,7 @@ contract HarvesterUSDATest is Test { assertGt(fromSTEAK, fromSTEAK2); assertGt(total, total2); assertApproxEqRel((fromUSDC2 * 1e9) / total2, targetExposure, 100 * BPS); + assertApproxEqRel(fromUSDC2 * 1e9, targetExposure * total, 100 * BPS); harvester.harvest(USDC); (uint256 fromUSDC3, uint256 total3) = transmuter.getIssuedByCollateral(address(USDC)); @@ -90,6 +91,7 @@ contract HarvesterUSDATest is Test { assertGt(fromSTEAK2, fromSTEAK3); assertGt(total2, total3); assertGt((fromUSDC3 * 1e9) / total3, (fromUSDC2 * 1e9) / total2); + assertApproxEqRel((fromUSDC3 * 1e9) / total3, (fromUSDC2 * 1e9) / total2, 10 * BPS); assertGt(targetExposure, (fromUSDC3 * 1e9) / total3); } diff --git a/test/units/upgrade/SavingsNameableUpgradeTest.t.sol b/test/units/upgrade/SavingsNameableUpgradeTest.t.sol index b0cbff4f..af456f77 100644 --- a/test/units/upgrade/SavingsNameableUpgradeTest.t.sol +++ b/test/units/upgrade/SavingsNameableUpgradeTest.t.sol @@ -10,8 +10,8 @@ import { IERC20Metadata } from "oz/interfaces/IERC20Metadata.sol"; import { TransparentUpgradeableProxy } from "oz/proxy/transparent/TransparentUpgradeableProxy.sol"; contract SavingsNameableUpgradeTest is Test, Helper { - uint256 constant CHAIN = CHAIN_ETHEREUM; - string constant CHAIN_NAME = "mainnet"; + uint256 constant CHAIN = CHAIN_POLYGONZKEVM; + string constant CHAIN_NAME = "polygonzkevm"; address public savings; address public savingsImpl; @@ -42,13 +42,14 @@ contract SavingsNameableUpgradeTest is Test, Helper { guardian = _chainToContract(CHAIN, ContractType.GuardianMultisig); // TODO: to be removed when chainToContract works - savings = 0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776; + // savings = 0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776; + savings = 0x004626A008B1aCdC4c74ab51644093b155e59A23; proxyAdmin = ProxyAdmin(0x1D941EF0D3Bba4ad67DBfBCeE5262F4CEE53A32b); - governor = 0xdC4e6DFe07EFCa50a197DF15D9200883eF4Eb1c8; - guardian = 0x0C2553e4B9dFA9f83b1A6D3EAB96c4bAaB42d430; + governor = 0x2a42Aeec7519883713272ec10FE44461a2Dfe354; + guardian = 0x10DeF8a92c51C8082087356186a1485301078DCd; - assertEq(IERC20Metadata(savings).name(), "Staked USDA"); - assertEq(IERC20Metadata(savings).symbol(), "stUSD"); + assertEq(IERC20Metadata(savings).name(), "Staked EURA"); + assertEq(IERC20Metadata(savings).symbol(), "stEUR"); rate = SavingsNameable(savings).rate(); lastUpdate = SavingsNameable(savings).lastUpdate(); paused = SavingsNameable(savings).paused(); @@ -60,19 +61,19 @@ contract SavingsNameableUpgradeTest is Test, Helper { previewWithdraw = SavingsNameable(savings).previewWithdraw(BASE_18); previewRedeem = SavingsNameable(savings).previewRedeem(BASE_18); - savingsImpl = address(new SavingsNameable()); - _upgradeContract("Staked USDA", "stUSD"); + // savingsImpl = address(new SavingsNameable()); + savingsImpl = 0x2C28Bd22aB59341892e85aD76d159d127c4B03FA; } function _upgradeContract(string memory name, string memory symbol) internal { - if (CHAIN == CHAIN_BASE || CHAIN == CHAIN_POLYGONZKEVM) vm.prank(guardian, guardian); - else vm.prank(governor, governor); + vm.prank(governor, governor); proxyAdmin.upgrade(TransparentUpgradeableProxy(payable(savings)), savingsImpl); vm.prank(governor, governor); SavingsNameable(savings).setNameAndSymbol(name, symbol); } function test_UpdatedValues() public { + _upgradeContract("Staked USDA", "stUSD"); assertEq(IERC20Metadata(savings).name(), "Staked USDA"); assertEq(IERC20Metadata(savings).symbol(), "stUSD"); assertEq(SavingsNameable(savings).previewRedeem(BASE_18), previewRedeem);