Skip to content

Commit

Permalink
Merge pull request #10 from charlesndalton/fix-chainlink-decimals
Browse files Browse the repository at this point in the history
fix: get chainlink to work with mismatched decimals
  • Loading branch information
charlesndalton committed Jul 26, 2023
2 parents 6d0f931 + 9ca6658 commit 357a6f6
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 54 deletions.
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

0 comments on commit 357a6f6

Please sign in to comment.