Skip to content

Commit

Permalink
feat: add foundry test for bitmaps
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Aug 31, 2024
1 parent b74526a commit 7bead94
Show file tree
Hide file tree
Showing 10 changed files with 1,317 additions and 2 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ deployments/
**.last_conf*
certora-logs
certora_debug_log.txt
resource_errors.json
resource_errors.json

out
lcov.info
.foundry
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
10 changes: 10 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[profile.default]
src = 'src'
out = 'out'
libs = ['lib']
fuzz_runs = 10000
optimizer = true

solc = "0.8.10"

# See more config options https://github.com/gakonst/foundry/tree/master/config
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at 1714be
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"node": ">=16.0.0"
},
"scripts": {
"setup:foundry": "foundryup && forge install",
"install:foundry": "curl -L https://foundry.paradigm.xyz | bash",
"size": "npm run compile && npm run hardhat size-contracts",
"run-env": "npm i && tail -f /dev/null",
"hardhat": "hardhat",
Expand All @@ -23,6 +25,7 @@
"prettier:write": "prettier -w .",
"coverage": ". ./setup-test-env.sh && COVERAGE=true npx hardhat coverage --temp temp-artifacts --testfiles test-suites/emptyrun.coverage.ts && rm -rf coverage.json coverage/ && COVERAGE=true npx hardhat coverage --temp temp-artifacts --testfiles 'test-suites/*.spec.ts'",
"test": ". ./setup-test-env.sh && TS_NODE_TRANSPILE_ONLY=1 hardhat test test-suites/*.spec.ts",
"test-fuzz": "forge test",
"test-scenarios": ". ./setup-test-env.sh && npx hardhat test test-suites/__setup.spec.ts test-suites/scenario.spec.ts",
"test-l2pool": ". ./setup-test-env.sh && npx hardhat test test-suites/__setup.spec.ts test-suites/pool-l2.spec.ts",
"test-subgraph:scenarios": ". ./setup-test-env.sh && hardhat --network hardhatevm_docker test test-suites/__setup.spec.ts test-suites/subgraph-scenarios.spec.ts",
Expand Down Expand Up @@ -88,4 +91,4 @@
"lint-staged": {
"*.{ts,js,md,sol}": "prettier --write"
}
}
}
293 changes: 293 additions & 0 deletions src/test/CalldataLogic.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.10;

import {DataTypes} from './../../contracts/protocol/libraries/types/DataTypes.sol';
import {ReserveConfiguration} from './../../contracts/protocol/libraries/configuration/ReserveConfiguration.sol';

import {CalldataLogic} from './../../contracts/protocol/libraries/logic/CalldataLogic.sol';
import {L2Encoder} from './../../contracts/misc/L2Encoder.sol';

import {Test} from 'forge-std/Test.sol';

