Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: get chainlink to work with mismatched decimals #10

Merged
merged 4 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions contracts/periphery/GasChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ pragma solidity ^0.7.6;
pragma abicoder v2;

interface EIP1271 {
function isValidSignature(bytes32, bytes calldata) external returns (bytes4);
function isValidSignature(bytes32, bytes calldata)
external
returns (bytes4);
}

/// Check that `isValidSignature` doesn't take up too much gas
contract GasChecker {
function isValidSignatureCheck(address milkman, bytes32 orderDigest, bytes calldata encodedOrder)
external
returns (bytes4)
{
function isValidSignatureCheck(
address milkman,
bytes32 orderDigest,
bytes calldata encodedOrder
) external returns (bytes4) {
return EIP1271(milkman).isValidSignature(orderDigest, encodedOrder);
}
}
33 changes: 31 additions & 2 deletions contracts/pricecheckers/ChainlinkExpectedOutCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ interface IPriceFeed {
function decimals() external view returns (uint8);
}

interface IERC20MetaData {
function decimals() external view returns (uint8);
}

/**
* @notice Checks a swap against Chainlink-compatible price feeds.
* @dev Doesn't care about how long ago the price feed answer was. Another expected out calculator can be built if this is desired.
Expand Down Expand Up @@ -47,13 +51,22 @@ contract ChainlinkExpectedOutCalculator is IExpectedOutCalculator {
(address[], bool[])
);

return getExpectedOutFromChainlink(_priceFeeds, _reverses, _amountIn); // how much Chainlink says we'd get out of this trade
return
getExpectedOutFromChainlink(
_priceFeeds,
_reverses,
_amountIn,
_fromToken,
_toToken
); // how much Chainlink says we'd get out of this trade
}

function getExpectedOutFromChainlink(
address[] memory _priceFeeds,
bool[] memory _reverses,
uint256 _amountIn
uint256 _amountIn,
address _fromToken,
address _toToken
) internal view returns (uint256 _expectedOutFromChainlink) {
uint256 _priceFeedsLen = _priceFeeds.length;

Expand Down Expand Up @@ -85,5 +98,21 @@ contract ChainlinkExpectedOutCalculator is IExpectedOutCalculator {
_scaleAnswerBy
);
}

uint256 _fromTokenDecimals = uint256(
IERC20MetaData(_fromToken).decimals()
);
uint256 _toTokenDecimals = uint256(IERC20MetaData(_toToken).decimals());

if (_fromTokenDecimals > _toTokenDecimals) {
// if fromToken has more decimals than toToken, we need to divide
_expectedOutFromChainlink = _expectedOutFromChainlink.div(
10**_fromTokenDecimals.sub(_toTokenDecimals)
);
} else if (_fromTokenDecimals < _toTokenDecimals) {
_expectedOutFromChainlink = _expectedOutFromChainlink.mul(
10**_toTokenDecimals.sub(_fromTokenDecimals)
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ library VaultReentrancyLib {

// read-only re-entrancy protection - this call is always unsuccessful but we need to make sure
// it didn't fail due to a re-entrancy attack
(, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(
(, bytes memory revertData) = address(vault).staticcall{gas: 10_000}(
abi.encodeWithSelector(
vault.manageUserBalance.selector,
new address[](0)
Expand Down
3 changes: 2 additions & 1 deletion milkman_py/tests/test_milkman.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import milkman_py


def test_simple():
print(milkman_py.curve_expected_out_data())
assert 1 + 2 == 4
assert 1 + 2 == 4
124 changes: 85 additions & 39 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
from lib2to3.pgen2 import token
from brownie import Contract
from eth_abi import encode_abi
#from milkman_py import (

# from milkman_py import (
# univ2_expected_out_data,
# univ3_expected_out_data,
# curve_expected_out_data,
# chainlink_expected_out_data,
# meta_expected_out_data,
# fixed_slippage_price_checker_data,
# dynamic_slippage_price_checker_data,
#)
# )
import pytest
import utils

EMPTY_BYTES = encode_abi(["uint8"], [int(0)])


def univ2_expected_out_data():
return EMPTY_BYTES

Expand Down Expand Up @@ -78,6 +81,8 @@ def deployer(accounts):
# GUSD -> USDC, $1k, Curve price checker
# AAVE -> WETH, $250k, Chainlink price checker
# BAT -> ALCX, $100k, Chainlink price checker
# YFI -> USDC, $20k, Chainlink price checker
# USDT -> UNI, $2M, Chainlink price checker
# WETH -> WETH/BAL, $650k SSB price checker
# UNI -> USDT, $500k & Uniswap as the price checker
# ALCX -> TOKE, $100k, Meta price checker with Chainlink and Sushiswap
Expand All @@ -98,6 +103,8 @@ def deployer(accounts):
"ALCX": "0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF",
"BAL": "0xba100000625a3754423978a60c9317c58a424e3D",
"BAL/WETH": "0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56",
"YFI": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
"USDT": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"COW": "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB",
}

Expand All @@ -111,6 +118,8 @@ def deployer(accounts):
"UNI": "USDT",
"ALCX": "TOKE",
"BAL": "BAL/WETH",
"YFI": "USDC",
"USDT": "UNI",
"COW": "DAI",
}

Expand All @@ -126,7 +135,9 @@ def deployer(accounts):
"UNI",
"ALCX",
"BAL",
"COW"
"YFI",
"USDT",
"COW",
],
scope="session",
autouse=True,
Expand All @@ -150,6 +161,8 @@ def token_to_buy(token_to_sell):
"UNI": 80_000,
"ALCX": 4_000,
"BAL": 300_000,
"YFI": 3,
"USDT": 2_000_000,
"COW": 900_000,
}

Expand Down Expand Up @@ -177,6 +190,8 @@ def amount(token_to_sell, user, whale):
"UNI": "0x1a9C8182C09F50C8318d769245beA52c32BE35BC",
"ALCX": "0x000000000000000000000000000000000000dEaD",
"BAL": "0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f",
"YFI": "0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52",
"USDT": "0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf",
"COW": "0xca771eda0c70aa7d053ab1b25004559b918fe662",
}

Expand Down Expand Up @@ -204,7 +219,7 @@ def price_checker(
yield sushiswap_price_checker
if symbol == "USDC" or symbol == "GUSD":
yield curve_price_checker
if symbol == "AAVE" or symbol == "BAT":
if symbol == "AAVE" or symbol == "BAT" or symbol == "YFI" or symbol == "USDT":
yield chainlink_price_checker
if symbol == "UNI":
yield univ3_price_checker
Expand Down Expand Up @@ -233,10 +248,14 @@ def sushiswap_expected_out_calculator(UniV2ExpectedOutCalculator, deployer):
UniV2ExpectedOutCalculator, "SUSHI_EXPECTED_OUT_CALCULATOR", sushi_router
)


@pytest.fixture
def ssb_bal_weth_expected_out_calculator(SingleSidedBalancerBalWethExpectedOutCalculator, deployer):
def ssb_bal_weth_expected_out_calculator(
SingleSidedBalancerBalWethExpectedOutCalculator, deployer
):
yield deployer.deploy(SingleSidedBalancerBalWethExpectedOutCalculator)


@pytest.fixture
def univ3_expected_out_calculator(UniV3ExpectedOutCalculator, deployer):
yield deployer.deploy(UniV3ExpectedOutCalculator)
Expand Down Expand Up @@ -300,21 +319,26 @@ def meta_price_checker(DynamicSlippageChecker, meta_expected_out_calculator, dep
meta_expected_out_calculator,
)


@pytest.fixture
def ssb_bal_weth_price_checker(DynamicSlippageChecker, ssb_bal_weth_expected_out_calculator, deployer):
def ssb_bal_weth_price_checker(
DynamicSlippageChecker, ssb_bal_weth_expected_out_calculator, deployer
):
yield deployer.deploy(
DynamicSlippageChecker,
"SSB_BAL_WETH_DYNAMIC_SLIPPAGE_PRICE_CHECKER",
ssb_bal_weth_expected_out_calculator
ssb_bal_weth_expected_out_calculator,
)


@pytest.fixture
def valid_from_price_checker_decorator(ValidFromPriceCheckerDecorator, deployer):
yield deployer.deploy(
ValidFromPriceCheckerDecorator
)
yield deployer.deploy(ValidFromPriceCheckerDecorator)


def valid_from_price_checker_decorator_data(valid_from, price_checker, price_checker_data):
def valid_from_price_checker_decorator_data(
valid_from, price_checker, price_checker_data
):
return encode_abi(
["uint256", "address", "bytes"],
[
Expand All @@ -341,6 +365,22 @@ def fixed_min_out_price_checker_data(min_out):
"TOKE": fixed_slippage_price_checker_data(univ2_expected_out_data()),
"USDC": dynamic_slippage_price_checker_data(400, curve_expected_out_data()),
"GUSD": dynamic_slippage_price_checker_data(500, curve_expected_out_data()),
"YFI": dynamic_slippage_price_checker_data(
100,
chainlink_expected_out_data(
["0xA027702dbb89fbd58938e4324ac03B58d812b0E1"], [False]
),
),
"USDT": dynamic_slippage_price_checker_data(
500,
chainlink_expected_out_data(
[
"0xee9f2375b4bdf6387aa8265dd4fb8f16512a1d46",
"0xd6aa3d25116d8da79ea0246c4826eb951872e02e",
],
[False, True],
),
),
"AAVE": dynamic_slippage_price_checker_data(
400,
chainlink_expected_out_data(
Expand All @@ -357,10 +397,7 @@ def fixed_min_out_price_checker_data(min_out):
[False, True],
),
), # BAT/ETH & ALCX/ETH feeds, allow 20% slippage since these are relatively illiquid
"WETH": dynamic_slippage_price_checker_data(
200,
utils.EMPTY_BYTES
),
"WETH": dynamic_slippage_price_checker_data(200, utils.EMPTY_BYTES),
"UNI": dynamic_slippage_price_checker_data(
600,
univ3_expected_out_data(
Expand All @@ -380,33 +417,41 @@ def fixed_min_out_price_checker_data(min_out):

@pytest.fixture
def price_checker_data(
chain, token_to_sell, meta_price_checker, chainlink_expected_out_calculator, sushiswap_expected_out_calculator
chain,
token_to_sell,
meta_price_checker,
chainlink_expected_out_calculator,
sushiswap_expected_out_calculator,
):
if token_to_sell.symbol() == "ALCX":
yield valid_from_price_checker_decorator_data(chain.time(), meta_price_checker.address, dynamic_slippage_price_checker_data(
1000,
meta_expected_out_data(
[
token_address["ALCX"],
token_address["WETH"],
token_address["TOKE"],
],
[
chainlink_expected_out_calculator.address,
sushiswap_expected_out_calculator.address,
],
[
encode_abi(
["address[]", "bool[]"],
[
["0x194a9aaf2e0b67c35915cd01101585a33fe25caa"],
[False],
], # forgive me father for this much nesting
),
utils.EMPTY_BYTES,
],
yield valid_from_price_checker_decorator_data(
chain.time(),
meta_price_checker.address,
dynamic_slippage_price_checker_data(
1000,
meta_expected_out_data(
[
token_address["ALCX"],
token_address["WETH"],
token_address["TOKE"],
],
[
chainlink_expected_out_calculator.address,
sushiswap_expected_out_calculator.address,
],
[
encode_abi(
["address[]", "bool[]"],
[
["0x194a9aaf2e0b67c35915cd01101585a33fe25caa"],
[False],
], # forgive me father for this much nesting
),
utils.EMPTY_BYTES,
],
),
),
))
)
else:
yield price_checker_datas[token_to_sell.symbol()]

Expand All @@ -417,6 +462,7 @@ def milkman(Milkman, deployer):

yield milkman


@pytest.fixture
def gas_checker(GasChecker, deployer):
gas_checker = deployer.deploy(GasChecker)
Expand Down
Loading
Loading