|
| 1 | +// SPDX-License-Identifier: MIT |
| 2 | +pragma solidity ^0.8.29; |
| 3 | + |
| 4 | +import "forge-std/Test.sol"; |
| 5 | +import "forge-std/console.sol"; |
| 6 | + |
| 7 | +import {DeployFeeDiamond} from "../../script/DeployFeeDiamond.sol"; |
| 8 | + |
| 9 | +import {Fee} from "../../contracts/fees/Fee.sol"; |
| 10 | +import {FeeViewsFacet} from "../../contracts/fees/facets/FeeViewsFacet.sol"; |
| 11 | +import {FeeAdminFacet} from "../../contracts/fees/facets/FeeAdminFacet.sol"; |
| 12 | +import {AerodromeSwapFeeFacet} from "../../contracts/fees/facets/AerodromeSwapFeeFacet.sol"; |
| 13 | +import {LibFeeStorage} from "../../contracts/fees/LibFeeStorage.sol"; |
| 14 | +import {FeeUtils} from "../../contracts/fees/FeeUtils.sol"; |
| 15 | +import {OwnershipFacet} from "../../contracts/diamond-base/facets/OwnershipFacet.sol"; |
| 16 | + |
| 17 | +import {USDC} from "../ABIs/USDC.sol"; |
| 18 | +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; |
| 19 | +import { IRouter } from "@aerodrome/contracts/interfaces/IRouter.sol"; |
| 20 | + |
| 21 | +contract FeeForkTest is Test { |
| 22 | + address owner; |
| 23 | + address APP_USER_ALICE = makeAddr("Alice"); |
| 24 | + // real aerodrome router on base from https://www.aerodrome.finance/security |
| 25 | + address REAL_AERODROME_ROUTER = 0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43; |
| 26 | + // real USDC address on base |
| 27 | + address REAL_USDC = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; |
| 28 | + // real USDC master_minter |
| 29 | + address REAL_USDC_MASTER_MINTER; |
| 30 | + address USDC_MINTER = makeAddr("USDCMinter"); |
| 31 | + // real WETH address on base |
| 32 | + address REAL_WETH = 0x4200000000000000000000000000000000000006; |
| 33 | + |
| 34 | + Fee public feeDiamond; |
| 35 | + FeeViewsFacet public feeViewsFacet; |
| 36 | + FeeAdminFacet public feeAdminFacet; |
| 37 | + AerodromeSwapFeeFacet public aerodromeSwapFeeFacet; |
| 38 | + OwnershipFacet public ownershipFacet; |
| 39 | + |
| 40 | + USDC public USDCErc20; |
| 41 | + ERC20 public WETHErc20; |
| 42 | + IRouter public aerodromeRouter; |
| 43 | + uint256 public erc20Decimals; |
| 44 | + |
| 45 | + function setUp() public { |
| 46 | + uint256 deployerPrivateKey = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; |
| 47 | + vm.setEnv("VINCENT_DEPLOYER_PRIVATE_KEY", vm.toString(deployerPrivateKey)); |
| 48 | + owner = vm.addr(deployerPrivateKey); |
| 49 | + |
| 50 | + DeployFeeDiamond deployScript = new DeployFeeDiamond(); |
| 51 | + |
| 52 | + address diamondAddress = deployScript.deployToNetwork("test", keccak256("testSalt")); |
| 53 | + feeDiamond = Fee(payable(diamondAddress)); |
| 54 | + |
| 55 | + feeViewsFacet = FeeViewsFacet(diamondAddress); |
| 56 | + feeAdminFacet = FeeAdminFacet(diamondAddress); |
| 57 | + aerodromeSwapFeeFacet = AerodromeSwapFeeFacet(diamondAddress); |
| 58 | + ownershipFacet = OwnershipFacet(diamondAddress); |
| 59 | + |
| 60 | + // set the aave pool address in the fee diamond |
| 61 | + vm.startPrank(owner); |
| 62 | + feeAdminFacet.setAerodromeRouter(REAL_AERODROME_ROUTER); |
| 63 | + vm.stopPrank(); |
| 64 | + |
| 65 | + // set up the real aave pool and USDC token |
| 66 | + aerodromeRouter = IRouter(REAL_AERODROME_ROUTER); |
| 67 | + USDCErc20 = USDC(REAL_USDC); |
| 68 | + WETHErc20 = ERC20(REAL_WETH); |
| 69 | + REAL_USDC_MASTER_MINTER = USDCErc20.masterMinter(); |
| 70 | + // configure the USDC minter |
| 71 | + vm.prank(REAL_USDC_MASTER_MINTER); |
| 72 | + USDCErc20.configureMinter(USDC_MINTER, type(uint256).max); |
| 73 | + vm.stopPrank(); |
| 74 | + erc20Decimals = USDCErc20.decimals(); |
| 75 | + console.log("setUp complete"); |
| 76 | + } |
| 77 | + |
| 78 | + function testSingleRouteSwap() public { |
| 79 | + uint256 swapAmount = 50 * 10 ** erc20Decimals; |
| 80 | + |
| 81 | + // mint the USDC to the user |
| 82 | + vm.startPrank(USDC_MINTER); |
| 83 | + USDCErc20.mint(APP_USER_ALICE, swapAmount); |
| 84 | + vm.stopPrank(); |
| 85 | + console.log("minted USDC to user"); |
| 86 | + |
| 87 | + vm.startPrank(APP_USER_ALICE); |
| 88 | + USDCErc20.approve(address(aerodromeSwapFeeFacet), swapAmount); |
| 89 | + console.log("approved USDC to our fee contract"); |
| 90 | + // create the route |
| 91 | + IRouter.Route[] memory routes = new IRouter.Route[](1); |
| 92 | + routes[0] = IRouter.Route(address(REAL_USDC), address(REAL_WETH), false, address(0)); |
| 93 | + |
| 94 | + // reduce the expected output by 0.5% for slippage |
| 95 | + uint256 expectedOutput = (aerodromeRouter.getAmountsOut(swapAmount, routes)[1] * 9950) / 10000; |
| 96 | + |
| 97 | + uint256 userWethBalanceBefore = WETHErc20.balanceOf(APP_USER_ALICE); |
| 98 | + |
| 99 | + aerodromeSwapFeeFacet.swapExactTokensForTokensOnAerodrome(swapAmount, expectedOutput, routes, APP_USER_ALICE, block.timestamp + 1 minutes); |
| 100 | + vm.stopPrank(); |
| 101 | + console.log("swapped USDC to WETH"); |
| 102 | + uint256 userWethBalanceAfter = WETHErc20.balanceOf(APP_USER_ALICE); |
| 103 | + assertGt(userWethBalanceAfter - userWethBalanceBefore, expectedOutput); |
| 104 | + console.log("userWethBalanceAfter", userWethBalanceAfter); |
| 105 | + |
| 106 | + // confirm the profit went to the fee contract, and the rest of the tokens went to the user |
| 107 | + uint256 userBalance = USDCErc20.balanceOf(APP_USER_ALICE); |
| 108 | + uint256 feeContractBalance = USDCErc20.balanceOf(address(aerodromeSwapFeeFacet)); |
| 109 | + console.log("usdc userBalance", userBalance); |
| 110 | + console.log("usdc feeContractBalance", feeContractBalance); |
| 111 | + |
| 112 | + // uint256 expectedTotalProfit = expectedTotalWithdrawal - depositAmount; |
| 113 | + // uint256 expectedUserProfit = expectedTotalProfit - (expectedTotalProfit * performanceFeePercentage / 10000); |
| 114 | + // uint256 expectedFeeContractProfit = expectedTotalProfit * performanceFeePercentage / 10000; |
| 115 | + // console.log("expectedTotalProfit", expectedTotalProfit); |
| 116 | + // console.log("expectedUserProfit", expectedUserProfit); |
| 117 | + // console.log("expectedFeeContractProfit", expectedFeeContractProfit); |
| 118 | + // console.log("userProfit", userBalance); |
| 119 | + // console.log("feeContractProfit", feeContractBalance); |
| 120 | + |
| 121 | + // assertEq(userBalance, depositAmount + expectedUserProfit); |
| 122 | + // assertEq(feeContractBalance, expectedFeeContractProfit); |
| 123 | + |
| 124 | + // test that USDC is in the set of tokens that have collected fees |
| 125 | + address[] memory tokensWithCollectedFees = feeAdminFacet.tokensWithCollectedFees(); |
| 126 | + assertEq(tokensWithCollectedFees.length, 1); |
| 127 | + assertEq(tokensWithCollectedFees[0], address(USDCErc20)); |
| 128 | + |
| 129 | + // test withdrawal of profit from the fee contract as owner |
| 130 | + vm.startPrank(owner); |
| 131 | + feeAdminFacet.withdrawTokens(address(USDCErc20)); |
| 132 | + vm.stopPrank(); |
| 133 | + |
| 134 | + // confirm the profit went to the owner |
| 135 | + // assertEq(USDCErc20.balanceOf(owner), expectedFeeContractProfit); |
| 136 | + |
| 137 | + // confirm that the token is no longer in the set of tokens that have collected fees |
| 138 | + tokensWithCollectedFees = feeAdminFacet.tokensWithCollectedFees(); |
| 139 | + assertEq(tokensWithCollectedFees.length, 0); |
| 140 | + } |
| 141 | +} |
0 commit comments