|
| 1 | +pragma solidity 0.6.6; |
| 2 | + |
| 3 | +import "./IKyberSanity.sol"; |
| 4 | +import "./utils/Utils5.sol"; |
| 5 | +import "./utils/Withdrawable3.sol"; |
| 6 | + |
| 7 | +/** |
| 8 | + * @title SanityRatesGasPrice contract |
| 9 | + * The contract provides the following functionality: |
| 10 | + * - setting reasonable diff |
| 11 | + * - setting max gas price criteria for a trade |
| 12 | + * - setting sanity rates |
| 13 | + * - getting sanity rates |
| 14 | + * |
| 15 | + * This allows FPR managers to protect their price updates from |
| 16 | + * front runners by setting the maxGasPriceWei param. But it mainly |
| 17 | + * protects reserves from (1) bugs in the conversion rate logic or |
| 18 | + * from (2) hacks into the conversion rate system. If there are large |
| 19 | + * inconsistencies between the sanity rates and the actual rates, |
| 20 | + * then trades involving the reserve will be disabled. |
| 21 | + */ |
| 22 | + |
| 23 | +contract SanityRatesGasPrice is IKyberSanity, Withdrawable3, Utils5 { |
| 24 | + struct SanityData { |
| 25 | + uint128 tokenRate; |
| 26 | + uint128 reasonableDiffInBps; |
| 27 | + } |
| 28 | + |
| 29 | + mapping(address => SanityData) public sanityData; |
| 30 | + uint256 public maxGasPriceWei; |
| 31 | + |
| 32 | + event SanityMaxGasPriceSet(uint256 maxGasPrice); |
| 33 | + |
| 34 | + constructor(address _admin, uint256 _maxGasPriceWei) public Withdrawable3(_admin) { |
| 35 | + setGasPrice(_maxGasPriceWei); |
| 36 | + } |
| 37 | + |
| 38 | + /// @dev set reasonableDiffInBps of a token to MAX_RATE to avoid handling the |
| 39 | + /// price feed for this token |
| 40 | + function setReasonableDiff(IERC20[] calldata srcs, uint256[] calldata diff) |
| 41 | + external |
| 42 | + onlyAdmin |
| 43 | + { |
| 44 | + require(srcs.length == diff.length, "srcs,diff length mismatch"); |
| 45 | + for (uint256 i = 0; i < srcs.length; i++) { |
| 46 | + require( |
| 47 | + diff[i] <= BPS || diff[i] == MAX_RATE, |
| 48 | + "Diff must be <= 10000 BPS or == MAX_RATE" |
| 49 | + ); |
| 50 | + sanityData[address(srcs[i])].reasonableDiffInBps = uint128(diff[i]); |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + function setMaxGasPriceWei(uint256 _maxGasPriceWei) external onlyOperator { |
| 55 | + setGasPrice(_maxGasPriceWei); |
| 56 | + emit SanityMaxGasPriceSet(maxGasPriceWei); |
| 57 | + } |
| 58 | + |
| 59 | + function setSanityRates(IERC20[] calldata srcs, uint256[] calldata rates) |
| 60 | + external |
| 61 | + onlyOperator |
| 62 | + { |
| 63 | + require(srcs.length == rates.length, "srcs,rates length mismatch"); |
| 64 | + |
| 65 | + for (uint256 i = 0; i < srcs.length; i++) { |
| 66 | + require(rates[i] > 0 && rates[i] <= MAX_RATE, "rate must be > 0 and <= MAX_RATE"); |
| 67 | + sanityData[address(srcs[i])].tokenRate = uint128(rates[i]); |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + function getSanityRate(IERC20 src, IERC20 dest) external override view returns (uint256 rate) { |
| 72 | + SanityData memory data; |
| 73 | + |
| 74 | + if (src != ETH_TOKEN_ADDRESS && dest != ETH_TOKEN_ADDRESS) return 0; |
| 75 | + if (tx.gasprice > maxGasPriceWei) return 0; |
| 76 | + |
| 77 | + uint128 reasonableDiffInBps; |
| 78 | + if (src == ETH_TOKEN_ADDRESS) { |
| 79 | + data = sanityData[address(dest)]; |
| 80 | + reasonableDiffInBps = data.reasonableDiffInBps; |
| 81 | + rate = data.tokenRate > 0 ? (PRECISION * PRECISION) / data.tokenRate : 0; |
| 82 | + } else { |
| 83 | + data = sanityData[address(src)]; |
| 84 | + reasonableDiffInBps = data.reasonableDiffInBps; |
| 85 | + rate = data.tokenRate; |
| 86 | + } |
| 87 | + |
| 88 | + if (reasonableDiffInBps == MAX_RATE) return MAX_RATE; |
| 89 | + |
| 90 | + return (rate * (BPS + data.reasonableDiffInBps)) / BPS; |
| 91 | + } |
| 92 | + |
| 93 | + function setGasPrice(uint256 _maxGasPriceWei) internal { |
| 94 | + require(_maxGasPriceWei > 0, "maxGasPriceWei must be > 0"); |
| 95 | + maxGasPriceWei = _maxGasPriceWei; |
| 96 | + } |
| 97 | +} |
0 commit comments