-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathDarkPool.sol
More file actions
169 lines (132 loc) · 6.35 KB
/
DarkPool.sol
File metadata and controls
169 lines (132 loc) · 6.35 KB
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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "fhevm/lib/TFHE.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "forge-std/Test.sol";
contract DarkPool is Test {
// Buy or Sell base token for quote token
enum OrderType {
Buy,
Sell
}
struct Order {
euint32 amount; // Amount of base to buy/sell
euint32 price; // Price of base asset to buy/sell at (e.g 2000 USDC/ETH)
}
// [ base, quote ] (e.g. [ ETH, USDC ])
ERC20[] public tokens;
uint8 public constant BASE_INDEX = 0;
uint8 public constant QUOTE_INDEX = 1;
// user => token => balance
mapping(address => mapping(uint8 => euint32)) public balances;
// user => buy/sell => sellorder
mapping(address => mapping(OrderType => Order)) public orders;
event OrderCreated(address indexed user, uint8 orderType, euint32 amount, euint32 price);
event OrderUpdated(address indexed user, uint8 orderType, euint32 amount, euint32 price);
event OrderDeleted(address indexed user, uint8 orderType);
constructor(ERC20[] memory _tokens) {
tokens = _tokens;
}
function deposit(uint8 tokenId, uint32 amount) public {
tokens[tokenId].transferFrom(msg.sender, address(this), amount);
euint32 prevBalance = balances[msg.sender][tokenId];
balances[msg.sender][tokenId] = TFHE.add(prevBalance, amount);
}
function _createOrder(OrderType orderType, euint32 amount, euint32 price) internal {
// ensure there is no existing order
TFHE.req(TFHE.ne(orders[msg.sender][orderType].amount, 0));
if (orderType == OrderType.Buy) {
// ensure amount * price <= quote balance
TFHE.req(TFHE.le(TFHE.mul(amount, price), balances[msg.sender][QUOTE_INDEX]));
} else {
// ensure amount <= base balance
TFHE.req(TFHE.le(amount, balances[msg.sender][BASE_INDEX]));
}
// create sell order
orders[msg.sender][orderType] = Order(amount, price);
emit OrderCreated(msg.sender, uint8(orderType), amount, price);
}
function createOrder(OrderType orderType, bytes calldata amountCypherText, bytes calldata priceCypherText) public {
euint32 amount = TFHE.asEuint32(amountCypherText);
euint32 price = TFHE.asEuint32(priceCypherText);
_createOrder(orderType, amount, price);
}
function fillOrder(address buyer, address seller) public {
Order memory buyOrder = orders[buyer][OrderType.Buy];
Order memory sellOrder = orders[seller][OrderType.Sell];
emit log("checking order empty");
// ensure neither order is empty
TFHE.req(TFHE.ne(buyOrder.amount, 0));
TFHE.req(TFHE.ne(sellOrder.amount, 0));
emit log("checking order prices match");
// ensure prices are the same
TFHE.req(TFHE.eq(buyOrder.price, sellOrder.price));
emit log("checking which order ls larger");
// Check which order is larger
ebool buyOrderLarger = TFHE.le(sellOrder.amount, buyOrder.amount);
// Get the amount being traded
euint32 baseAmount = TFHE.cmux(buyOrderLarger, sellOrder.amount, buyOrder.amount);
euint32 quoteAmount = TFHE.mul(baseAmount, sellOrder.price); // note that buyOrder.price == sellOrder.price
/* Adjust order amounts */
// Subtract amount filled from each order
orders[buyer][OrderType.Buy].amount = TFHE.sub(buyOrder.amount, baseAmount);
orders[seller][OrderType.Sell].amount = TFHE.sub(sellOrder.amount, baseAmount);
// Adjust base balances
balances[seller][BASE_INDEX] = TFHE.sub(balances[seller][BASE_INDEX], baseAmount);
balances[buyer][BASE_INDEX] = TFHE.add(balances[buyer][BASE_INDEX], baseAmount);
// Adjust quote balances
balances[seller][QUOTE_INDEX] = TFHE.add(balances[seller][QUOTE_INDEX], quoteAmount);
balances[buyer][QUOTE_INDEX] = TFHE.sub(balances[buyer][QUOTE_INDEX], quoteAmount);
// Remove price of filled orders
orders[buyer][OrderType.Buy].price = TFHE.cmux(TFHE.le(buyOrder.amount, 0), TFHE.asEuint32(0), buyOrder.price);
orders[seller][OrderType.Sell].price =
TFHE.cmux(TFHE.le(sellOrder.amount, 0), TFHE.asEuint32(0), sellOrder.price);
emit OrderUpdated(
buyer, uint8(OrderType.Buy), orders[buyer][OrderType.Buy].amount, orders[buyer][OrderType.Buy].price
);
emit OrderUpdated(
seller, uint8(OrderType.Sell), orders[seller][OrderType.Sell].amount, orders[seller][OrderType.Sell].price
);
}
// Since we don't have control flow with TFHE,
// we require users or market makers to delete their
// orders once they have been filled
function deleteOrder(address user, OrderType orderType) public {
Order memory order = orders[user][orderType];
// ensure order exists
require(TFHE.isInitialized(order.amount), "Order does not exist");
// ensure order is empty
TFHE.req(TFHE.eq(order.amount, 0));
// delete order
delete orders[user][orderType];
emit OrderDeleted(user, uint8(orderType));
}
function retractOrder(OrderType orderType) public {
delete orders[msg.sender][orderType];
emit OrderDeleted(msg.sender, uint8(orderType));
}
function getBalance(uint8 tokenId, bytes32 publicKey) public view returns (bytes memory) {
return TFHE.reencrypt(balances[msg.sender][tokenId], publicKey);
}
function withdraw(uint8 tokenId, uint32 amount) public {
if (tokenId == BASE_INDEX) {
// ensure the user doesn't have an open sell order
require(
!TFHE.isInitialized(orders[msg.sender][OrderType.Sell].amount),
"Close sell order before withdrawing base"
);
} else {
// ensure the user doesn't have an open buy order
require(
!TFHE.isInitialized(orders[msg.sender][OrderType.Buy].amount),
"Close buy order before withdrawing quote"
);
}
// ensure user has enough balance
TFHE.req(TFHE.ge(balances[msg.sender][tokenId], amount));
// transfer tokens
tokens[tokenId].transfer(msg.sender, amount);
// update balance
balances[msg.sender][tokenId] = TFHE.sub(balances[msg.sender][tokenId], amount);
}
}