generated from AngleProtocol/boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* updating rebalancer * simplify tests * feat: add fuzz back * feat: add new test for minAmount * feat: add deployment script * Empty-Commit * tests: comment out upgrade tests of the transmuter and remove helpers * chore: add cache to folders to ignore * chore: fix slither ci infinite loop * chore: setup repo before slither ci --------- Co-authored-by: 0xtekgrinder <[email protected]>
- Loading branch information
1 parent
ec7d7ab
commit 6aecf1a
Showing
11 changed files
with
590 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,9 @@ jobs: | |
with: | ||
registry-token: ${{ secrets.GH_REGISTRY_ACCESS_TOKEN }} | ||
|
||
- name: Install dependencies | ||
run: yarn install | ||
|
||
- name: Run solhint | ||
run: yarn lint:check | ||
|
||
|
@@ -51,6 +54,9 @@ jobs: | |
with: | ||
registry-token: ${{ secrets.GH_REGISTRY_ACCESS_TOKEN }} | ||
|
||
- name: Install dependencies | ||
run: yarn install --frozen-lockfile | ||
|
||
- name: Compile foundry | ||
run: yarn compile --sizes | ||
|
||
|
@@ -224,12 +230,13 @@ jobs: | |
run: forge build --build-info --skip */test/** */scripts/** --force | ||
|
||
- name: "Run Slither analysis" | ||
uses: "crytic/[email protected].0" | ||
uses: "crytic/[email protected].2" | ||
id: "slither" | ||
with: | ||
ignore-compile: true | ||
fail-on: "none" | ||
sarif: "results.sarif" | ||
slither-version: "0.10.1" | ||
|
||
- name: "Upload SARIF file to GitHub code scanning" | ||
uses: "github/codeql-action/upload-sarif@v2" | ||
|
@@ -239,4 +246,4 @@ jobs: | |
- name: "Add Slither summary" | ||
run: | | ||
echo "## Slither result" >> $GITHUB_STEP_SUMMARY | ||
echo "✅ Uploaded to GitHub code scanning" >> $GITHUB_STEP_SUMMARY | ||
echo "✅ Uploaded to GitHub code scanning" >> $GITHUB_STEP_SUMMARY |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ export | |
typechain | ||
slither-audit.txt | ||
slither | ||
cache | ||
|
||
# Test output | ||
coverage | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity ^0.8.19; | ||
|
||
import "./Rebalancer.sol"; | ||
import { IERC4626 } from "interfaces/external/IERC4626.sol"; | ||
import { IERC3156FlashBorrower } from "oz/interfaces/IERC3156FlashBorrower.sol"; | ||
import { IERC3156FlashLender } from "oz/interfaces/IERC3156FlashLender.sol"; | ||
|
||
/// @title RebalancerFlashloan | ||
/// @author Angle Labs, Inc. | ||
/// @dev Rebalancer contract for a Transmuter with as collaterals a liquid stablecoin and an ERC4626 token | ||
/// using this liquid stablecoin as an asset | ||
contract RebalancerFlashloan is Rebalancer, IERC3156FlashBorrower { | ||
using SafeERC20 for IERC20; | ||
using SafeCast for uint256; | ||
bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); | ||
|
||
/// @notice Angle stablecoin flashloan contract | ||
IERC3156FlashLender public immutable FLASHLOAN; | ||
|
||
constructor( | ||
IAccessControlManager _accessControlManager, | ||
ITransmuter _transmuter, | ||
IERC3156FlashLender _flashloan | ||
) Rebalancer(_accessControlManager, _transmuter) { | ||
if (address(_flashloan) == address(0)) revert ZeroAddress(); | ||
FLASHLOAN = _flashloan; | ||
IERC20(AGTOKEN).safeApprove(address(_flashloan), type(uint256).max); | ||
} | ||
|
||
/// @notice Burns `amountStablecoins` for one collateral asset and mints stablecoins from the proceeds of the | ||
/// first burn | ||
/// @dev If `increase` is 1, then the system tries to increase its exposure to the yield bearing asset which means | ||
/// burning stablecoin for the liquid asset, depositing into the ERC4626 vault, then minting the stablecoin | ||
/// @dev This function reverts if the second stablecoin mint gives less than `minAmountOut` of stablecoins | ||
function adjustYieldExposure( | ||
uint256 amountStablecoins, | ||
uint8 increase, | ||
address collateral, | ||
address vault, | ||
uint256 minAmountOut | ||
) external { | ||
if (!TRANSMUTER.isTrustedSeller(msg.sender)) revert NotTrusted(); | ||
FLASHLOAN.flashLoan( | ||
IERC3156FlashBorrower(address(this)), | ||
address(AGTOKEN), | ||
amountStablecoins, | ||
abi.encode(increase, collateral, vault, minAmountOut) | ||
); | ||
} | ||
|
||
/// @inheritdoc IERC3156FlashBorrower | ||
function onFlashLoan( | ||
address initiator, | ||
address, | ||
uint256 amount, | ||
uint256 fee, | ||
bytes calldata data | ||
) external returns (bytes32) { | ||
if (msg.sender != address(FLASHLOAN) || initiator != address(this) || fee != 0) revert NotTrusted(); | ||
(uint256 typeAction, address collateral, address vault, uint256 minAmountOut) = abi.decode( | ||
data, | ||
(uint256, address, address, uint256) | ||
); | ||
address tokenOut; | ||
address tokenIn; | ||
if (typeAction == 1) { | ||
// Increase yield exposure action: we bring in the ERC4626 token | ||
tokenOut = collateral; | ||
tokenIn = vault; | ||
} else { | ||
// Decrease yield exposure action: we bring in the liquid asset | ||
tokenIn = collateral; | ||
tokenOut = vault; | ||
} | ||
uint256 amountOut = TRANSMUTER.swapExactInput(amount, 0, AGTOKEN, tokenOut, address(this), block.timestamp); | ||
if (typeAction == 1) { | ||
// Granting allowance with the collateral for the vault asset | ||
_adjustAllowance(collateral, vault, amountOut); | ||
amountOut = IERC4626(vault).deposit(amountOut, address(this)); | ||
} else amountOut = IERC4626(vault).redeem(amountOut, address(this), address(this)); | ||
_adjustAllowance(tokenIn, address(TRANSMUTER), amountOut); | ||
uint256 amountStableOut = TRANSMUTER.swapExactInput( | ||
amountOut, | ||
minAmountOut, | ||
tokenIn, | ||
AGTOKEN, | ||
address(this), | ||
block.timestamp | ||
); | ||
if (amount > amountStableOut) { | ||
uint256 subsidy = amount - amountStableOut; | ||
orders[tokenIn][tokenOut].subsidyBudget -= subsidy.toUint112(); | ||
budget -= subsidy; | ||
emit SubsidyPaid(tokenIn, tokenOut, subsidy); | ||
} | ||
return CALLBACK_SUCCESS; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.19; | ||
|
||
import "./utils/Utils.s.sol"; | ||
import { console } from "forge-std/console.sol"; | ||
import { RebalancerFlashloan } from "contracts/helpers/RebalancerFlashloan.sol"; | ||
import { IAccessControlManager } from "contracts/utils/AccessControl.sol"; | ||
import { ITransmuter } from "contracts/interfaces/ITransmuter.sol"; | ||
import { IERC3156FlashLender } from "oz/interfaces/IERC3156FlashLender.sol"; | ||
import "./Constants.s.sol"; | ||
import "oz/interfaces/IERC20.sol"; | ||
import "oz-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; | ||
|
||
contract DeployRebalancerFlashloan 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); | ||
console.log(address(IAccessControlManager(_chainToContract(CHAIN_SOURCE, ContractType.CoreBorrow)))); | ||
console.log(address(ITransmuter(_chainToContract(CHAIN_SOURCE, ContractType.TransmuterAgUSD)))); | ||
RebalancerFlashloan rebalancer = new RebalancerFlashloan( | ||
IAccessControlManager(_chainToContract(CHAIN_SOURCE, ContractType.CoreBorrow)), | ||
ITransmuter(_chainToContract(CHAIN_SOURCE, ContractType.TransmuterAgUSD)), | ||
IERC3156FlashLender(0x4A2FF9bC686A0A23DA13B6194C69939189506F7F) | ||
); | ||
/* | ||
RebalancerFlashloan rebalancer = new RebalancerFlashloan( | ||
IAccessControlManager(0x5bc6BEf80DA563EBf6Df6D6913513fa9A7ec89BE), | ||
ITransmuter(0x222222fD79264BBE280b4986F6FEfBC3524d0137), | ||
IERC3156FlashLender(0x4A2FF9bC686A0A23DA13B6194C69939189506F7F) | ||
); | ||
*/ | ||
console.log("Rebalancer deployed at: ", address(rebalancer)); | ||
|
||
vm.stopBroadcast(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.19; | ||
|
||
import { SafeERC20 } from "oz/token/ERC20/utils/SafeERC20.sol"; | ||
|
||
import { stdError } from "forge-std/Test.sol"; | ||
|
||
import "contracts/utils/Errors.sol" as Errors; | ||
|
||
import "../Fixture.sol"; | ||
import { IERC20Metadata } from "../mock/MockTokenPermit.sol"; | ||
import "../utils/FunctionUtils.sol"; | ||
|
||
import "contracts/savings/Savings.sol"; | ||
import "../mock/MockTokenPermit.sol"; | ||
import "contracts/helpers/RebalancerFlashloan.sol"; | ||
|
||
contract RebalancerFlashloanTest is Fixture, FunctionUtils { | ||
using SafeERC20 for IERC20; | ||
|
||
RebalancerFlashloan public rebalancer; | ||
Savings internal _saving; | ||
string internal _name; | ||
string internal _symbol; | ||
address public collat; | ||
|
||
function setUp() public override { | ||
super.setUp(); | ||
|
||
MockTokenPermit token = new MockTokenPermit("EURC", "EURC", 6); | ||
collat = address(token); | ||
|
||
address _savingImplementation = address(new Savings()); | ||
bytes memory data; | ||
_saving = Savings(_deployUpgradeable(address(proxyAdmin), address(_savingImplementation), data)); | ||
_name = "savingAgEUR"; | ||
_symbol = "SAGEUR"; | ||
|
||
vm.startPrank(governor); | ||
token.mint(governor, 1e12); | ||
token.approve(address(_saving), 1e12); | ||
_saving.initialize(accessControlManager, IERC20MetadataUpgradeable(address(token)), _name, _symbol, BASE_6); | ||
vm.stopPrank(); | ||
|
||
rebalancer = new RebalancerFlashloan(accessControlManager, transmuter, IERC3156FlashLender(governor)); | ||
} | ||
|
||
function test_RebalancerInitialization() public { | ||
assertEq(address(rebalancer.accessControlManager()), address(accessControlManager)); | ||
assertEq(address(rebalancer.AGTOKEN()), address(agToken)); | ||
assertEq(address(rebalancer.TRANSMUTER()), address(transmuter)); | ||
assertEq(address(rebalancer.FLASHLOAN()), governor); | ||
assertEq(IERC20Metadata(address(agToken)).allowance(address(rebalancer), address(governor)), type(uint256).max); | ||
assertEq(IERC20Metadata(address(collat)).allowance(address(rebalancer), address(_saving)), 0); | ||
} | ||
|
||
function test_Constructor_RevertWhen_ZeroAddress() public { | ||
vm.expectRevert(Errors.ZeroAddress.selector); | ||
new RebalancerFlashloan(accessControlManager, transmuter, IERC3156FlashLender(address(0))); | ||
} | ||
|
||
function test_adjustYieldExposure_RevertWhen_NotTrusted() public { | ||
vm.expectRevert(Errors.NotTrusted.selector); | ||
rebalancer.adjustYieldExposure(1, 1, address(0), address(0), 0); | ||
} | ||
|
||
function test_onFlashLoan_RevertWhen_NotTrusted() public { | ||
vm.expectRevert(Errors.NotTrusted.selector); | ||
rebalancer.onFlashLoan(address(rebalancer), address(0), 1, 0, abi.encode(1)); | ||
|
||
vm.expectRevert(Errors.NotTrusted.selector); | ||
rebalancer.onFlashLoan(address(rebalancer), address(0), 1, 1, abi.encode(1)); | ||
|
||
vm.expectRevert(Errors.NotTrusted.selector); | ||
vm.startPrank(governor); | ||
rebalancer.onFlashLoan(address(0), address(0), 1, 0, abi.encode(1)); | ||
vm.stopPrank(); | ||
|
||
vm.expectRevert(Errors.NotTrusted.selector); | ||
vm.startPrank(governor); | ||
rebalancer.onFlashLoan(address(rebalancer), address(0), 1, 1, abi.encode(1)); | ||
vm.stopPrank(); | ||
|
||
vm.expectRevert(); | ||
vm.startPrank(governor); | ||
rebalancer.onFlashLoan(address(rebalancer), address(0), 1, 0, abi.encode(1, 2)); | ||
vm.stopPrank(); | ||
} | ||
} |
Oops, something went wrong.