/
exchange.go
394 lines (318 loc) · 12 KB
/
exchange.go
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
package dex
import (
"encoding/hex"
"fmt"
"log"
"math/big"
"github.com/amansardana/matching-engine/dex/interfaces"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
. "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
// Exchange is an augmented interface to the Exchange.sol smart-contract. It uses the
// smart-contract bindings generated with abigen and adds additional functionality and
// simplifications to these bindings.
// Address is the Ethereum address of the exchange contract
// Contract is the original abigen bindings
// CallOptions are options for making read calls to the connected backend
// TxOptions are options for making write txs to the connected backend
type Exchange struct {
Admin *Wallet
Address Address
Contract *interfaces.Exchange
CallOptions *bind.CallOpts
TxOptions *bind.TransactOpts
}
// Returns a new exchange interface for a given wallet, contract address and connected backend.
// The exchange contract need to be already deployed at the given address. The given wallet will
// be used by default when sending transactions with this object.
func NewExchange(admin *Wallet, contractAddress Address, backend bind.ContractBackend) (*Exchange, error) {
instance, err := interfaces.NewExchange(contractAddress, backend)
if err != nil {
return nil, err
}
callOptions := &bind.CallOpts{Pending: true}
txOptions := bind.NewKeyedTransactor(admin.PrivateKey)
return &Exchange{
Address: contractAddress,
Contract: instance,
CallOptions: callOptions,
TxOptions: txOptions,
Admin: admin,
}, nil
}
// SetDefaultTxOptions resets the transaction value to 0
func (e *Exchange) SetDefaultTxOptions() {
e.TxOptions = bind.NewKeyedTransactor(e.Admin.PrivateKey)
e.TxOptions.Value = big.NewInt(0)
}
// SetTxValue sets the transaction ether value
func (e *Exchange) SetTxValue(value *big.Int) {
e.TxOptions.Value = value
}
// SetCustomSender updates the sender address address to the exchange contract
func (e *Exchange) SetCustomSender(wallet *Wallet) {
e.TxOptions = bind.NewKeyedTransactor(wallet.PrivateKey)
}
// SetDefaultSender sets the default sender address that will be used when sending a transcation to
// the exchange contract
func (e *Exchange) SetDefaultSender() {
e.TxOptions = bind.NewKeyedTransactor(e.Admin.PrivateKey)
}
// SetFeeAccount sets the fee account of the exchange contract. The fee account receives
// the trading fees whenever a trade is settled.
func (e *Exchange) SetFeeAccount(account Address) (*types.Transaction, error) {
tx, err := e.Contract.SetFeeAccount(e.TxOptions, account)
if err != nil {
return nil, err
}
return tx, nil
}
// SetOperator updates the operator settings of the given address. Only addresses with an
// operator access can execute Withdraw and Trade transactions to the Exchange smart contract
func (e *Exchange) SetOperator(account Address, isOperator bool) (*types.Transaction, error) {
tx, err := e.Contract.SetOperator(e.TxOptions, account, isOperator)
if err != nil {
return nil, err
}
return tx, nil
}
// SetWithdrawalSecurityPeriod sets the period after which a non-operator address can send
// a transaction to the exchange smart-contract to withdraw their funds. This acts as security mechanism
// to prevent the operator of the exchange from holding funds
func (e *Exchange) SetWithdrawalSecurityPeriod(p *big.Int) (*types.Transaction, error) {
tx, err := e.Contract.SetWithdrawalSecurityPeriod(e.TxOptions, p)
if err != nil {
return nil, err
}
return tx, nil
}
// DepositEther deposits ether into the exchange smart-contract.
func (e *Exchange) DepositEther(value *big.Int) (*types.Transaction, error) {
e.SetTxValue(value)
tx, err := e.Contract.DepositEther(e.TxOptions)
if err != nil {
return nil, err
}
e.SetDefaultTxOptions()
return tx, nil
}
// DepositEtherFrom deposits ether from a custom address. The transaction sender is reset
// after the transaction is carried out.
func (e *Exchange) DepositEtherFrom(wallet *Wallet, value *big.Int) (*types.Transaction, error) {
e.SetTxValue(value)
e.SetCustomSender(wallet)
tx, err := e.Contract.DepositEther(e.TxOptions)
if err != nil {
return nil, err
}
e.SetDefaultTxOptions()
return tx, nil
}
// DepositToken deposits tokens into the exchange smart-contract.
func (e *Exchange) DepositToken(token Address, amount *big.Int) (*types.Transaction, error) {
// e.SetDefaultTxOptions()
tx, err := e.Contract.DepositToken(e.TxOptions, token, amount)
if err != nil {
return nil, err
}
return tx, err
}
// DepositEtherFrom deposits ether from a custom address. The transaction sender is reset
// after the transaction is carried out.
func (e *Exchange) DepositTokenFrom(wallet *Wallet, token Address, amount *big.Int) (*types.Transaction, error) {
e.SetCustomSender(wallet)
tx, err := e.Contract.DepositToken(e.TxOptions, token, amount)
if err != nil {
return nil, err
}
e.SetDefaultSender()
return tx, err
}
// TokenBalance returns the Exchange token balance of the given token at the given account address.
// Note: This is not the token BalanceOf() function, it's the balance of tokens that have been deposited
// in the exchange smart contract.
func (e *Exchange) TokenBalance(trader Address, token Address) (*big.Int, error) {
balance, err := e.Contract.TokenBalance(e.CallOptions, trader, token)
if err != nil {
return nil, err
}
return balance, nil
}
// EtherBalance returns the Exchange ether balance of the given account address.
// Note: This is not the current ether balance of the given ether address. It's the balance of ether
// that has been deposited in the exchange smart contract.
func (e *Exchange) EtherBalance(trader Address) (*big.Int, error) {
balance, err := e.Contract.EtherBalance(e.CallOptions, trader)
if err != nil {
return nil, err
}
return balance, nil
}
// WithdrawalSecurityPeriod is the period after which a non-operator account can withdraw their funds from
// the exchange smart contract.
func (e *Exchange) WithdrawalSecurityPeriod() (*big.Int, error) {
period, err := e.Contract.WithdrawalSecurityPeriod(e.CallOptions)
if err != nil {
return nil, err
}
return period, nil
}
// FeeAccount is the Ethereum towards the exchange trading fees are sent
func (e *Exchange) FeeAccount() (Address, error) {
account, err := e.Contract.FeeAccount(e.CallOptions)
if err != nil {
return Address{}, err
}
return account, nil
}
// Operator returns true if the given address is an operator of the exchange and returns false otherwise
func (e *Exchange) Operator(address Address) (bool, error) {
isOperator, err := e.Contract.Operators(e.CallOptions, address)
if err != nil {
return false, err
}
return isOperator, nil
}
// SecurityWithdraw executes a security withdraw transaction. Security withdraw transactions can only be
// executed after the security withdrawal period has ended.
func (e *Exchange) SecurityWithdraw(wallet *Wallet, token Address, amount *big.Int) (*types.Transaction, error) {
e.SetDefaultTxOptions()
e.SetCustomSender(wallet)
tx, err := e.Contract.SecurityWithdraw(e.TxOptions, token, amount)
if err != nil {
return nil, err
}
return tx, nil
}
// Withdraw executes a normal withdraw transaction. This withdraws tokens or ether from the exchange
// and returns them to the payload Receiver. Only an operator account can send a withdraw
// transaction
func (e *Exchange) Withdraw(w *Withdrawal) (*types.Transaction, error) {
e.SetDefaultTxOptions()
s := w.Signature
tx, err := e.Contract.Withdraw(e.TxOptions, w.Token, w.Amount, w.Trader, w.Receiver, w.Nonce, s.V, [2][32]byte{s.R, s.S}, w.Fee)
if err != nil {
return nil, err
}
return tx, nil
}
// Trade executes a settlements transaction. The order and trade payloads need to be signed respectively
// by the Maker and the Taker of the trade. Only the operator account can send a Trade function to the
// Exchange smart contract.
func (e *Exchange) Trade(o *Order, t *Trade) (*types.Transaction, error) {
e.SetDefaultTxOptions()
orderValues := [8]*big.Int{o.AmountBuy, o.AmountSell, o.Expires, o.Nonce, o.FeeMake, o.FeeTake, t.Amount, t.TradeNonce}
orderAddresses := [4]Address{o.TokenBuy, o.TokenSell, o.Maker, t.Taker}
vValues := [2]uint8{o.Signature.V, t.Signature.V}
rsValues := [4][32]byte{o.Signature.R, o.Signature.S, t.Signature.R, t.Signature.S}
tx, err := e.Contract.ExecuteTrade(e.TxOptions, orderValues, orderAddresses, vValues, rsValues)
if err != nil {
return nil, err
}
return tx, nil
}
// ListenToErrorEvents returns a channel that receives errors logs (events) from the exchange smart contract.
// The error IDs correspond to the following codes:
// 1. MAKER_INSUFFICIENT_BALANCE,
// 2. TAKER_INSUFFICIENT_BALANCE,
// 3. WITHDRAW_INSUFFICIENT_BALANCE,
// 4. WITHDRAW_FEE_TO_HIGH,
// 5. ORDER_EXPIRED,
// 6. WITHDRAW_ALREADY_COMPLETED,
// 7. TRADE_ALREADY_COMPLETED,
// 8. TRADE_AMOUNT_TOO_BIG,
// 9. SIGNATURE_INVALID,
// 10. MAKER_SIGNATURE_INVALID,
// 11. TAKER_SIGNATURE_INVALID
func (e *Exchange) ListenToErrorEvents() (chan *interfaces.ExchangeLogError, error) {
events := make(chan *interfaces.ExchangeLogError)
opts := &bind.WatchOpts{nil, nil}
_, err := e.Contract.WatchLogError(opts, events)
if err != nil {
return nil, err
}
return events, nil
}
func (e *Exchange) GetErrorEvents(logs chan *interfaces.ExchangeLogError) error {
opts := &bind.WatchOpts{nil, nil}
_, err := e.Contract.WatchLogError(opts, logs)
if err != nil {
return err
}
return nil
}
// ListenToTrades returns a channel that receivs trade logs (events) from the underlying exchange smart contract
func (e *Exchange) ListenToTrades() (chan *interfaces.ExchangeLogTrade, error) {
events := make(chan *interfaces.ExchangeLogTrade)
opts := &bind.WatchOpts{nil, nil}
_, err := e.Contract.WatchLogTrade(opts, events)
if err != nil {
return nil, err
}
return events, nil
}
func (e *Exchange) GetTrades(logs chan *interfaces.ExchangeLogTrade) error {
opts := &bind.WatchOpts{nil, nil}
_, err := e.Contract.WatchLogTrade(opts, logs)
if err != nil {
return err
}
return nil
}
// ListenToTrades returns a channel that receivs deposit logs (events) from the underlying exchange smart contract
func (e *Exchange) ListenToDeposits() (chan *interfaces.ExchangeLogDeposit, error) {
events := make(chan *interfaces.ExchangeLogDeposit)
opts := &bind.WatchOpts{nil, nil}
_, err := e.Contract.WatchLogDeposit(opts, events)
if err != nil {
return nil, err
}
return events, nil
}
func (e *Exchange) PrintTrades() error {
events := make(chan *interfaces.ExchangeLogTrade)
opts := &bind.WatchOpts{nil, nil}
_, err := e.Contract.WatchLogTrade(opts, events)
if err != nil {
return err
}
go func() {
for {
event := <-events
fmt.Printf("New event: %v", event)
}
}()
return nil
}
func (e *Exchange) PrintErrors() error {
events := make(chan *interfaces.ExchangeLogError)
opts := &bind.WatchOpts{nil, nil}
_, err := e.Contract.WatchLogError(opts, events)
if err != nil {
return err
}
go func() {
for {
event := <-events
log.Printf("New Error Event. Id: %v, Hash: %v\n\n", event.ErrorId, hex.EncodeToString(event.OrderHash[:]))
}
}()
return nil
}
func PrintErrorLog(log *interfaces.ExchangeLogError) string {
return fmt.Sprintf("Error:\nErrorID: %v\nOrderHash: %v\n\n", log.ErrorId, log.OrderHash)
}
func PrintTradeLog(log *interfaces.ExchangeLogTrade) string {
return fmt.Sprintf("Error:\nAmount: %v\nMaker: %v\nTaker: %v\nTokenBuy: %v\nTokenSell: %v\nOrderHash: %v\nTradeHash: %v\n\n",
log.Amount, log.Maker, log.Taker, log.TokenBuy, log.TokenSell, log.OrderHash, log.TradeHash)
}
func PrintCancelOrderLog(log *interfaces.ExchangeLogCancelOrder) string {
return fmt.Sprintf("Error:\nSender: %v\nOrderHash: %v\n\n", log.Sender, log.OrderHash)
}
func PrintCancelTradeLog(log *interfaces.ExchangeLogCancelTrade) string {
return fmt.Sprintf("Error:\nSender: %v\nTradeHash: %v\n\n", log.Sender, log.TradeHash)
}
func PrintWithdrawalErrorLog(log *interfaces.ExchangeLogWithdrawalError) string {
return fmt.Sprintf("Error:\nError ID: %v\n, WithdrawalHash: %v\n\n", log.ErrorId, log.WithdrawalHash)
}