Skip to content

Commit

Permalink
feat: large tests using via ir
Browse files Browse the repository at this point in the history
* wip: is compiling now
* wip: math
* ci: fix github yml
* ci(nvm): upgrade node version
* lint: address issues
* ci: update action version
* test: fix route setting
* test: fix copy pasta error
  • Loading branch information
xenide authored Jun 11, 2024
1 parent 5ca6a8a commit 918a907
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 102 deletions.
22 changes: 17 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
- run: npm ci
Expand All @@ -24,18 +24,30 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- run: npm run test:unit

test-unit-large: # For large/complex tests that require via-ir compilation
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- run: npm run test:unit-large

test-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: foundry-rs/foundry-toolchain@v1
Expand All @@ -47,7 +59,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: foundry-rs/foundry-toolchain@v1
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18.17.0
v20.14.0
8 changes: 7 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[profile.default]
solc = "0.8.26"
#via_ir = true
bytecode_hash = "ipfs"
optimizer_runs = 1_000_000
libs = ['lib']
Expand All @@ -14,6 +13,13 @@ fs_permissions = [
{ access = "read", path = "./out" }
]
ignored_error_codes = []
skip = ["test/large/*.sol"]

[profile.large-test]
via_ir = true
match_path = "test/large/*.sol"
match_test = "testGetQuote_RandomizeAllParam_3HopRoute"
skip = []

[profile.integration]
match_path = "test/integration/*.sol"
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@
"submodule:reset": "git submodule update --recursive",
"test": "npm run test:unit",
"test:all": "npm run test:unit && npm run test:integration",
"test:integration": "export FOUNDRY_PROFILE=integration && forge test ",
"test:unit": "forge test"
"test:integration": "export FOUNDRY_PROFILE=integration && forge test",
"test:unit": "forge test",
"test:unit-large": "export FOUNDRY_PROFILE=large-test && forge test"
},
"devDependencies": {
"markdownlint": "0.34.0",
Expand Down
6 changes: 5 additions & 1 deletion src/ReservoirPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,11 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.
}
}