contract CalldataLogicTest is Test {
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;

mapping(uint256 => address) reservesList;

uint256 constant RESERVE_COUNT = 128;

function setUp() public {
for (uint256 i = 0; i < RESERVE_COUNT; i++) {
reservesList[i] = address(uint160(400 + i));
}
}

function testDecodeSupplyParams(uint8 assetId, uint128 amount, uint16 referralCode) public view {
vm.assume(assetId < RESERVE_COUNT);

bytes32 args;
assembly {
args := add(assetId, add(shl(16, amount), shl(144, referralCode)))
}

(address _asset, uint256 _amount, uint16 _referralCode) = CalldataLogic.decodeSupplyParams(
reservesList,
args
);

assertEq(_asset, reservesList[assetId]);
assertNotEq(_asset, address(0));
assertEq(_amount, amount);
assertEq(_referralCode, referralCode);
}

function testDecodeSupplyWithPermitParams(
uint8 assetId,
uint128 amount,
uint16 referralCode,
uint32 deadLine,
uint8 permitV
) public view {
vm.assume(assetId < RESERVE_COUNT);

bytes32 args;
assembly {
args := add(
assetId,
add(
shl(16, amount),
add(shl(144, referralCode), add(shl(160, deadLine), shl(192, permitV)))
)
)
}

(
address _asset,
uint256 _amount,
uint16 _referralCode,
uint256 _deadLine,
uint8 _permitV
) = CalldataLogic.decodeSupplyWithPermitParams(reservesList, args);

assertEq(_asset, reservesList[assetId]);
assertNotEq(_asset, address(0));
assertEq(_amount, amount);
assertEq(_referralCode, referralCode);
assertEq(_deadLine, deadLine);
assertEq(_permitV, permitV);
}

function testDecodeWithdrawParams(uint8 assetId, uint128 amount) public view {
vm.assume(assetId < RESERVE_COUNT);
bytes32 args;
assembly {
args := add(assetId, shl(16, amount))
}

(address _asset, uint256 _amount) = CalldataLogic.decodeWithdrawParams(reservesList, args);

uint256 expectedAmount = amount == type(uint128).max ? type(uint256).max : uint256(amount);

assertEq(_asset, reservesList[assetId]);
assertNotEq(_asset, address(0));
assertEq(_amount, expectedAmount);
}

function testDecodeBorrowParams(
uint8 assetId,
uint128 amount,
bool stableRateMode,
uint16 referralCode
) public view {
vm.assume(assetId < RESERVE_COUNT);
uint256 interestRateMode = stableRateMode ? 1 : 2;

bytes32 args;
assembly {
args := add(
assetId,
add(shl(16, amount), add(shl(144, interestRateMode), shl(152, referralCode)))
)
}

(
address _asset,
uint256 _amount,
uint256 _interestRateMode,
uint16 _referralCode
) = CalldataLogic.decodeBorrowParams(reservesList, args);

assertEq(_asset, reservesList[assetId]);
assertNotEq(_asset, address(0));
assertEq(_amount, amount);
assertEq(_interestRateMode, interestRateMode);
assertEq(_referralCode, referralCode);
}

function testDecodeRepayParams(uint8 assetId, uint128 amount, bool stableRateMode) public view {
vm.assume(assetId < RESERVE_COUNT);
uint256 interestRateMode = stableRateMode ? 1 : 2;
uint256 expectedAmount = amount == type(uint128).max ? type(uint256).max : amount;

bytes32 args;
assembly {
args := add(assetId, add(shl(16, amount), shl(144, interestRateMode)))
}

(address _asset, uint256 _amount, uint256 _interestRateMode) = CalldataLogic.decodeRepayParams(
reservesList,
args
);

assertEq(_asset, reservesList[assetId]);
assertNotEq(_asset, address(0));
assertEq(_amount, expectedAmount);
assertEq(_interestRateMode, interestRateMode);
}

struct DecodeRepayWithPermitHelper {
uint256 expectedAmount;
address _asset;
uint256 _amount;
uint256 _interestRateMode;
uint256 _deadline;
uint8 _permitV;
}

function testDecodeRepayWithPermitParams(
uint8 assetId,
uint128 amount,
bool stableRateMode,
uint32 deadline,
uint8 permitV
) public view {
vm.assume(assetId < RESERVE_COUNT);
DecodeRepayWithPermitHelper memory vars;

vars.expectedAmount = amount == type(uint128).max ? type(uint256).max : amount;

bytes32 args;
assembly {
args := add(
assetId,
add(
shl(16, amount),
add(shl(144, sub(2, stableRateMode)), add(shl(152, deadline), shl(184, permitV)))
)
)
}

(
vars._asset,
vars._amount,
vars._interestRateMode,
vars._deadline,
vars._permitV
) = CalldataLogic.decodeRepayWithPermitParams(reservesList, args);

assertEq(vars._asset, reservesList[assetId]);
assertNotEq(vars._asset, address(0));
assertEq(vars._amount, vars.expectedAmount);
assertEq(vars._interestRateMode, stableRateMode ? 1 : 2);
assertEq(vars._deadline, uint256(deadline));
assertEq(vars._permitV, permitV);
}

function testDecodeSwapBorrowRateModeParams(uint8 assetId, bool stableRateMode) public view {
vm.assume(assetId < RESERVE_COUNT);
uint256 interestRateMode = stableRateMode ? 1 : 2;
bytes32 args;
assembly {
args := add(assetId, shl(16, interestRateMode))
}

(address _asset, uint256 _interestRateMode) = CalldataLogic.decodeSwapBorrowRateModeParams(
reservesList,
args
);

assertEq(_asset, reservesList[assetId]);
assertNotEq(_asset, address(0));
assertEq(_interestRateMode, interestRateMode);
}

function testDecodeRebalanceStableBorrowRateParams(uint8 assetId, address user) public view {
vm.assume(assetId < RESERVE_COUNT);
bytes32 args;
assembly {
args := add(assetId, shl(16, user))
}

(address _asset, address _user) = CalldataLogic.decodeRebalanceStableBorrowRateParams(
reservesList,
args
);
assertEq(_asset, reservesList[assetId]);
assertNotEq(_asset, address(0));
assertEq(_user, user);
}

function testDecodeSetUserUseReserveAsCollateralParams(
uint8 assetId,
bool useAsCollateral
) public view {
vm.assume(assetId < RESERVE_COUNT);
bytes32 args;
assembly {
args := add(assetId, shl(16, useAsCollateral))
}

(address _asset, bool _useAsCollateral) = CalldataLogic
.decodeSetUserUseReserveAsCollateralParams(reservesList, args);

assertEq(_asset, reservesList[assetId]);
assertNotEq(_asset, address(0));
assertEq(_useAsCollateral, useAsCollateral);
}

struct DecodeLiquidationCallParamsHelper {
uint256 expectedDebtToCover;
address collateralAsset;
address debtAsset;
address user;
uint256 debtToCover;
bool receiveAToken;
}

function testDecodeLiquidationCallParams(
uint8 collateralAssetId,
uint8 debtAssetId,
address user,
uint128 debtToCover,
bool receiveAToken
) public view {
vm.assume(collateralAssetId < RESERVE_COUNT && debtAssetId < RESERVE_COUNT);
DecodeLiquidationCallParamsHelper memory vars;

bytes32 args1;
bytes32 args2;

vars.expectedDebtToCover = debtToCover == type(uint128).max ? type(uint256).max : debtToCover;

assembly {
args1 := add(add(collateralAssetId, shl(16, debtAssetId)), shl(32, user))
args2 := add(debtToCover, shl(128, receiveAToken))
}

(
vars.collateralAsset,
vars.debtAsset,
vars.user,
vars.debtToCover,
vars.receiveAToken
) = CalldataLogic.decodeLiquidationCallParams(reservesList, args1, args2);

assertEq(vars.collateralAsset, reservesList[collateralAssetId]);
assertNotEq(vars.collateralAsset, address(0));
assertEq(vars.debtAsset, reservesList[debtAssetId]);
assertNotEq(vars.debtAsset, address(0));
assertEq(vars.user, user);
assertEq(vars.debtToCover, vars.expectedDebtToCover);
assertEq(vars.receiveAToken, receiveAToken);
}
}
39 changes: 39 additions & 0 deletions src/test/PercentageMath.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.10;

