This repository has been archived by the owner on Sep 13, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
/
ConditionUniswapV2RateStateful.sol
209 lines (184 loc) · 6.98 KB
/
ConditionUniswapV2RateStateful.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.10;
pragma experimental ABIEncoderV2;
import {GelatoStatefulConditionsStandard} from "../../GelatoStatefulConditionsStandard.sol";
import {IUniswapV2Router02, IUniswapV2Factory, IWETH} from "../../../dapp_interfaces/uniswap_v2/IUniswapV2.sol";
import {SafeMath} from "../../../external/SafeMath.sol";
import {IGelatoCore} from "../../../gelato_core/interfaces/IGelatoCore.sol";
import {IERC20} from "../../../external/IERC20.sol";
contract ConditionUniswapV2RateStateful is GelatoStatefulConditionsStandard {
using SafeMath for uint256;
IUniswapV2Router02 public immutable uniRouter;
IUniswapV2Factory public immutable uniFactory;
IWETH public immutable WETH;
address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// userProxy => taskReceipt.id => refPrice
mapping(address => mapping(uint256 => uint256)) public refRate;
constructor(
IUniswapV2Router02 _uniswapV2Router,
IUniswapV2Factory _uniswapV2Factory,
IWETH _weth,
IGelatoCore _gelatoCore
)
public
GelatoStatefulConditionsStandard(_gelatoCore)
{
uniRouter = _uniswapV2Router;
uniFactory = _uniswapV2Factory;
WETH = _weth;
}
/// @dev use this function to encode the data off-chain for the condition data field
function getConditionData(
address _userProxy,
address _sellToken,
uint256 _sellAmount,
address _buyToken,
bool _greaterElseSmaller
)
public
pure
virtual
returns(bytes memory)
{
return abi.encodeWithSelector(
this.checkRefRateUniswap.selector,
uint256(0), // taskReceiptId placeholder
_userProxy,
_sellToken,
_sellAmount,
_buyToken,
_greaterElseSmaller
);
}
// STANDARD Interface
/// @param _conditionData The encoded data from getConditionData()
function ok(uint256 _taskReceiptId, bytes calldata _conditionData, uint256)
public
view
virtual
override
returns(string memory)
{
(address userProxy,
address sendToken,
uint256 sendAmount,
address receiveToken,
bool greaterElseSmaller
) = abi.decode(
_conditionData[36:], // slice out selector & taskReceiptId
(address,address,uint256,address,bool)
);
return checkRefRateUniswap(
_taskReceiptId, userProxy, sendToken, sendAmount, receiveToken, greaterElseSmaller
);
}
// Specific Implementation
function checkRefRateUniswap(
uint256 _taskReceiptId,
address _userProxy,
address _sellToken,
uint256 _sellAmount,
address _buyToken,
bool _greaterElseSmaller
)
public
view
virtual
returns(string memory)
{
uint256 currentRefRate = refRate[_userProxy][_taskReceiptId];
(_sellToken, _buyToken) = convertEthToWeth(_sellToken, _buyToken);
uint256 expectedRate = getUniswapRate(_sellToken, _sellAmount, _buyToken);
if (_greaterElseSmaller) { // greaterThan
if (expectedRate >= currentRefRate) return OK;
else return "ExpectedRateIsNotGreaterThanRefRate";
} else { // smallerThan
if (expectedRate <= currentRefRate) return OK;
else return "ExpectedRateIsNotSmallerThanRefRate";
}
}
function getPaths(address _sellToken, address _buyToken)
internal pure returns(address[] memory paths)
{
paths = new address[](2);
paths[0] = _sellToken;
paths[1] = _buyToken;
}
/// @dev This function should be called via the userProxy of a Gelato Task as part
/// of the Task.actions, if the Condition state should be updated after the task.
/// @param _rateDeltaAbsolute The change in price after which this condition should return for a given taskId
/// @param _idDelta Default to 0. If you submit multiple tasks in one action, this can help
// customize which taskId the state should be allocated to
function setRefRateAbsolute(
address _sellToken,
uint256 _sellAmount,
address _buyToken,
bool _greaterElseSmaller,
uint256 _rateDeltaAbsolute,
uint256 _idDelta
)
external
{
uint256 taskReceiptId = _getIdOfNextTaskInCycle() + _idDelta;
(_sellToken, _buyToken) = convertEthToWeth(_sellToken, _buyToken);
uint256 expectedRate = getUniswapRate(_sellToken, _sellAmount, _buyToken);
if (_greaterElseSmaller) {
refRate[msg.sender][taskReceiptId] = expectedRate.add(_rateDeltaAbsolute);
} else {
refRate[msg.sender][taskReceiptId] = expectedRate.sub(
_rateDeltaAbsolute,
"ConditionKyberRateStateful.setRefRate: Underflow"
);
}
}
/// @dev This function should be called via the userProxy of a Gelato Task as part
/// of the Task.actions, if the Condition state should be updated after the task.
/// @param _rateDeltaNominator The nominator defining the % change, e.g. 50 for 50%
/// @param _idDelta Default to 0. If you submit multiple tasks in one action, this can help
// customize which taskId the state should be allocated to
function setRefRateRelative(
address _sellToken,
uint256 _sellAmount,
address _buyToken,
bool _greaterElseSmaller,
uint256 _rateDeltaNominator,
uint256 _idDelta
)
external
{
uint256 taskReceiptId = _getIdOfNextTaskInCycle() + _idDelta;
(_sellToken, _buyToken) = convertEthToWeth(_sellToken, _buyToken);
uint256 expectedRate = getUniswapRate(_sellToken, _sellAmount, _buyToken);
uint256 absoluteDelta = expectedRate.mul(_rateDeltaNominator).div(100);
if (_greaterElseSmaller) {
refRate[msg.sender][taskReceiptId] = expectedRate.add(absoluteDelta);
} else {
refRate[msg.sender][taskReceiptId] = expectedRate.sub(
absoluteDelta,
"ConditionKyberRateStateful.setRefRate: Underflow"
);
}
}
function getUniswapRate(address _sellToken, uint256 _sellAmount, address _buyToken)
public
view
returns(uint256 expectedRate)
{
address[] memory tokenPath = getPaths(_sellToken, _buyToken);
try uniRouter.getAmountsOut(_sellAmount, tokenPath)
returns (uint[] memory expectedRates) {
expectedRate = expectedRates[1];
} catch {
revert("UniswapV2GetExpectedRateError");
}
}
function convertEthToWeth(address _sellToken, address _buyToken)
private
view
returns(address, address)
{
if (_sellToken == ETH_ADDRESS) _sellToken = address(WETH);
if (_buyToken == ETH_ADDRESS) _buyToken = address(WETH);
return (_sellToken, _buyToken);
}
}