· 6 years ago · May 28, 2019, 01:32 PM
1 const option = {
2 amount: "1000000000000000000",
3 fee: 10000000000000000,
4 gas: 1000000,
5 gasPrice: 1000000000,
6 ttl: 55,
7 verify: true
8 }
9 const ctAddress = 'ct_27EnScVeeWpBwFxVniQJhLv4k76hFPDxaXYitfLke5tAfatRA5'
10 const NODE_COMPILER_URL = 'http://167.99.209.49:3080'
11 const NODE_URL = 'http://167.99.209.49:3013'
12 const NODE_INTERNAL_URL = 'http://167.99.209.49:3113'
13
14 const wallet = await Universal({
15 url: NODE_URL,
16 internalUrl: NODE_INTERNAL_URL,
17 keypair: {
18 publicKey: 'ak_pusrbH8SNMSaHLwr9SLymLgZnLwg5YNGxtcuiwFL9zuxXZ2Qx',
19 secretKey: '0f8ed0ed8134d7ae0d98f6cb1c93dcf67ad904616e6567389a8524bcbd6fbf446cca0ff4f7325635e2a7b7cdd4aa72912ad4b28ae32a208f66d00bf8668f052b',
20 },
21 nativeMode: true,
22 networkId: 'ae_uat',
23 compilerUrl: NODE_COMPILER_URL,
24 });
25 const source = `contract Token =
26 public function transfer : (address, int) => bool
27 public function transferFrom : (address, address, int) => bool
28
29contract WeiDex =
30 record state =
31 {
32 maker_fee : int,
33 taker_fee : int,
34 referral_fee: int,
35 token_orders : map(address, map(string, order)),
36 trade_history : list(order),
37 users : map(address, user),
38 referrals: map(address, address),
39 fee_account: address,
40 owner: address
41 }
42
43 public stateful function init(fee_account' : address, maker_fee' : int, taker_fee' : int) =
44 {
45 maker_fee = maker_fee',
46 taker_fee = taker_fee',
47 referral_fee = 200000000000000000, // 20%
48 token_orders = {},
49 trade_history = [],
50 users={},
51 referrals = {},
52 fee_account = fee_account',
53 owner = Call.caller
54 }
55
56 record balance =
57 {
58 full_balance : int,
59 available_balance : int
60 }
61
62 record user =
63 {
64 balances : map(address, balance),
65 order_history : map(string, order),
66 orders : map(string, order)
67 }
68
69 // order of the properties is important for memory optimizations
70 record order =
71 {
72 sell_amount : int,
73 buy_amount : int,
74 filled : int,
75 status : int, // 0: open, 1: partially filled, 2: filled, 3: cancelled, 4: error
76 sell_token : address,
77 buy_token : address,
78 maker : address,
79 taker : address,
80 hash : string
81 }
82
83 /************************PUBLIC STATEFUL FUNCTIONS*****************************/
84 public stateful function deposit(
85 token: option(Token),
86 amount : int,
87 beneficiary : address,
88 referral : address
89 ) : bool =
90 require(amount > 0, "DEPOSIT_SUB_ZERO_AMOUNT")
91 let value : int = amount
92 let user : address = Call.caller
93
94 switch(token)
95 None =>
96 require(value == Call.value, "DEPOSIT_INVALID_AMOUNT")
97 increase_balance(user, ak_11111111111111111111111111111111273Yts, value)
98 Some(t) =>
99 require(t.transferFrom(user, Contract.address, value), "DEPOSIT_TOKEN_TRANSFER_FAIL")
100 increase_balance(user, t.address, value)
101
102 if(!user_has_referrer(user))
103 add_referral(user, referral)
104
105 true
106
107 public stateful function withdraw(token : option(Token), amount : int) : bool =
108 require(amount > 0, "WITHDRAW_SUB_ZERO_AMOUNT")
109 let user : address = Call.caller
110
111 switch(token)
112 None =>
113 require(available_balance_of(user, ak_11111111111111111111111111111111273Yts) >= amount, "WITHDRAW_INSUFFICIENT_FUNDS")
114 decrease_balance(user, ak_11111111111111111111111111111111273Yts, amount)
115 Chain.spend(user, amount)
116 Some(t) =>
117 require(available_balance_of(user, t.address) >= amount, "WITHDRAW_INSUFFICIENT_FUNDS")
118 decrease_balance(user, t.address, amount)
119 require(t.transfer(user, amount), "WITHDRAW_TOKEN_TRANSFER_FAIL")
120
121 true
122
123 public stateful function place_order(
124 sell_amount : int,
125 buy_amount : int,
126 sell_token : option(address),
127 buy_token : option(address),
128 hash : string
129 ) : bool =
130 let maker : address = Call.caller
131 let user : user = get_user(maker, state.users)
132
133 let parsed_sell_token =
134 switch(sell_token)
135 None => ak_11111111111111111111111111111111273Yts
136 Some(t) => t
137
138 let parsed_buy_token =
139 switch(buy_token)
140 None => ak_11111111111111111111111111111111273Yts
141 Some(t) => t
142
143 require(available_balance_of(maker, parsed_sell_token) >= sell_amount, "PLACE_INSUFFICIENT_FUNDS")
144 require(!user_has_order(hash, user.orders), "PLACE_ORDER_ALREADY_EXISTS")
145
146 let order : order =
147 {
148 sell_amount = sell_amount,
149 buy_amount = buy_amount,
150 filled = 0,
151 status = 0,
152 sell_token = parsed_sell_token,
153 buy_token = parsed_buy_token,
154 maker = maker,
155 taker = ak_11111111111111111111111111111111273Yts,
156 hash = hash}
157
158 decrease_available_balance(maker, parsed_sell_token, sell_amount)
159 add_user_order(maker, hash, order)
160 add_token_order(parsed_sell_token, hash, order)
161
162 true
163
164 public stateful function cancel_order(hash : string) : bool =
165 let maker : address = Call.caller
166 let user : user = get_user(maker, state.users)
167
168 require(user_has_order(hash, user.orders), "CANCEL_INVALID_ORDER")
169
170 let found_order : order = get_order_by_hash(hash, user.orders)
171 // user should not be able to cancel fully filled or already cancelled orders,
172 // because his locked balance will be decreased
173 require(found_order.status < 2, "CANCEL_INVALID")
174
175 update_user_order_status(maker, hash, 3)
176
177 let updatedOrder : order = update_token_order_status(hash, found_order, 3)
178 let makerRefund : int = found_order.sell_amount - get_partial_amount(found_order.sell_amount, found_order.buy_amount, found_order.filled)
179
180 // update state
181 increase_available_balance(maker, found_order.sell_token, makerRefund)
182 delete_order(maker, found_order.sell_token, hash)
183 add_order_history(maker, hash, updatedOrder)
184 add_trade_history(updatedOrder)
185 true
186
187 public stateful function take_order(maker : address, hash : string, taker_sell_amount : int) : bool =
188 let taker : address = Call.caller
189 let order : order = get_order(maker, hash)
190
191 require(order.status < 3, "TAKE_ORDER_CANCELLED")
192
193 let taker_received_amount : int = get_partial_amount(order.sell_amount, order.buy_amount, taker_sell_amount)
194
195 // assert conditions
196 require((order.filled + taker_sell_amount) =< order.buy_amount, "TAKE_ORDER_FILLED")
197 require(available_balance_of(taker, order.buy_token) >= taker_sell_amount, "TAKE_INSUFFICIENT_FUNDS_TAKER")
198 require(full_balance_of(maker, order.sell_token) >= taker_received_amount, "TAKE_INSUFFICIENT_FUNDS_MAKER")
199
200 // update user balances
201 decrease_full_balance(maker, order.sell_token, taker_received_amount)
202 decrease_balance(taker, order.buy_token, taker_sell_amount)
203 increase_balance_after_fee(maker, order.buy_token, taker_sell_amount, state.maker_fee)
204 increase_balance_after_fee(taker, order.sell_token, taker_received_amount, state.taker_fee)
205
206 // update order history
207 let takerHistoryOrder = get_taker_history_order(taker_received_amount, taker, order)
208 let makerHistoryOrder = get_maker_history_order(taker_sell_amount, taker, order)
209 add_order_history(taker, hash, takerHistoryOrder)
210 add_order_history(maker, hash, makerHistoryOrder)
211
212 // update user and token orders
213 if((order.filled + taker_sell_amount) == order.buy_amount)
214 delete_order(maker, order.sell_token, hash) // if order is fully filled remove it from the open orders
215 add_trade_history(order{status = 2, filled @ f = f + taker_sell_amount})
216 else
217 update_token_order_filled_amount_and_status(hash, order, taker_sell_amount, 1)
218 update_user_order_filled_amount_and_status(maker, hash, taker_sell_amount, 1)
219 add_trade_history(order{status = 1, filled @ f = f + taker_sell_amount})
220 true
221
222 /************************PUBLIC VIEW FUNCTIONS*****************************/
223 public function get_user(who : address, users : map(address, user)) : user =
224 let user : user = lookup_by_address(who, users, {balances={}, order_history= {}, orders={}})
225 user
226
227 public function get_refferal(who : address) : address =
228 let referral : address = lookup_by_address(who, state.referrals, ak_11111111111111111111111111111111273Yts)
229 referral
230
231 public function user_has_referrer(user : address) : bool =
232 Map.member(user, state.referrals)
233
234 public function get_user_balances(who : address) : list((address, balance)) =
235 let user : user = get_user(who, state.users)
236 let balances : list((address, balance)) = Map.to_list(user.balances)
237 balances
238
239 public function full_balance_of(who: address, token : address) : int =
240 let user : user = get_user(who, state.users)
241 let balances = lookup_by_address(token, user.balances, {full_balance = 0, available_balance = 0})
242 balances.full_balance
243
244 public function available_balance_of(who: address, token : address) : int =
245 let user : user = get_user(who, state.users)
246 let balances = lookup_by_address(token, user.balances, {full_balance = 0, available_balance = 0})
247 balances.available_balance
248
249 public function get_open_orders_by_user(who : address) : list((string, order)) =
250 let user : user = get_user(who, state.users)
251 let open_orders : list((string, order)) = Map.to_list(user.orders)
252 open_orders
253
254 public function get_open_orders_by_token(token : address) : list((string, order)) =
255 let token_orders : list((string, order)) = Map.to_list(state.token_orders[token = {}])
256 token_orders
257
258 public function get_open_sell_orders_by_token(quote : address, base : address) : list((string, order)) =
259 let l : list((string, order)) = get_open_orders_by_token(base)
260 filter(get_order_by_type, l, quote)
261
262 public function get_open_buy_orders_by_token(quote : address, base : address) : list((string, order)) =
263 let l : list((string, order)) = get_open_orders_by_token(quote)
264 filter(get_order_by_type, l, base)
265
266 public function get_open_orders_by_user_and_token(who : address, token : address) : list((string,order)) =
267 let l : list((string, order)) = get_open_orders_by_user(who)
268 filter(is_order_open, l, token)
269
270 public function get_order_history_by_user(who : address) : list((string, order)) =
271 let user : user = get_user(who, state.users)
272 let order_history : list((string, order)) = Map.to_list(user.order_history)
273 order_history
274
275 public function get_order_history_by_token(token : address) : list(order) =
276 filter(is_order_valid_for_history, state.trade_history, token)
277
278 public function get_order_history() : list(order) =
279 state.trade_history
280
281 /************************PRIVATE STATEFUL FUNCTIONS*****************************/
282 private stateful function add_referral(user : address, referral : address) =
283 put(state{referrals[user] = referral})
284
285 private stateful function add_user_order(user : address, hash : string, order : order) =
286 put(state{users @ old_users = old_users{[user] @ old_user = old_user{orders @ old_orders = old_orders{[hash] = order}}}})
287
288 private stateful function add_token_order(token: address, hash : string, order : order) =
289 let new_token_orders : map(address, map(string, order)) = state.token_orders{[token = {[hash] = order}] @ previousOrders = previousOrders{[hash] = order}}
290 put(state{token_orders = new_token_orders})
291
292 private stateful function update_user_order(maker : address, hash : string, newOrder : order) =
293 let newUserOrders : map(string, order) = state.users[maker].orders{[hash] = newOrder}
294 let new_user : user = state.users[maker]{orders = newUserOrders}
295 let new_users : map(address, user) = state.users{[maker] = new_user}
296 put(state{users = new_users})
297
298 private stateful function update_user_order_status(maker : address, hash : string, status : int) =
299 let new_user_order : order = state.users[maker].orders[hash]{status = status}
300 update_user_order(maker, hash, new_user_order)
301
302 private stateful function update_user_order_filled_amount_and_status(maker : address, hash : string, filled : int, status : int) =
303 let new_user_order : order = state.users[maker].orders[hash]{filled @ f = f + filled, status = status}
304 update_user_order(maker, hash, new_user_order)
305
306 private stateful function update_token_order_status(hash : string, order : order, status : int) : order =
307 let cancelled_order : order = order{status = status}
308 let new_token_orders : map(address, map(string, order)) = state.token_orders{[order.sell_token = {[hash] = cancelled_order}] @ previousOrders = previousOrders{[hash] = cancelled_order}}
309 put(state{token_orders = new_token_orders})
310 cancelled_order
311
312 private stateful function update_token_order_filled_amount_and_status(hash : string, order : order, filled : int, status : int) =
313 let filled_order : order = order{filled @ f = f + filled, status = status}
314 let new_token_orders : map(address, map(string, order)) = state.token_orders{[order.sell_token = {[hash] = filled_order}] @ previousOrders = previousOrders{[hash] = filled_order}}
315 put(state{token_orders = new_token_orders})
316
317 private stateful function update_balance(who : address, token : address, newBalance : balance) =
318 let default_user : user = {balances={}, order_history= {}, orders={}}
319 let new_user_balances : map(address, balance) = state.users[who=default_user].balances{[token] = newBalance}
320 let new_user : user = state.users[who=default_user]{balances = new_user_balances}
321 let new_users : map(address, user) = state.users{[who] = new_user}
322 put(state{users = new_users})
323
324 private stateful function decrease_full_balance(who : address, token : address, amount : int) =
325 let new_user_balance : balance = state.users[who].balances[token]{full_balance @ f = f - amount}
326 update_balance(who, token, new_user_balance)
327
328 private stateful function increase_balance(who : address, token : address, amount : int) =
329 increase_full_balance(who, token, amount)
330 increase_available_balance(who, token, amount)
331
332 private stateful function decrease_balance(who : address, token : address, amount : int) =
333 decrease_full_balance(who, token, amount)
334 decrease_available_balance(who, token, amount)
335
336 private stateful function increase_full_balance(who : address, token : address, amount : int) =
337 let default_user : user = {balances = {}, order_history = {}, orders = {}}
338 let default_balance : balance = {full_balance = 0, available_balance = 0}
339 let new_user_balance : balance = state.users[who = default_user].balances[token = default_balance]{full_balance @ b = b + amount}
340 update_balance(who, token, new_user_balance)
341
342 private stateful function decrease_available_balance(who : address, token : address, amount : int) =
343 let new_user_balance : balance = state.users[who].balances[token]{available_balance @ a = a - amount}
344 update_balance(who, token, new_user_balance)
345
346 private stateful function increase_available_balance(who : address, token : address, amount : int) =
347 let default_user : user = {balances = {}, order_history = {}, orders = {}}
348 let default_balance : balance = {full_balance = 0, available_balance = 0}
349 let new_user_balance : balance = state.users[who = default_user].balances[token = default_balance]{available_balance @ b = b + amount}
350 update_balance(who, token, new_user_balance)
351
352 private stateful function delete_user_order(user : address, hash : string) =
353 let new_user : user = state.users[user]{orders @ o = Map.delete(hash, o)}
354 let new_users : map(address, user) = state.users{[user] = new_user}
355 put(state{users = new_users})
356
357 private stateful function delete_token_order(token : address, hash : string) =
358 let new_token_orders : map(address, map(string, order)) = state.token_orders{[token] @ previousOrders = Map.delete(hash, previousOrders)}
359 put(state{token_orders = new_token_orders})
360
361 private stateful function delete_order(user : address, token : address, hash : string) =
362 delete_user_order(user, hash)
363 delete_token_order(token, hash)
364
365 private stateful function add_trade_history(order : order) =
366 put(state{ trade_history = order :: state.trade_history })
367
368 private stateful function add_order_history(user : address, hash : string, order : order) =
369 put(state{users @ old_users = old_users{[user] @ old_user = old_user{order_history @ old_orders = old_orders{[hash] = order}}}})
370
371 /************************PRIVATE VIEW FUNCTIONS*****************************/
372 private function get_order(who : address, hash : string) : order =
373 let user : user = get_user(who, state.users)
374 require(user_has_order(hash, user.orders), "GET_ORDER_INVALID_ORDER")
375 let found_order : order = get_order_by_hash(hash, user.orders)
376 found_order
377
378 private function user_has_order(hash: string, orders : map(string, order)) : bool =
379 Map.member(hash, orders)
380
381 private function get_maker_history_order(filled : int, taker : address, order : order) : order =
382 let maker_order : order = order{taker = taker, filled = filled}
383 maker_order
384
385 private function get_taker_history_order(filled : int, taker : address, order : order) : order =
386 let taker_order : order = order{buy_token = order.sell_token, sell_token = order.buy_token, taker = taker, filled = filled}
387 taker_order
388
389 private function lookup_by_address(k : address, m, v) =
390 Map.lookup_default(k, m, v)
391
392 private function get_order_by_hash(hash : string, orders : map(string, order)) =
393 Map.lookup_default(hash, orders, {
394 sell_amount = 0,
395 buy_amount = 0,
396 filled = 0,
397 status = 4,
398 sell_token = ak_11111111111111111111111111111111273Yts,
399 buy_token = ak_11111111111111111111111111111111273Yts,
400 maker = ak_11111111111111111111111111111111273Yts,
401 taker = ak_11111111111111111111111111111111273Yts,
402 hash = "0"})
403
404 /************************HELPER FUNCTIONS*****************************/
405 private function filter (f : ('a, address) => bool, l : list('a), token : address) = filter'(f, l, token, [])
406 private function filter'(f : ('a, address) => bool, l : list('a), token : address, acc : list('a)) =
407 switch(l)
408 [] => acc
409 e :: g =>
410 if(f(e, token))
411 filter'(f, g, token, e :: acc)
412 else
413 filter'(f, g, token, acc)
414
415 private function is_order_valid_for_history(order : order, token : address) : bool =
416 (order.buy_token == token || order.sell_token == token) && order.status < 3
417
418 private function is_order_open(order_pair : (string, order), token : address) : bool =
419 let o : order = pair_second(order_pair)
420 o.buy_token == token || o.sell_token == token
421
422 private function get_order_by_type(order_pair : (string, order), token : address) : bool =
423 let o : order = pair_second(order_pair)
424 o.buy_token == token
425
426 function pair_second(tuple) =
427 switch(tuple)
428 (_, e) => e
429
430 /************************MATH HELPER*****************************/
431 private function get_partial_amount(numerator : int, denominator : int, target : int) : int =
432 let partialAmount : int = (numerator * target) / denominator
433 partialAmount
434
435 private function get_fee_amount(numerator : int, target : int) : int =
436 let feeAmount : int = (numerator * target) / 1000000000000000000
437 feeAmount
438
439 private stateful function increase_balance_after_fee(who : address, token : address, amount : int, fee : int) =
440 let feeAmount : int = get_fee_amount(amount, fee)
441 let increaseAmount : int = amount - feeAmount
442
443 if(user_has_referrer(who))
444 let referral : address = get_refferal(who)
445 let referral_fee : int = get_fee_amount(feeAmount, state.referral_fee)
446 let exchangeFee : int = feeAmount - referral_fee
447 increase_balance(who, token, increaseAmount)
448 increase_balance(state.fee_account, token, exchangeFee)
449 increase_balance(referral, token, referral_fee)
450 else
451 increase_balance(who, token, increaseAmount)
452 increase_balance(state.fee_account, token, feeAmount)
453
454 /************************OWNABLE*****************************/
455 public function owner() : address = state.owner
456
457 private function only_owner() : bool =
458 require(state.owner == Call.caller, "ONLY_OWNER")
459 true
460
461 public stateful function transfer_ownership(new_owner : address) =
462 only_owner()
463 transfer_ownership'(new_owner)
464
465 private stateful function transfer_ownership'(new_owner : address) =
466 put(state{owner = new_owner})
467
468 /************************CONFIG*****************************/
469 public stateful function set_maker_fee(new_fee : int) =
470 only_owner()
471 put(state{maker_fee = new_fee})
472
473 public stateful function set_taker_fee(new_fee : int) =
474 only_owner()
475 put(state{taker_fee = new_fee})
476
477 public stateful function set_fee_account(new_fee_account : address) =
478 only_owner()
479 put(state{fee_account = new_fee_account})
480
481 public function maker_fee() : int = state.maker_fee
482
483 public function taker_fee() : int = state.taker_fee
484
485 /************************ASSERT*****************************/
486 private function require(b : bool, err : string) =
487 if(!b)
488 abort(err)
489`;
490
491 const ctIns = await wallet.getContractInstance(source, { contractAddress: ctAddress })
492 const res = await ctIns.call('deposit', [Promise.reject(), '1000000000000000000', 'ak_pusrbH8SNMSaHLwr9SLymLgZnLwg5YNGxtcuiwFL9zuxXZ2Qx', 'ak_pusrbH8SNMSaHLwr9SLymLgZnLwg5YNGxtcuiwFL9zuxXZ2Qx'], option)