Skip to content

Commit ac3829b

Browse files
refactor sanity rate contract, update to solidity 6. Add maxGasPrice setting
* Added maxGasPrice setting to SanityRates, and updated to solidity 6 * Removed buidler console.log in MockReserveSanity.sol * Added interface for sanity rates IKyberSanity, and added maxGasPrice to ctor * 100% coverage and added contract comments * Modified test to use existing MockReserve.sol, and added more tests Co-authored-by: Ilan Doron <[email protected]>
1 parent f868709 commit ac3829b

File tree

4 files changed

+486
-0
lines changed

4 files changed

+486
-0
lines changed

contracts/sol6/IKyberSanity.sol

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pragma solidity 0.6.6;
2+
3+
import "./IERC20.sol";
4+
5+
interface IKyberSanity {
6+
function getSanityRate(IERC20 src, IERC20 dest) external view returns (uint256);
7+
}
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
}

contracts/sol6/mock/MockReserve.sol

+14
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
pragma solidity 0.6.6;
22

33
import "../IKyberReserve.sol";
4+
import "../IKyberSanity.sol";
45
import "../utils/Utils5.sol";
56
import "../utils/zeppelin/SafeERC20.sol";
67

78

89
contract MockReserve is IKyberReserve, Utils5 {
910
using SafeERC20 for IERC20;
1011

12+
IKyberSanity public sanityRatesContract;
1113
mapping(address => uint256) public buyTokenRates;
1214
mapping(address => uint256) public sellTokenRates;
1315

1416
receive() external payable {}
1517

18+
function setContracts(
19+
IKyberSanity _sanityRates
20+
) public {
21+
sanityRatesContract = _sanityRates;
22+
}
23+
1624
function setRate(
1725
IERC20 token,
1826
uint256 buyRate,
@@ -90,6 +98,12 @@ contract MockReserve is IKyberReserve, Utils5 {
9098
if (dest != ETH_TOKEN_ADDRESS && dest.balanceOf(address(this)) < destAmount) {
9199
return 0;
92100
}
101+
102+
if (address(sanityRatesContract) != address(0)) {
103+
uint sanityRate = sanityRatesContract.getSanityRate(src, dest);
104+
if (rate > sanityRate) return 0;
105+
}
106+
93107
return rate;
94108
}
95109
}

0 commit comments

Comments
 (0)