import {PercentageMath} from './../../contracts/protocol/libraries/math/PercentageMath.sol';

import {Test} from 'forge-std/Test.sol';

contract PercentageMathTest is Test {
using PercentageMath for uint256;

function testPercentMul(uint256 a, uint256 b) public pure {
bool safe = b == 0 || a <= (type(uint256).max - PercentageMath.HALF_PERCENTAGE_FACTOR) / b;
vm.assume(safe);
assertEq(
a.percentMul(b),
(a * b + PercentageMath.HALF_PERCENTAGE_FACTOR) / PercentageMath.PERCENTAGE_FACTOR
);
}

function testPercentDiv(uint256 a, uint256 b) public pure {
bool safe = b != 0 && a <= (type(uint256).max - b / 2) / PercentageMath.PERCENTAGE_FACTOR;
vm.assume(safe);
assertEq(a.percentDiv(b), (a * PercentageMath.PERCENTAGE_FACTOR + b / 2) / b);
}

/// NEGATIVES ///

function testFailPercentMul(uint256 a, uint256 b) public pure {
bool safe = b == 0 || a <= (type(uint256).max - PercentageMath.HALF_PERCENTAGE_FACTOR) / b;
vm.assume(!safe);
a.percentMul(b);
}

function testFailPercentDiv(uint256 a, uint256 b) public pure {
bool safe = b != 0 && a <= (type(uint256).max - b / 2) / PercentageMath.PERCENTAGE_FACTOR;
vm.assume(!safe);
a.percentDiv(b);
}
}
Loading

0 comments on commit 7bead94

Please sign in to comment.