-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathBroker.sol
225 lines (206 loc) · 8.22 KB
/
Broker.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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/math/SignedSafeMath.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/SafeCast.sol";
import "../interface/ILiquidityPool.sol";
import "../interface/IAccessControl.sol";
import "../interface/IPoolCreator.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/OrderData.sol";
import "../libraries/Utils.sol";
import "../Type.sol";
contract Broker is ReentrancyGuard {
using Address for address;
using SafeMath for uint256;
using SafeMathExt for int256;
using SignedSafeMath for int256;
using SafeCast for int256;
using OrderData for Order;
using OrderData for bytes;
uint256 internal constant GWEI = 10**9;
uint256 internal _chainID;
mapping(address => uint32) internal _nonces;
mapping(address => uint256) internal _balances;
mapping(bytes32 => int256) internal _orderFilled;
mapping(bytes32 => bool) internal _orderCanceled;
event Deposit(address indexed trader, uint256 amount);
event Withdraw(address indexed trader, uint256 amount);
event Transfer(address indexed sender, address indexed recipient, uint256 amount);
event TradeFailed(bytes32 orderHash, Order order, int256 amount, string reason);
event TradeSuccess(bytes32 orderHash, Order order, int256 amount, uint256 gasReward);
event CancelOrder(bytes32 orderHash);
event FillOrder(bytes32 orderHash, int256 fillAmount);
event CallFunction(
bytes32 userData1,
bytes32 userData2,
string functionSignature,
bytes callData,
bytes signature
);
constructor() {
_chainID = Utils.chainID();
}
/**
* @notice Sending eth to this contract is equivalent to depositing eth to the account of sender
*/
receive() external payable {
deposit();
}
/**
* @notice Get the eth balance of the trader's account
* @param trader The address of the trader
* @return uint256 The eth balance of the trader's account
*/
function balanceOf(address trader) public view returns (uint256) {
return _balances[trader];
}
/**
* @notice Deposit eth to the account of sender as gas reward of the broker
*/
function deposit() public payable nonReentrant {
_balances[msg.sender] = _balances[msg.sender].add(msg.value);
emit Deposit(msg.sender, msg.value);
}
/**
* @notice Withdraw eth for gas reward.
*
* @param amount The amount of eth to withdraw.
*/
function withdraw(uint256 amount) public nonReentrant {
_balances[msg.sender] = _balances[msg.sender].sub(amount);
Address.sendValue(payable(msg.sender), amount);
emit Withdraw(msg.sender, amount);
}
/**
* @notice Return if an order is canceled.
*
* @param order Order object.
*/
function isOrderCanceled(Order memory order) public view returns (bool) {
bytes32 orderHash = order.getOrderHash();
return _orderCanceled[orderHash];
}
/**
* @notice Return filled amount of an order.
*
* @param order Order object.
* @return filledAmount The amount of already filled.
*/
function getOrderFilledAmount(Order memory order) public view returns (int256 filledAmount) {
bytes32 orderHash = order.getOrderHash();
filledAmount = _orderFilled[orderHash];
}
/**
* @notice Cancel an order to prevent any further trade.
* Currently, Only trader or elayer and anthorized account (by order.trader)
* are able to cancel an order.
*
* @param order Order object.
*/
function cancelOrder(Order memory order) public {
if (msg.sender != order.trader && msg.sender != order.relayer) {
(, , address[7] memory addresses, , ) =
ILiquidityPool(order.liquidityPool).getLiquidityPoolInfo();
IAccessControl accessControl =
IAccessControl(IPoolCreator(addresses[0]).getAccessController());
bool isGranted =
accessControl.isGranted(order.trader, msg.sender, Constant.PRIVILEGE_TRADE);
require(isGranted, "sender must be trader or relayer or authorized");
}
bytes32 orderHash = order.getOrderHash();
require(!_orderCanceled[orderHash], "order is already canceled");
_orderCanceled[orderHash] = true;
emit CancelOrder(orderHash);
}
/**
* @notice Trade multiple orders, each order will be treated seperately.
* @param compressedOrders The compressed order objects to trade.
* @param amounts The trading amounts of position.
* @param gasRewards The gas rewards of eth given to their brokers.
*/
function batchTrade(
bytes[] calldata compressedOrders,
int256[] calldata amounts,
uint256[] calldata gasRewards
) external {
uint256 orderCount = compressedOrders.length;
for (uint256 i = 0; i < orderCount; i++) {
Order memory order = compressedOrders[i].decodeOrderData();
int256 amount = amounts[i];
uint256 gasReward = gasRewards[i];
bytes32 orderHash = order.getOrderHash();
if (order.chainID != _chainID) {
emit TradeFailed(orderHash, order, amount, "chain id mismatch");
return;
}
if (_orderCanceled[orderHash]) {
emit TradeFailed(orderHash, order, amount, "order is canceled");
return;
}
if (_orderFilled[orderHash].add(amount).abs() > order.amount.abs()) {
emit TradeFailed(orderHash, order, amount, "no enough amount to fill");
return;
}
if (gasReward > balanceOf(order.trader)) {
emit TradeFailed(orderHash, order, amount, "insufficient fee");
return;
}
if (gasReward > order.brokerFeeLimit * GWEI) {
emit TradeFailed(orderHash, order, amount, "fee exceeds trade gas limit");
return;
}
if (amount.abs() < order.minTradeAmount) {
emit TradeFailed(orderHash, order, amount, "amount is less than min trade amount");
return;
}
try
ILiquidityPool(order.liquidityPool).brokerTrade(compressedOrders[i], amount)
returns (int256 filledAmount) {
_fillOrder(orderHash, filledAmount);
_transfer(order.trader, order.relayer, gasReward);
emit TradeSuccess(orderHash, order, filledAmount, gasReward);
} catch Error(string memory reason) {
emit TradeFailed(orderHash, order, amount, reason);
return;
} catch {
emit TradeFailed(orderHash, order, amount, "transaction failed");
return;
}
}
}
/**
* @dev Update the filled position amount of the order.
*
* @param orderHash The hash of the order.
* @param amount The changed amount of filled position.
*/
function _fillOrder(bytes32 orderHash, int256 amount) internal {
_orderFilled[orderHash] = _orderFilled[orderHash].add(amount);
emit FillOrder(orderHash, amount);
}
/**
* @dev Transfer eth from sender's account to recipient's account.
*
* @param sender The address of the sender
* @param recipient The address of the recipient
* @param amount The amount of eth to transfer
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal {
require(recipient != address(0), "the recipient is zero address");
if (amount == 0) {
return;
}
require(_balances[sender] >= amount, "insufficient fee");
_balances[sender] = _balances[sender].sub(amount);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
}