From 3f70474d2a079a270bd8a3cea1b79f5dcfa96ac2 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 13 Dec 2024 10:09:54 +0100 Subject: [PATCH] feat: add initial gas snapshots (#91) While the ci continuously checks gas usage via --gas-report, having isolated gas metering has some merit as it allows exact tracking for selected scenarios. --- .prettierignore | 1 + Makefile | 3 + snapshots/AToken.transfer.json | 11 ++ snapshots/Pool.Getters.json | 10 ++ snapshots/Pool.Operations.json | 15 +++ snapshots/Pool.Setters.json | 6 ++ snapshots/StataTokenV2.json | 6 ++ tests/gas/AToken.Transfer.gas.t.sol | 145 +++++++++++++++++++++++++ tests/gas/Pool.Getters.gas.t.sol | 67 ++++++++++++ tests/gas/Pool.Operations.gas.t.sol | 162 ++++++++++++++++++++++++++++ tests/gas/Pool.Setters.gas.t.sol | 56 ++++++++++ tests/gas/StataToken.gas.t.sol | 55 ++++++++++ tests/gas/Testhelpers.sol | 61 +++++++++++ 13 files changed, 598 insertions(+) create mode 100644 snapshots/AToken.transfer.json create mode 100644 snapshots/Pool.Getters.json create mode 100644 snapshots/Pool.Operations.json create mode 100644 snapshots/Pool.Setters.json create mode 100644 snapshots/StataTokenV2.json create mode 100644 tests/gas/AToken.Transfer.gas.t.sol create mode 100644 tests/gas/Pool.Getters.gas.t.sol create mode 100644 tests/gas/Pool.Operations.gas.t.sol create mode 100644 tests/gas/Pool.Setters.gas.t.sol create mode 100644 tests/gas/StataToken.gas.t.sol create mode 100644 tests/gas/Testhelpers.sol diff --git a/.prettierignore b/.prettierignore index 2339d723..e76b03e3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,4 @@ lib cache node_modules report +snapshots diff --git a/Makefile b/Makefile index 8db7ae3b..1a8e2b28 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,9 @@ coverage : make coverage-report make coverage-badge +# Gas reports +forge test --mp 'tests/gas/*.t.sol' --isolate + # Utilities download :; cast etherscan-source --chain ${chain} -d src/etherscan/${chain}_${address} ${address} git-diff : diff --git a/snapshots/AToken.transfer.json b/snapshots/AToken.transfer.json new file mode 100644 index 00000000..76ac4bda --- /dev/null +++ b/snapshots/AToken.transfer.json @@ -0,0 +1,11 @@ +{ + "full amount; receiver: ->enableCollateral": "144837", + "full amount; sender: ->disableCollateral;": "103274", + "full amount; sender: ->disableCollateral; receiver: ->enableCollateral": "145016", + "full amount; sender: ->disableCollateral; receiver: dirty, ->enableCollateral": "133059", + "full amount; sender: collateralDisabled": "103095", + "partial amount; sender: collateralDisabled;": "103095", + "partial amount; sender: collateralDisabled; receiver: ->enableCollateral": "144837", + "partial amount; sender: collateralEnabled;": "103303", + "partial amount; sender: collateralEnabled; receiver: ->enableCollateral": "145045" +} \ No newline at end of file diff --git a/snapshots/Pool.Getters.json b/snapshots/Pool.Getters.json new file mode 100644 index 00000000..954829ee --- /dev/null +++ b/snapshots/Pool.Getters.json @@ -0,0 +1,10 @@ +{ + "getEModeCategoryCollateralConfig": "8000", + "getEModeCategoryData": "13430", + "getLiquidationGracePeriod": "29197", + "getReserveData": "34935", + "getUserAccountData: supplies: 0, borrows: 0": "22641", + "getUserAccountData: supplies: 1, borrows: 0": "34774", + "getUserAccountData: supplies: 2, borrows: 0": "47296", + "getUserAccountData: supplies: 2, borrows: 1": "28968" +} \ No newline at end of file diff --git a/snapshots/Pool.Operations.json b/snapshots/Pool.Operations.json new file mode 100644 index 00000000..834b5f33 --- /dev/null +++ b/snapshots/Pool.Operations.json @@ -0,0 +1,15 @@ +{ + "borrow: first borrow->borrowingEnabled": "256189", + "borrow: recurrent borrow": "248474", + "liquidationCall: deficit on liquidated asset": "392266", + "liquidationCall: deficit on liquidated asset + other asset": "422243", + "liquidationCall: full liquidation": "392266", + "liquidationCall: partial liquidation": "381149", + "repay: full repay": "176547", + "repay: partial repay": "189971", + "supply: collateralDisabled": "146829", + "supply: collateralEnabled": "146829", + "supply: first supply->collateralEnabled": "176387", + "withdraw: full withdraw": "165303", + "withdraw: partial withdraw": "181992" +} \ No newline at end of file diff --git a/snapshots/Pool.Setters.json b/snapshots/Pool.Setters.json new file mode 100644 index 00000000..a92f7e05 --- /dev/null +++ b/snapshots/Pool.Setters.json @@ -0,0 +1,6 @@ +{ + "setUserEMode: enter eMode, 1 borrow, 1 supply": "140164", + "setUserEMode: leave eMode, 1 borrow, 1 supply": "112199", + "setUserUseReserveAsCollateral: disableCollateral, 1 supply": "93434", + "setUserUseReserveAsCollateral: enableCollateral, 1 supply": "105145" +} \ No newline at end of file diff --git a/snapshots/StataTokenV2.json b/snapshots/StataTokenV2.json new file mode 100644 index 00000000..5f9315f7 --- /dev/null +++ b/snapshots/StataTokenV2.json @@ -0,0 +1,6 @@ +{ + "deposit": "284677", + "depositATokens": "220136", + "redeem": "205767", + "redeemAToken": "153413" +} \ No newline at end of file diff --git a/tests/gas/AToken.Transfer.gas.t.sol b/tests/gas/AToken.Transfer.gas.t.sol new file mode 100644 index 00000000..cd471e70 --- /dev/null +++ b/tests/gas/AToken.Transfer.gas.t.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import {IAToken} from '../../src/contracts/interfaces/IAToken.sol'; +import {Errors} from '../../src/contracts/protocol/libraries/helpers/Errors.sol'; +import {Testhelpers, IERC20} from './Testhelpers.sol'; + +/** + * Scenario suite for transfer operations. + */ +contract ATokenTransfer_gas_Tests is Testhelpers { + address token; + IAToken aToken; + address variableDebtToken; + + address sender = makeAddr('sender'); + address receiver = makeAddr('receiver'); + + function setUp() public override { + super.setUp(); + token = tokenList.usdx; + (address aTokenAddress, , address variableDebtTokenAddress) = contracts + .protocolDataProvider + .getReserveTokensAddresses(tokenList.usdx); + aToken = IAToken(aTokenAddress); + variableDebtToken = variableDebtTokenAddress; + } + + function test_transfer_fullAmount() external { + _supplyOnReserve(sender, 1 ether); + vm.prank(sender); + + _skip(100); + + aToken.transfer(receiver, 1 ether); + vm.snapshotGasLastCall( + 'AToken.transfer', + 'full amount; sender: ->disableCollateral; receiver: ->enableCollateral' + ); + } + + function test_transfer_fullAmount_dirtyReceiver() external { + _supplyOnReserve(receiver, 1 ether, tokenList.weth); + _supplyOnReserve(sender, 1 ether); + vm.prank(sender); + + _skip(100); + + aToken.transfer(receiver, 1 ether); + vm.snapshotGasLastCall( + 'AToken.transfer', + 'full amount; sender: ->disableCollateral; receiver: dirty, ->enableCollateral' + ); + } + + function test_transfer_fullAmount_senderCollateralDisabled() external { + _supplyOnReserve(sender, 1 ether); + vm.startPrank(sender); + contracts.poolProxy.setUserUseReserveAsCollateral(token, false); + + _skip(100); + + aToken.transfer(receiver, 1 ether); + vm.snapshotGasLastCall('AToken.transfer', 'full amount; receiver: ->enableCollateral'); + } + + function test_transfer_fullAmount_senderCollateralDisabled_receiverNonZeroFunds2() external { + _supplyOnReserve(sender, 1 ether); + _supplyOnReserve(receiver, 1 ether); + vm.startPrank(sender); + + _skip(100); + + aToken.transfer(receiver, 1 ether); + vm.snapshotGasLastCall('AToken.transfer', 'full amount; sender: ->disableCollateral;'); + } + + function test_transfer_fullAmount_senderCollateralDisabled_receiverNonZeroFunds() external { + _supplyOnReserve(sender, 1 ether); + _supplyOnReserve(receiver, 1 ether); + vm.startPrank(sender); + contracts.poolProxy.setUserUseReserveAsCollateral(token, false); + + _skip(100); + + aToken.transfer(receiver, 1 ether); + vm.snapshotGasLastCall('AToken.transfer', 'full amount; sender: collateralDisabled'); + } + + function test_transfer_partialAmount_senderCollateralEnabled() external { + _supplyOnReserve(sender, 1 ether); + vm.startPrank(sender); + + _skip(100); + + aToken.transfer(receiver, 0.5 ether); + vm.snapshotGasLastCall( + 'AToken.transfer', + 'partial amount; sender: collateralEnabled; receiver: ->enableCollateral' + ); + } + + function test_transfer_partialAmount_senderCollateralEnabled_receiverNonZeroFunds() external { + _supplyOnReserve(sender, 1 ether); + _supplyOnReserve(receiver, 1 ether); + vm.startPrank(sender); + + _skip(100); + + aToken.transfer(receiver, 0.5 ether); + vm.snapshotGasLastCall('AToken.transfer', 'partial amount; sender: collateralEnabled;'); + } + + function test_transfer_partialAmount_receiverNonZeroFunds() external { + _supplyOnReserve(sender, 1 ether); + _supplyOnReserve(receiver, 1 ether); + vm.startPrank(sender); + contracts.poolProxy.setUserUseReserveAsCollateral(token, false); + + _skip(100); + + aToken.transfer(receiver, 0.5 ether); + vm.snapshotGasLastCall('AToken.transfer', 'partial amount; sender: collateralDisabled;'); + } + + function test_transfer_partialAmount() external { + _supplyOnReserve(sender, 1 ether); + vm.startPrank(sender); + contracts.poolProxy.setUserUseReserveAsCollateral(token, false); + + _skip(100); + + aToken.transfer(receiver, 0.5 ether); + vm.snapshotGasLastCall( + 'AToken.transfer', + 'partial amount; sender: collateralDisabled; receiver: ->enableCollateral' + ); + } + + function _supplyOnReserve(address user, uint256 amount) internal { + _supplyOnReserve(user, amount, token); + } +} diff --git a/tests/gas/Pool.Getters.gas.t.sol b/tests/gas/Pool.Getters.gas.t.sol new file mode 100644 index 00000000..0e6735f7 --- /dev/null +++ b/tests/gas/Pool.Getters.gas.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import {Errors} from '../../src/contracts/protocol/libraries/helpers/Errors.sol'; +import {UserConfiguration} from '../../src/contracts/protocol/libraries/configuration/UserConfiguration.sol'; +import {Testhelpers, IERC20} from './Testhelpers.sol'; + +/** + * Scenario suite for pool getter operations. + */ +contract PoolGetters_gas_Tests is Testhelpers { + // mock users to supply and borrow liquidity + address user = makeAddr('user'); + + function test_getReserveData() external { + contracts.poolProxy.getReserveData(tokenList.usdx); + vm.snapshotGasLastCall('Pool.Getters', 'getReserveData'); + } + + function test_getUserAccountData() external { + contracts.poolProxy.getUserAccountData(user); + vm.snapshotGasLastCall('Pool.Getters', 'getUserAccountData: supplies: 0, borrows: 0'); + } + + function test_getUserAccountData_oneSupplies() external { + _supplyOnReserve(user, 1 ether, tokenList.usdx); + contracts.poolProxy.getUserAccountData(user); + vm.snapshotGasLastCall('Pool.Getters', 'getUserAccountData: supplies: 1, borrows: 0'); + } + + function test_getUserAccountData_twoSupplies() external { + _supplyOnReserve(user, 1 ether, tokenList.usdx); + _supplyOnReserve(user, 1 ether, tokenList.weth); + + contracts.poolProxy.getUserAccountData(user); + vm.snapshotGasLastCall('Pool.Getters', 'getUserAccountData: supplies: 2, borrows: 0'); + } + + function test_getUserAccountData_twoSupplies_oneBorrows() external { + _supplyOnReserve(user, 1 ether, tokenList.usdx); + _supplyOnReserve(user, 1 ether, tokenList.weth); + + _supplyOnReserve(address(1), 0.001e8, tokenList.wbtc); + vm.prank(user); + contracts.poolProxy.borrow(tokenList.wbtc, 0.001e8, 2, 0, user); + + contracts.poolProxy.getUserAccountData(user); + vm.snapshotGasLastCall('Pool.Getters', 'getUserAccountData: supplies: 2, borrows: 1'); + } + + function test_getEModeCategoryData() external { + contracts.poolProxy.getEModeCategoryData(1); + vm.snapshotGasLastCall('Pool.Getters', 'getEModeCategoryData'); + } + + function test_getEModeCategoryCollateralConfig() external { + contracts.poolProxy.getEModeCategoryCollateralConfig(1); + vm.snapshotGasLastCall('Pool.Getters', 'getEModeCategoryCollateralConfig'); + } + + function test_getLiquidationGracePeriod() external { + contracts.poolProxy.getLiquidationGracePeriod(tokenList.usdx); + vm.snapshotGasLastCall('Pool.Getters', 'getLiquidationGracePeriod'); + } +} diff --git a/tests/gas/Pool.Operations.gas.t.sol b/tests/gas/Pool.Operations.gas.t.sol new file mode 100644 index 00000000..8318c0f1 --- /dev/null +++ b/tests/gas/Pool.Operations.gas.t.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import {Errors} from '../../src/contracts/protocol/libraries/helpers/Errors.sol'; +import {UserConfiguration} from '../../src/contracts/protocol/libraries/configuration/UserConfiguration.sol'; +import {Testhelpers, IERC20} from './Testhelpers.sol'; + +/** + * Scenario suite for common operations supply/borrow/repay/withdraw/liquidationCall. + */ +contract PoolOperations_gas_Tests is Testhelpers { + address supplier = makeAddr('supplier'); + address borrower = makeAddr('borrower'); + address liquidator = makeAddr('liquidator'); + + function test_supply() external { + // borrow some, so hf checks are not skipped + _supplyOnReserve(supplier, 1 ether, tokenList.weth); + _borrowArbitraryAmount(supplier, 1e5, tokenList.wbtc); + + _supplyOnReserve(supplier, 100e6, tokenList.usdx); + vm.snapshotGasLastCall('Pool.Operations', 'supply: first supply->collateralEnabled'); + + _skip(100); + + _supplyOnReserve(supplier, 100e6, tokenList.usdx); + vm.snapshotGasLastCall('Pool.Operations', 'supply: collateralEnabled'); + vm.prank(supplier); + contracts.poolProxy.setUserUseReserveAsCollateral(tokenList.usdx, false); + + _skip(100); + + _supplyOnReserve(supplier, 100e6, tokenList.usdx); + vm.snapshotGasLastCall('Pool.Operations', 'supply: collateralDisabled'); + } + + function test_withdraw() external { + _supplyOnReserve(supplier, 100e6, tokenList.usdx); + vm.startPrank(supplier); + _skip(100); + + contracts.poolProxy.withdraw(tokenList.usdx, 50e6, supplier); + vm.snapshotGasLastCall('Pool.Operations', 'withdraw: partial withdraw'); + + _skip(100); + + contracts.poolProxy.withdraw(tokenList.usdx, type(uint256).max, supplier); + vm.snapshotGasLastCall('Pool.Operations', 'withdraw: full withdraw'); + } + + function test_borrow() external { + _supplyOnReserve(borrower, 100 ether, tokenList.weth); + uint256 amountToBorrow = 1000e6; + vm.startPrank(borrower); + contracts.poolProxy.borrow(tokenList.usdx, amountToBorrow, 2, 0, borrower); + vm.snapshotGasLastCall('Pool.Operations', 'borrow: first borrow->borrowingEnabled'); + + _skip(100); + + contracts.poolProxy.borrow(tokenList.usdx, amountToBorrow, 2, 0, borrower); + vm.snapshotGasLastCall('Pool.Operations', 'borrow: recurrent borrow'); + } + + function test_repay() external { + _supplyOnReserve(borrower, 100 ether, tokenList.weth); + uint256 amountToBorrow = 1000e6; + deal(tokenList.usdx, borrower, amountToBorrow); + vm.startPrank(borrower); + contracts.poolProxy.borrow(tokenList.usdx, amountToBorrow, 2, 0, borrower); + IERC20(tokenList.usdx).approve(report.poolProxy, type(uint256).max); + + _skip(100); + + contracts.poolProxy.repay(tokenList.usdx, amountToBorrow / 2, 2, borrower); + vm.snapshotGasLastCall('Pool.Operations', 'repay: partial repay'); + + _skip(100); + + contracts.poolProxy.repay(tokenList.usdx, type(uint256).max, 2, borrower); + vm.snapshotGasLastCall('Pool.Operations', 'repay: full repay'); + } + + function test_liquidationCall_partial() external { + uint256 price = contracts.aaveOracle.getAssetPrice(tokenList.weth); + _supplyOnReserve(borrower, (((price * 1e6) / 1e8) * 90) / 100, tokenList.usdx); + _borrowArbitraryAmount(borrower, 1 ether, tokenList.weth); + deal(tokenList.weth, liquidator, 0.5 ether); + vm.startPrank(liquidator); + IERC20(tokenList.weth).approve(report.poolProxy, 0.5 ether); + + _skip(100); + + contracts.poolProxy.liquidationCall(tokenList.usdx, tokenList.weth, borrower, 0.5 ether, false); + vm.snapshotGasLastCall('Pool.Operations', 'liquidationCall: partial liquidation'); + } + + function test_liquidationCall_full() external { + uint256 price = contracts.aaveOracle.getAssetPrice(tokenList.weth); + _supplyOnReserve(borrower, (((price * 1e6) / 1e8) * 90) / 100, tokenList.usdx); + _borrowArbitraryAmount(borrower, 1 ether, tokenList.weth); + deal(tokenList.weth, liquidator, 2 ether); + vm.startPrank(liquidator); + IERC20(tokenList.weth).approve(report.poolProxy, type(uint256).max); + + _skip(100); + + contracts.poolProxy.liquidationCall( + tokenList.usdx, + tokenList.weth, + borrower, + type(uint256).max, + false + ); + vm.snapshotGasLastCall('Pool.Operations', 'liquidationCall: full liquidation'); + } + + function test_liquidationCall_deficit() external { + uint256 price = contracts.aaveOracle.getAssetPrice(tokenList.weth); + _supplyOnReserve(borrower, (price * 1e6) / 1e8, tokenList.usdx); + _borrowArbitraryAmount(borrower, 1 ether, tokenList.weth); + deal(tokenList.weth, liquidator, 2 ether); + vm.startPrank(liquidator); + IERC20(tokenList.weth).approve(report.poolProxy, type(uint256).max); + + _skip(100); + + contracts.poolProxy.liquidationCall( + tokenList.usdx, + tokenList.weth, + borrower, + type(uint256).max, + false + ); + vm.snapshotGasLastCall('Pool.Operations', 'liquidationCall: deficit on liquidated asset'); + } + + function test_liquidationCall_deficitInAdditionalReserve() external { + uint256 price = contracts.aaveOracle.getAssetPrice(tokenList.weth); + _supplyOnReserve(borrower, (price * 1e6) / 1e8, tokenList.usdx); + _borrowArbitraryAmount(borrower, 1e5, tokenList.wbtc); // additional deficit + _borrowArbitraryAmount(borrower, 1 ether, tokenList.weth); + deal(tokenList.weth, liquidator, 2 ether); + vm.startPrank(liquidator); + IERC20(tokenList.weth).approve(report.poolProxy, type(uint256).max); + + _skip(100); + + contracts.poolProxy.liquidationCall( + tokenList.usdx, + tokenList.weth, + borrower, + type(uint256).max, + false + ); + vm.snapshotGasLastCall( + 'Pool.Operations', + 'liquidationCall: deficit on liquidated asset + other asset' + ); + } +} diff --git a/tests/gas/Pool.Setters.gas.t.sol b/tests/gas/Pool.Setters.gas.t.sol new file mode 100644 index 00000000..7a89eab3 --- /dev/null +++ b/tests/gas/Pool.Setters.gas.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import {Errors} from '../../src/contracts/protocol/libraries/helpers/Errors.sol'; +import {UserConfiguration} from '../../src/contracts/protocol/libraries/configuration/UserConfiguration.sol'; +import {Testhelpers, IERC20} from './Testhelpers.sol'; + +/** + * Scenario suite for pool setter operations. + */ +contract PoolSetters_gas_Tests is Testhelpers { + // mock users to supply and borrow liquidity + address user = makeAddr('user'); + + function test_setUserEMode() external { + vm.startPrank(poolAdmin); + EModeCategoryInput memory ct1 = _genCategoryOne(); + contracts.poolConfiguratorProxy.setEModeCategory(ct1.id, ct1.ltv, ct1.lt, ct1.lb, ct1.label); + contracts.poolConfiguratorProxy.setAssetCollateralInEMode(tokenList.usdx, ct1.id, true); + contracts.poolConfiguratorProxy.setAssetBorrowableInEMode(tokenList.weth, ct1.id, true); + vm.stopPrank(); + _supplyOnReserve(address(this), 0.5 ether, tokenList.weth); + + _supplyOnReserve(user, 5000e6, tokenList.usdx); + vm.startPrank(user); + contracts.poolProxy.borrow(tokenList.weth, 0.5 ether, 2, 0, user); + + _skip(100); + + contracts.poolProxy.setUserEMode(1); + vm.snapshotGasLastCall('Pool.Setters', 'setUserEMode: enter eMode, 1 borrow, 1 supply'); + + _skip(100); + + contracts.poolProxy.setUserEMode(0); + vm.snapshotGasLastCall('Pool.Setters', 'setUserEMode: leave eMode, 1 borrow, 1 supply'); + } + + function test_setUserUseReserveAsCollateral() external { + _supplyOnReserve(address(this), 5000e6, tokenList.usdx); + + contracts.poolProxy.setUserUseReserveAsCollateral(tokenList.usdx, false); + vm.snapshotGasLastCall( + 'Pool.Setters', + 'setUserUseReserveAsCollateral: disableCollateral, 1 supply' + ); + + contracts.poolProxy.setUserUseReserveAsCollateral(tokenList.usdx, true); + vm.snapshotGasLastCall( + 'Pool.Setters', + 'setUserUseReserveAsCollateral: enableCollateral, 1 supply' + ); + } +} diff --git a/tests/gas/StataToken.gas.t.sol b/tests/gas/StataToken.gas.t.sol new file mode 100644 index 00000000..eb997bc5 --- /dev/null +++ b/tests/gas/StataToken.gas.t.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import {Errors} from '../../src/contracts/protocol/libraries/helpers/Errors.sol'; +import {UserConfiguration} from '../../src/contracts/protocol/libraries/configuration/UserConfiguration.sol'; +import {Testhelpers, IERC20} from './Testhelpers.sol'; +import {StataTokenFactory} from '../../src/contracts/extensions/stata-token/StataTokenFactory.sol'; +import {StataTokenV2} from '../../src/contracts/extensions/stata-token/StataTokenV2.sol'; +import {DataTypes} from '../../src/contracts/protocol/libraries/types/DataTypes.sol'; + +/** + * Scenario suite for statatoken operations. + */ +contract StataToken_gas_Tests is Testhelpers { + StataTokenV2 public stataToken; + + function setUp() public override { + super.setUp(); + StataTokenFactory(report.staticATokenFactoryProxy).createStataTokens( + contracts.poolProxy.getReservesList() + ); + stataToken = StataTokenV2( + StataTokenFactory(report.staticATokenFactoryProxy).getStataToken(tokenList.usdx) + ); + } + + function test_deposit() external { + uint256 amountToDeposit = 1000e8; + deal(tokenList.usdx, address(this), amountToDeposit); + IERC20(tokenList.usdx).approve(address(stataToken), amountToDeposit); + + uint256 shares = stataToken.deposit(amountToDeposit, address(this)); + vm.snapshotGasLastCall('StataTokenV2', 'deposit'); + + stataToken.redeem(shares, address(this), address(this)); + vm.snapshotGasLastCall('StataTokenV2', 'redeem'); + } + + function test_depositATokens() external { + uint256 amountToDeposit = 1000e8; + _supplyOnReserve(address(this), amountToDeposit, tokenList.usdx); + DataTypes.ReserveDataLegacy memory reserveData = contracts.poolProxy.getReserveData( + tokenList.usdx + ); + IERC20(reserveData.aTokenAddress).approve(address(stataToken), amountToDeposit); + + uint256 shares = stataToken.depositATokens(amountToDeposit, address(this)); + vm.snapshotGasLastCall('StataTokenV2', 'depositATokens'); + + stataToken.redeemATokens(shares, address(this), address(this)); + vm.snapshotGasLastCall('StataTokenV2', 'redeemAToken'); + } +} diff --git a/tests/gas/Testhelpers.sol b/tests/gas/Testhelpers.sol new file mode 100644 index 00000000..32ac2d58 --- /dev/null +++ b/tests/gas/Testhelpers.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {IERC20} from 'openzeppelin-contracts/contracts/interfaces/IERC20.sol'; +import {IPriceOracleGetter} from '../../src/contracts/interfaces/IPriceOracleGetter.sol'; +import {TestnetProcedures} from '../utils/TestnetProcedures.sol'; + +contract Testhelpers is TestnetProcedures { + address rando = makeAddr('randomUser'); + + function setUp() public virtual { + initTestEnvironment(false); + + // supply and borrow some on reserve with a random user as "some" interest accrual + // is the realisitc usecase we want to check in gas snapshots + _supplyOnReserve(rando, 100 ether, tokenList.weth); + _supplyOnReserve(rando, 1_000_000e6, tokenList.usdx); + _supplyOnReserve(rando, 100e8, tokenList.wbtc); + vm.startPrank(rando); + contracts.poolProxy.borrow(tokenList.weth, 1 ether, 2, 0, rando); + contracts.poolProxy.borrow(tokenList.usdx, 1000e6, 2, 0, rando); + contracts.poolProxy.borrow(tokenList.wbtc, 1e8, 2, 0, rando); + vm.stopPrank(); + _skip(100); // skip some blocks to allow interest to accrue & the block to be not cached + } + + /** + * Supplies the specified amount of asset to the reserve. + */ + function _supplyOnReserve(address user, uint256 amount, address asset) internal { + vm.startPrank(user); + deal(asset, user, amount); + IERC20(asset).approve(report.poolProxy, amount); + contracts.poolProxy.supply(asset, amount, user, 0); + vm.stopPrank(); + } + + // assumes that the caller has at least one unit of collateralAsset that is not the borrowAsset + function _borrowArbitraryAmount(address borrower, uint256 amount, address asset) internal { + // set the oracle price of the borrow asset to 0 + vm.mockCall( + address(contracts.aaveOracle), + abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, address(asset)), + abi.encode(0) + ); + // borrow the full emount of the asset + vm.prank(borrower); + contracts.poolProxy.borrow(asset, amount, 2, 0, borrower); + // revert the oracle price + vm.clearMockedCalls(); + } + + /** + * Skips the specified amount of blocks and adjusts the time accordingly. + * Using vm. methods for --via-ir compat. + */ + function _skip(uint256 amount) internal { + vm.warp(vm.getBlockTimestamp() + amount * 12); + vm.roll(vm.getBlockNumber() + amount); + } +}