function _useFallbackOracle(uint256 aAmount, address aBase, address aQuote, bool aIsGetQuotes) internal view returns (uint256 rBidOut, uint256 rAskOut) {
function _useFallbackOracle(uint256 aAmount, address aBase, address aQuote, bool aIsGetQuotes)
internal
view
returns (uint256 rBidOut, uint256 rAskOut)
{
if (fallbackOracle == address(0)) revert OracleErrors.NoPath();

// We do not catch errors here so the fallback oracle will revert if it doesn't support the query.
Expand Down
3 changes: 2 additions & 1 deletion test/__fixtures/BaseTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ contract BaseTest is Test {
GenericFactory internal _factory = new GenericFactory();
ReservoirPair internal _pair;

ReservoirPriceOracle internal _oracle = new ReservoirPriceOracle(0.02e18, 15 minutes, 500_000, PriceType.CLAMPED_PRICE);
ReservoirPriceOracle internal _oracle =
new ReservoirPriceOracle(0.02e18, 15 minutes, 500_000, PriceType.CLAMPED_PRICE);

MintableERC20 internal _tokenA = MintableERC20(address(0x100));
MintableERC20 internal _tokenB = MintableERC20(address(0x200));
Expand Down
111 changes: 111 additions & 0 deletions test/large/ReservoirPriceOracleLarge.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import {
ReservoirPriceOracleTest,
EnumerableSetLib,
MintableERC20,
ReservoirPair,
IERC20
} from "test/unit/ReservoirPriceOracle.t.sol";

contract ReservoirPriceOracleLargeTest is ReservoirPriceOracleTest {
using EnumerableSetLib for EnumerableSetLib.AddressSet;

function testGetQuote_RandomizeAllParam_3HopRoute(
uint256 aPrice1,
uint256 aPrice2,
uint256 aPrice3,
uint256 aAmtIn,
address aTokenAAddress,
address aTokenBAddress,
address aTokenCAddress,
address aTokenDAddress,
uint8 aTokenADecimal,
uint8 aTokenBDecimal,
uint8 aTokenCDecimal,
uint8 aTokenDDecimal
) external {
// assume
vm.assume(
aTokenAAddress > ADDRESS_THRESHOLD && aTokenBAddress > ADDRESS_THRESHOLD
&& aTokenCAddress > ADDRESS_THRESHOLD && aTokenDAddress > ADDRESS_THRESHOLD
);
vm.assume(
_addressSet.add(aTokenAAddress) && _addressSet.add(aTokenBAddress) && _addressSet.add(aTokenCAddress)
&& _addressSet.add(aTokenDAddress)
);
uint256 lPrice1 = bound(aPrice1, 1e12, 1e24);
uint256 lPrice2 = bound(aPrice2, 1e12, 1e24);
uint256 lPrice3 = bound(aPrice3, 1e12, 1e24);
uint256 lAmtIn = bound(aAmtIn, 0, 1_000_000_000);
uint256 lTokenADecimal = bound(aTokenADecimal, 0, 18);
uint256 lTokenBDecimal = bound(aTokenBDecimal, 0, 18);
uint256 lTokenCDecimal = bound(aTokenCDecimal, 0, 18);
uint256 lTokenDDecimal = bound(aTokenDDecimal, 0, 18);

// arrange
MintableERC20 lTokenA = MintableERC20(aTokenAAddress);
MintableERC20 lTokenB = MintableERC20(aTokenBAddress);
MintableERC20 lTokenC = MintableERC20(aTokenCAddress);
MintableERC20 lTokenD = MintableERC20(aTokenDAddress);
deployCodeTo("MintableERC20.sol", abi.encode("T", "T", uint8(lTokenADecimal)), aTokenAAddress);
deployCodeTo("MintableERC20.sol", abi.encode("T", "T", uint8(lTokenBDecimal)), aTokenBAddress);
deployCodeTo("MintableERC20.sol", abi.encode("T", "T", uint8(lTokenCDecimal)), aTokenCAddress);
deployCodeTo("MintableERC20.sol", abi.encode("T", "T", uint8(lTokenDDecimal)), aTokenDAddress);

ReservoirPair lPair1 = ReservoirPair(_factory.createPair(IERC20(aTokenAAddress), IERC20(aTokenBAddress), 0));
ReservoirPair lPair2 = ReservoirPair(_factory.createPair(IERC20(aTokenBAddress), IERC20(aTokenCAddress), 0));
ReservoirPair lPair3 = ReservoirPair(_factory.createPair(IERC20(aTokenCAddress), IERC20(aTokenDAddress), 0));

_oracle.designatePair(aTokenAAddress, aTokenBAddress, lPair1);
_oracle.designatePair(aTokenBAddress, aTokenCAddress, lPair2);
_oracle.designatePair(aTokenCAddress, aTokenDAddress, lPair3);

address[] memory lRoute = new address[](4);
if (lTokenA < lTokenD) {
lRoute[0] = aTokenAAddress;
lRoute[1] = aTokenBAddress;
lRoute[2] = aTokenCAddress;
lRoute[3] = aTokenDAddress;
} else {
lRoute[0] = aTokenDAddress;
lRoute[1] = aTokenCAddress;
lRoute[2] = aTokenBAddress;
lRoute[3] = aTokenAAddress;
}

_oracle.setRoute(lRoute[0], lRoute[3], lRoute);
_writePriceCache(
lTokenA < lTokenB ? aTokenAAddress : aTokenBAddress,
lTokenA < lTokenB ? aTokenBAddress : aTokenAAddress,
lPrice1
);
_writePriceCache(
lTokenB < lTokenC ? aTokenBAddress : aTokenCAddress,
lTokenB < lTokenC ? aTokenCAddress : aTokenBAddress,
lPrice2
);
_writePriceCache(
lTokenC < lTokenD ? aTokenCAddress : aTokenDAddress,
lTokenC < lTokenD ? aTokenDAddress : aTokenCAddress,
lPrice3
);

// act
uint256 lAmtDOut = _oracle.getQuote(lAmtIn * 10 ** lTokenADecimal, aTokenAAddress, aTokenDAddress);

// assert
uint256 lExpectedAmtBOut = lTokenA < lTokenB
? lAmtIn * 10 ** lTokenADecimal * lPrice1 * 10 ** lTokenBDecimal / 10 ** lTokenADecimal / WAD
: lAmtIn * 10 ** lTokenADecimal * WAD * 10 ** lTokenBDecimal / lPrice1 / 10 ** lTokenADecimal;
uint256 lExpectedAmtCOut = lTokenB < lTokenC
? lExpectedAmtBOut * lPrice2 * 10 ** lTokenCDecimal / 10 ** lTokenBDecimal / WAD
: lExpectedAmtBOut * WAD * 10 ** lTokenCDecimal / lPrice2 / 10 ** lTokenBDecimal;
uint256 lExpectedAmtDOut = lTokenC < lTokenD
? lExpectedAmtCOut * lPrice3 * 10 ** lTokenDDecimal / 10 ** lTokenCDecimal / WAD
: lExpectedAmtCOut * WAD * 10 ** lTokenDDecimal / lPrice3 / 10 ** lTokenCDecimal;

assertEq(lAmtDOut, lExpectedAmtDOut);
}
}
6 changes: 1 addition & 5 deletions test/mock/MockFallbackOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@ contract MockFallbackOracle is IPriceOracle {
out = amount;
}

function getQuotes(uint256 amount, address, address)
external
pure
returns (uint256 bidOut, uint256 askOut)
{
function getQuotes(uint256 amount, address, address) external pure returns (uint256 bidOut, uint256 askOut) {
(bidOut, askOut) = (amount, amount);
}
}
4 changes: 2 additions & 2 deletions test/mock/StubERC4626.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ pragma solidity ^0.8.0;
contract StubERC4626 {
address public asset;
uint256 private rate;
string revertMsg = "oops";
bool doRevert;
string public revertMsg = "oops";
bool public doRevert;

constructor(address _asset, uint256 _rate) {
asset = _asset;
Expand Down
85 changes: 1 addition & 84 deletions test/unit/ReservoirPriceOracle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ contract ReservoirPriceOracleTest is BaseTest {
event RewardGasAmount(uint256 newAmount);
event Route(address token0, address token1, address[] route);

uint256 private constant WAD = 1e18;
uint256 internal constant WAD = 1e18;

address internal constant ADDRESS_THRESHOLD = address(0x1000);

Expand Down Expand Up @@ -356,89 +356,6 @@ contract ReservoirPriceOracleTest is BaseTest {
assertEq(lAmtCOut, lExpectedAmtCOut);
}

// function testGetQuote_RandomizeAllParam_3HopRoute(
// uint256 aPrice1,
// uint256 aPrice2,
// uint256 aPrice3,
// uint256 aAmtIn,
// address aTokenAAddress,
// address aTokenBAddress,
// address aTokenCAddress,
// address aTokenDAddress,
// uint8 aTokenADecimal,
// uint8 aTokenBDecimal,
// uint8 aTokenCDecimal,
// uint8 aTokenDDecimal
// ) external {
// // assume
// vm.assume(
// aTokenAAddress > ADDRESS_THRESHOLD && aTokenBAddress > ADDRESS_THRESHOLD
// && aTokenCAddress > ADDRESS_THRESHOLD && aTokenDAddress > ADDRESS_THRESHOLD
// );
// vm.assume(
// _addressSet.add(aTokenAAddress) && _addressSet.add(aTokenBAddress) && _addressSet.add(aTokenCAddress)
// && _addressSet.add(aTokenDAddress)
// );
// uint256 lPrice1 = bound(aPrice1, 1e12, 1e24); // need to bound price within this range as a price below this will go to zero as during the mul and div of prices
// uint256 lPrice2 = bound(aPrice2, 1e12, 1e24);
// uint256 lPrice3 = bound(aPrice3, 1e12, 1e24);
// uint256 lAmtIn = bound(aAmtIn, 0, 1_000_000_000);
// uint256 lTokenADecimal = bound(aTokenADecimal, 0, 18);
// uint256 lTokenBDecimal = bound(aTokenBDecimal, 0, 18);
// uint256 lTokenCDecimal = bound(aTokenCDecimal, 0, 18);
// uint256 lTokenDDecimal = bound(aTokenDDecimal, 0, 18);
//
// // arrange
// MintableERC20 lTokenA = MintableERC20(aTokenAAddress);
// MintableERC20 lTokenB = MintableERC20(aTokenBAddress);
// MintableERC20 lTokenC = MintableERC20(aTokenCAddress);
// MintableERC20 lTokenD = MintableERC20(aTokenDAddress);
// deployCodeTo("MintableERC20.sol", abi.encode("T", "T", uint8(lTokenADecimal)), address(lTokenA));
// deployCodeTo("MintableERC20.sol", abi.encode("T", "T", uint8(lTokenBDecimal)), address(lTokenB));
// deployCodeTo("MintableERC20.sol", abi.encode("T", "T", uint8(lTokenCDecimal)), address(lTokenC));
// deployCodeTo("MintableERC20.sol", abi.encode("T", "T", uint8(lTokenDDecimal)), address(lTokenD));
//
// ReservoirPair lPair1 = ReservoirPair(_factory.createPair(IERC20(address(lTokenA)), IERC20(address(lTokenB)), 0));
// ReservoirPair lPair2 = ReservoirPair(_factory.createPair(IERC20(address(lTokenB)), IERC20(address(lTokenC)), 0));
// ReservoirPair lPair3 = ReservoirPair(_factory.createPair(IERC20(address(lTokenC)), IERC20(address(lTokenD)), 0));
//
// _oracle.designatePair(address(lTokenA), address(lTokenB), lPair1);
// _oracle.designatePair(address(lTokenB), address(lTokenC), lPair2);
// _oracle.designatePair(address(lTokenC), address(lTokenD), lPair3);
//
// address[] memory lRoute = new address[](4);
// (lRoute[0], lRoute[3]) =
// lTokenA < lTokenD ? (address(lTokenA), address(lTokenD)) : (address(lTokenD), address(lTokenA));
// lRoute[1] = address(lTokenB);
// lRoute[2] = address(lTokenC);
//
// _oracle.setRoute(lRoute[0], lRoute[3], lRoute);
// _writePriceCache(
// lRoute[0] < lRoute[1] ? lRoute[0] : lRoute[1], lRoute[0] < lRoute[1] ? lRoute[1] : lRoute[0], lPrice1
// );
// _writePriceCache(
// address(lTokenB) < address(lTokenC) ? address(lTokenB) : address(lTokenC),
// address(lTokenB) < address(lTokenC) ? address(lTokenC) : address(lTokenB),
// lPrice2
// );
// _writePriceCache(
// lRoute[2] < lRoute[3] ? lRoute[2] : lRoute[3], lRoute[2] < lRoute[3] ? lRoute[3] : lRoute[2], lPrice3
// );
//
// // act
// uint256 lAmtDOut = _oracle.getQuote(lAmtIn * 10 ** lTokenADecimal, address(lTokenA), address(lTokenD));
//
// // assert
// uint256 lPriceStartEnd = (lRoute[0] < lRoute[1] ? lPrice1 : lPrice1.invertWad())
// * (lRoute[1] < lRoute[2] ? lPrice2 : lPrice2.invertWad()) / WAD
// * (lRoute[2] < lRoute[3] ? lPrice3 : lPrice3.invertWad()) / WAD;
// assertEq(
// lAmtDOut,
// lAmtIn * (lRoute[0] == address(lTokenA) ? lPriceStartEnd : lPriceStartEnd.invertWad())
// * (10 ** lTokenDDecimal) / WAD
// );
// }

function testGetQuotes(uint256 aPrice, uint256 aAmountIn) external {
// assume
uint256 lPrice = bound(aPrice, 1, 1e36);
Expand Down

0 comments on commit 918a907

Please sign in to comment.