-
Notifications
You must be signed in to change notification settings - Fork 89
/
types.go
895 lines (802 loc) · 28 KB
/
types.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
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
// This code is available on the terms of the project LICENSE.md file,
// also available online at https://blueoakcouncil.org/license/1.0.0.
package msgjson
import (
"encoding/binary"
"encoding/json"
"fmt"
"decred.org/dcrdex/dex"
)
// Error codes
const (
RPCErrorUnspecified = iota // 0
RPCParseError // 1
RPCUnknownRoute // 2
RPCInternal // 3
RPCQuarantineClient // 4
RPCVersionUnsupported // 5
RPCUnknownMatch // 6
RPCInternalError // 7
SignatureError // 8
SerializationError // 9
TransactionUndiscovered // 10
ContractError // 11
SettlementSequenceError // 12
ResultLengthError // 13
IDMismatchError // 14
RedemptionError // 15
IDTypeError // 16
AckCountError // 17
UnknownResponseID // 18
OrderParameterError // 19
UnknownMarketError // 20
ClockRangeError // 21
FundingError // 22
CoinAuthError // 23
UnknownMarket // 24
NotSubscribedError // 25
UnauthorizedConnection // 26
AuthenticationError // 27
PubKeyParseError // 28
FeeError // 29
InvalidPreimage // 30
PreimageCommitmentMismatch // 31
UnknownMessageType // 32
AccountClosedError // 33
RPCInitError // 34
RPCCreateWalletError // 35
RPCOpenWalletError // 36
RPCWalletExistsError // 37
RPCCloseWalletError // 38
RPCGetFeeError // 39
RPCRegisterError // 40
RPCArgumentsError // 41
MarketNotRunningError // 42
)
// Routes are destinations for a "payload" of data. The type of data being
// delivered, and what kind of action is expected from the receiving party, is
// completely dependent on the route. The route designation is a string sent as
// the "route" parameter of a JSON-encoded Message.
const (
// MatchRoute is the route of a DEX-originating request-type message notifying
// the client of a match and initiating swap negotiation.
MatchRoute = "match"
// InitRoute is the route of a client-originating request-type message
// notifying the DEX, and subsequently the match counter-party, of the details
// of a swap contract.
InitRoute = "init"
// AuditRoute is the route of a DEX-originating request-type message relaying
// swap contract details (from InitRoute) from one client to the other.
AuditRoute = "audit"
// RedeemRoute is the route of a client-originating request-type message
// notifying the DEX, and subsequently the match counter-party, of the details
// of a redemption transaction.
RedeemRoute = "redeem"
// RedemptionRoute is the route of a DEX-originating request-type message
// relaying redemption transaction (from RedeemRoute) details from one client
// to the other.
RedemptionRoute = "redemption"
// RevokeMatchRoute is a DEX-originating request-type message informing a
// client that a match has been revoked.
RevokeMatchRoute = "revoke_match"
// LimitRoute is the client-originating request-type message placing a limit
// order.
LimitRoute = "limit"
// MarketRoute is the client-originating request-type message placing a market
// order.
MarketRoute = "market"
// CancelRoute is the client-originating request-type message placing a cancel
// order.
CancelRoute = "cancel"
// OrderBookRoute is the client-originating request-type message subscribing
// to an order book update notification feed.
OrderBookRoute = "orderbook"
// UnsubOrderBookRoute is client-originating request-type message cancelling
// an order book subscription.
UnsubOrderBookRoute = "unsub_orderbook"
// BookOrderRoute is the DEX-originating notification-type message informing
// the client to add the order to the order book.
BookOrderRoute = "book_order"
// UnbookOrderRoute is the DEX-originating notification-type message informing
// the client to remove an order from the order book.
UnbookOrderRoute = "unbook_order"
// EpochOrderRoute is the DEX-originating notification-type message informing
// the client about an order added to the epoch queue.
EpochOrderRoute = "epoch_order"
// ConnectRoute is a client-originating request-type message seeking
// authentication so that the connection can be used for trading.
ConnectRoute = "connect"
// RegisterRoute is the client-originating request-type message initiating a
// new client registration.
RegisterRoute = "register"
// NotifyFeeRoute is the client-originating request-type message informing the
// DEX that the fee has been paid and has the requisite number of
// confirmations.
NotifyFeeRoute = "notifyfee"
// ConfigRoute is the client-originating request-type message requesting the
// DEX configuration information.
ConfigRoute = "config"
// MatchProofRoute is the DEX-originating notification-type message
// delivering match cycle results to the client.
MatchProofRoute = "match_proof"
// PreimageRoute is the DEX-originating request-type message requesting the
// preimages for the client's epoch orders.
PreimageRoute = "preimage"
// SuspensionRoute is the DEX-originating request-type message informing the
// client of an upcoming trade suspension. This is part of the
// subscription-based orderbook notification feed.
SuspensionRoute = "suspension"
// ResumptionRoute is the DEX-originating request-type message informing the
// client of an upcoming trade resumption. This is part of the
// subscription-based orderbook notification feed.
ResumptionRoute = "resumption"
)
type Bytes = dex.Bytes
// Signable allows for serialization and signing.
type Signable interface {
Serialize() ([]byte, error)
SetSig([]byte)
SigBytes() []byte
}
// signable partially implements Signable, and can be embedded by types intended
// to satisfy Signable, which must themselves implement the Serialize method.
type signable struct {
Sig Bytes `json:"sig"`
}
// SetSig sets the Sig field.
func (s *signable) SetSig(b []byte) {
s.Sig = b
}
// SigBytes returns the signature as a []byte.
func (s *signable) SigBytes() []byte {
// Assuming the Sig was set with SetSig, there is likely no way to error
// here. Ignoring error for now.
return s.Sig
}
// Stampable is an interface that supports timestamping and signing.
type Stampable interface {
Signable
Stamp(serverTime uint64)
}
// Acknowledgement is the 'result' field in a response to a request that
// requires an acknowledgement. It is typically a signature of some serialized
// data associated with the request.
type Acknowledgement struct {
MatchID Bytes `json:"matchid"`
Sig Bytes `json:"sig"`
}
// Error is returned as part of the Response to indicate that an error
// occurred during method execution.
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}
// String satisfies the Stringer interface for pretty printing.
func (e Error) String() string {
return fmt.Sprintf("error code %d: %s", e.Code, e.Message)
}
// NewError is a constructor for an Error.
func NewError(code int, msg string) *Error {
return &Error{
Code: code,
Message: msg,
}
}
// ResponsePayload is the payload for a Response-type Message.
type ResponsePayload struct {
// Result is the payload, if successful, else nil.
Result json.RawMessage `json:"result,omitempty"`
// Error is the error, or nil if none was encountered.
Error *Error `json:"error,omitempty"`
}
// MessageType indicates the type of message. MessageType is typically the first
// switch checked when examining a message, and how the rest of the message is
// decoded depends on its MessageType.
type MessageType uint8
const (
InvalidMessageType MessageType = iota // 0
Request // 1
Response // 2
Notification // 3
)
// String satisfies the Stringer interface for translating the MessageType code
// into a description, primarily for logging.
func (mt MessageType) String() string {
switch mt {
case Request:
return "request"
case Response:
return "response"
case Notification:
return "notification"
default:
return "unknown MessageType"
}
}
// Message is the primary messaging type for websocket communications.
type Message struct {
// Type is the message type.
Type MessageType `json:"type"`
// Route is used for requests and notifications, and specifies a handler for
// the message.
Route string `json:"route,omitempty"`
// ID is a unique number that is used to link a response to a request.
ID uint64 `json:"id,omitempty"`
// Payload is any data attached to the message. How Payload is decoded
// depends on the Route.
Payload json.RawMessage `json:"payload,omitempty"`
}
// DecodeMessage decodes a *Message from JSON-formatted bytes.
func DecodeMessage(b []byte) (*Message, error) {
msg := new(Message)
err := json.Unmarshal(b, &msg)
if err != nil {
return nil, err
}
return msg, nil
}
// NewRequest is the constructor for a Request-type *Message.
func NewRequest(id uint64, route string, payload interface{}) (*Message, error) {
if id == 0 {
return nil, fmt.Errorf("id = 0 not allowed for a request-type message")
}
if route == "" {
return nil, fmt.Errorf("empty string not allowed for route of request-type message")
}
encoded, err := json.Marshal(payload)
if err != nil {
return nil, err
}
return &Message{
Type: Request,
Payload: json.RawMessage(encoded),
Route: route,
ID: id,
}, nil
}
// NewResponse encodes the result and creates a Response-type *Message.
func NewResponse(id uint64, result interface{}, rpcErr *Error) (*Message, error) {
if id == 0 {
return nil, fmt.Errorf("id = 0 not allowed for response-type message")
}
encResult, err := json.Marshal(result)
if err != nil {
return nil, err
}
resp := &ResponsePayload{
Result: encResult,
Error: rpcErr,
}
encResp, err := json.Marshal(resp)
if err != nil {
return nil, err
}
return &Message{
Type: Response,
Payload: json.RawMessage(encResp),
ID: id,
}, nil
}
// Response attempts to decode the payload to a *ResponsePayload. Response will
// return an error if the Type is not Response.
func (msg *Message) Response() (*ResponsePayload, error) {
if msg.Type != Response {
return nil, fmt.Errorf("invalid type %d for ResponsePayload", msg.Type)
}
resp := new(ResponsePayload)
err := json.Unmarshal(msg.Payload, &resp)
if err != nil {
return nil, err
}
return resp, nil
}
// NewNotification encodes the payload and creates a Notification-type *Message.
func NewNotification(route string, payload interface{}) (*Message, error) {
if route == "" {
return nil, fmt.Errorf("empty string not allowed for route of notification-type message")
}
encPayload, err := json.Marshal(payload)
if err != nil {
return nil, err
}
return &Message{
Type: Notification,
Route: route,
Payload: json.RawMessage(encPayload),
}, nil
}
// Unmarshal unmarshals the Payload field into the provided interface.
func (msg *Message) Unmarshal(payload interface{}) error {
return json.Unmarshal(msg.Payload, payload)
}
// UnmarshalResult is a convenience method for decoding the Result field of a
// ResponsePayload.
func (msg *Message) UnmarshalResult(result interface{}) error {
resp, err := msg.Response()
if err != nil {
return err
}
if resp.Error != nil {
return fmt.Errorf("rpc error: %d: %s", resp.Error.Code, resp.Error.Message)
}
return json.Unmarshal(resp.Result, result)
}
// String prints the message as a JSON-encoded string.
func (msg *Message) String() string {
b, err := json.Marshal(msg)
if err != nil {
return "[Message decode error]"
}
return string(b)
}
// Match is the params for a DEX-originating MatchRoute request.
type Match struct {
signable
OrderID Bytes `json:"orderid"`
MatchID Bytes `json:"matchid"`
Quantity uint64 `json:"quantity"`
Rate uint64 `json:"rate"`
Address string `json:"address"`
ServerTime uint64 `json:"tserver"`
// Status and Side are provided for convenience and are not part of the
// match serialization.
Status uint8 `json:"status"`
Side uint8 `json:"side"`
}
var _ Stampable = (*Match)(nil)
// Serialize serializes the Match data.
func (m *Match) Serialize() ([]byte, error) {
// Match serialization is orderid (32) + matchid (32) + quantity (8) + rate (8)
// + server time (8) + address (variable, guess 35). Sum = 123
s := make([]byte, 0, 123)
s = append(s, m.OrderID...)
s = append(s, m.MatchID...)
s = append(s, uint64Bytes(m.Quantity)...)
s = append(s, uint64Bytes(m.Rate)...)
s = append(s, uint64Bytes(m.ServerTime)...)
s = append(s, []byte(m.Address)...)
return s, nil
}
// Stamp sets the server timestamp and epoch ID. Partially satisfies the
// Stampable interface.
func (m *Match) Stamp(t uint64) {
m.ServerTime = t
}
// Init is the payload for a client-originating InitRoute request.
type Init struct {
signable
OrderID Bytes `json:"orderid"`
MatchID Bytes `json:"matchid"`
CoinID Bytes `json:"coinid"`
// Time uint64 `json:"timestamp"` // client originating
Contract Bytes `json:"contract"`
}
var _ Signable = (*Init)(nil)
// Serialize serializes the Init data.
func (init *Init) Serialize() ([]byte, error) {
// Init serialization is orderid (32) + matchid (32) + txid (probably 32) +
// vout (4) + contract (97 ish). Sum = 197
s := make([]byte, 0, 197)
s = append(s, init.OrderID...)
s = append(s, init.MatchID...)
s = append(s, init.CoinID...)
//s = append(s, uint64Bytes(init.Time)...)
s = append(s, init.Contract...)
return s, nil
}
// Audit is the payload for a DEX-originating AuditRoute request.
type Audit struct {
signable
OrderID Bytes `json:"orderid"`
MatchID Bytes `json:"matchid"`
Time uint64 `json:"timestamp"`
CoinID Bytes `json:"coinid"`
Contract Bytes `json:"contract"`
}
var _ Signable = (*Audit)(nil)
// Serialize serializes the Audit data.
func (audit *Audit) Serialize() ([]byte, error) {
// Audit serialization is orderid (32) + matchid (32) + time (8) +
// coin ID (36) + contract (97 ish) = 205
s := make([]byte, 0, 205)
s = append(s, audit.OrderID...)
s = append(s, audit.MatchID...)
s = append(s, uint64Bytes(audit.Time)...)
s = append(s, audit.CoinID...)
s = append(s, audit.Contract...)
return s, nil
}
// RevokeMatch are the params for a DEX-originating RevokeMatchRoute request.
type RevokeMatch struct {
signable
OrderID Bytes `json:"orderid"`
MatchID Bytes `json:"matchid"`
}
var _ Signable = (*RevokeMatch)(nil)
// Serialize serializes the RevokeMatchParams data.
func (rev *RevokeMatch) Serialize() ([]byte, error) {
// RevokeMatch serialization is order id (32) + match id (32) = 64 bytes
s := make([]byte, 0, 64)
s = append(s, rev.OrderID...)
s = append(s, rev.MatchID...)
return s, nil
}
// Redeem are the params for a client-originating RedeemRoute request.
type Redeem struct {
signable
OrderID Bytes `json:"orderid"`
MatchID Bytes `json:"matchid"`
CoinID Bytes `json:"coinid"`
Secret Bytes `json:"secret"`
// Time uint64 `json:"timestamp"`
}
var _ Signable = (*Redeem)(nil)
// Serialize serializes the Redeem data.
func (redeem *Redeem) Serialize() ([]byte, error) {
// Redeem serialization is orderid (32) + matchid (32) + coin ID (36) + secret
// (32) = 132
s := make([]byte, 0, 100)
s = append(s, redeem.OrderID...)
s = append(s, redeem.MatchID...)
s = append(s, redeem.CoinID...)
//s = append(s, uint64Bytes(redeem.Time)...)
return append(s, redeem.Secret...), nil
}
// Redemption is the payload for a DEX-originating RedemptionRoute request.
type Redemption struct {
Redeem
Time uint64 `json:"timestamp"`
}
// Serialize serializes the Redemption data.
func (r *Redemption) Serialize() ([]byte, error) {
// Redemption serialization is Redeem (100) + timestamp (8) = 108
s, _ := r.Redeem.Serialize()
return append(s, uint64Bytes(r.Time)...), nil
}
const (
BuyOrderNum = 1
SellOrderNum = 2
StandingOrderNum = 1
ImmediateOrderNum = 2
LimitOrderNum = 1
MarketOrderNum = 2
CancelOrderNum = 3
)
// Coin is information for validating funding coins. Some number of
// Coins must be included with both Limit and Market payloads.
type Coin struct {
ID Bytes `json:"coinid"`
PubKeys []Bytes `json:"pubkeys"`
Sigs []Bytes `json:"sigs"`
Redeem Bytes `json:"redeem"`
}
// Prefix is a common structure shared among order type payloads.
type Prefix struct {
signable
AccountID Bytes `json:"accountid"`
Base uint32 `json:"base"`
Quote uint32 `json:"quote"`
OrderType uint8 `json:"ordertype"`
ClientTime uint64 `json:"tclient"`
ServerTime uint64 `json:"tserver"`
Commit Bytes `json:"com"`
}
// Stamp sets the server timestamp and epoch ID. Partially satisfies the
// Stampable interface.
func (p *Prefix) Stamp(t uint64) {
p.ServerTime = t
}
// TODO: Update prefix serialization with commitment.
// Serialize serializes the Prefix data.
func (p *Prefix) Serialize() []byte {
// serialization: account ID (32) + base asset (4) + quote asset (4) +
// order type (1) + client time (8) + server time (8) + commitment (32)
// = 89 bytes
b := make([]byte, 0, 89)
b = append(b, p.AccountID...)
b = append(b, uint32Bytes(p.Base)...)
b = append(b, uint32Bytes(p.Quote)...)
b = append(b, p.OrderType)
b = append(b, uint64Bytes(p.ClientTime)...)
b = append(b, uint64Bytes(p.ServerTime)...)
return append(b, p.Commit...)
}
// Trade is common to Limit and Market Payloads.
type Trade struct {
Side uint8 `json:"side"`
Quantity uint64 `json:"ordersize"`
Coins []*Coin `json:"coins"`
Address string `json:"address"`
}
// Serialize serializes the Trade data.
func (t *Trade) Serialize() []byte {
// serialization: coin count (1), coin data (36*count), side (1), qty (8)
// = 10 + 36*count
// Address is not serialized as part of the trade.
coinCount := len(t.Coins)
b := make([]byte, 0, 10+36*coinCount)
b = append(b, byte(coinCount))
for _, coin := range t.Coins {
b = append(b, coin.ID...)
}
b = append(b, t.Side)
return append(b, uint64Bytes(t.Quantity)...)
// Note that Address is part of LimitOrder and MarketOrder serialization.
}
// LimitOrder is the payload for the LimitRoute, which places a limit order.
type LimitOrder struct {
Prefix
Trade
Rate uint64 `json:"rate"`
TiF uint8 `json:"timeinforce"`
}
// Serialize serializes the Limit data.
func (l *LimitOrder) Serialize() ([]byte, error) {
// serialization: prefix (89) + trade (variable) + rate (8)
// + time-in-force (1) + address (~35) = 133 + len(trade)
trade := l.Trade.Serialize()
b := make([]byte, 0, 133+len(trade))
b = append(b, l.Prefix.Serialize()...)
b = append(b, trade...)
b = append(b, uint64Bytes(l.Rate)...)
b = append(b, l.TiF)
return append(b, []byte(l.Trade.Address)...), nil
}
// MarketOrder is the payload for the MarketRoute, which places a market order.
type MarketOrder struct {
Prefix
Trade
}
// Serialize serializes the MarketOrder data.
func (m *MarketOrder) Serialize() ([]byte, error) {
// serialization: prefix (89) + trade (varies) + address (35 ish)
b := append(m.Prefix.Serialize(), m.Trade.Serialize()...)
return append(b, []byte(m.Trade.Address)...), nil
}
// CancelOrder is the payload for the CancelRoute, which places a cancel order.
type CancelOrder struct {
Prefix
TargetID Bytes `json:"targetid"`
}
// Serialize serializes the CancelOrder data.
func (c *CancelOrder) Serialize() ([]byte, error) {
// serialization: prefix (89) + target id (32) = 121
return append(c.Prefix.Serialize(), c.TargetID...), nil
}
// OrderResult is returned from the order-placing routes.
type OrderResult struct {
Sig Bytes `json:"sig"`
OrderID Bytes `json:"orderid"`
ServerTime uint64 `json:"tserver"`
}
// OrderBookSubscription is the payload for a client-originating request to the
// OrderBookRoute, intializing an order book feed.
type OrderBookSubscription struct {
Base uint32 `json:"base"`
Quote uint32 `json:"quote"`
}
// UnsubOrderBook is the payload for a client-originating request to the
// UnsubOrderBookRoute, terminating an order book subscription.
type UnsubOrderBook struct {
MarketID string `json:"marketid"`
}
// orderbook subscription notification payloads include: BookOrderNote,
// UnbookOrderNote, EpochOrderNote, and MatchProofNote.
// OrderNote is part of a notification about any type of order.
type OrderNote struct {
Seq uint64 `json:"seq,omitempty"` // May be empty when part of an OrderBook.
MarketID string `json:"marketid,omitempty"` // May be empty when part of an OrderBook.
OrderID Bytes `json:"oid"`
}
// TradeNote is part of a notification that includes information about a
// limit or market order.
type TradeNote struct {
Side uint8 `json:"side,omitempty"`
Quantity uint64 `json:"osize,omitempty"`
Rate uint64 `json:"rate,omitempty"`
TiF uint8 `json:"tif,omitempty"`
Time uint64 `json:"time,omitempty"`
}
// BookOrderNote is the payload for a DEX-originating notification-type message
// informing the client to add the order to the order book.
type BookOrderNote struct {
OrderNote
TradeNote
}
// UnbookOrderRoute is the DEX-originating notification-type message informing
// the client to remove an order from the order book.
type UnbookOrderNote OrderNote
// EpochOrderRoute is the DEX-originating notification-type message informing
// the client about an order added to the epoch queue.
type EpochOrderNote struct {
BookOrderNote
Commit Bytes `json:"com"`
OrderType uint8 `json:"otype"`
Epoch uint64 `json:"epoch"`
TargetID Bytes `json:"target,omitempty"` // omit for cancel orders
}
// OrderBook is the response to a successful OrderBookSubscription.
type OrderBook struct {
MarketID string `json:"marketid"`
Seq uint64 `json:"seq"`
Epoch uint64 `json:"epoch"`
// MarketStatus `json:"status"`// maybe
// DRAFT NOTE: We might want to use a different structure for bulk updates.
// Sending a struct of arrays rather than an array of structs could
// potentially cut the encoding effort and encoded size substantially.
Orders []*BookOrderNote `json:"orders"`
}
// MatchProofNote is the match_proof notification payload.
type MatchProofNote struct {
MarketID string `json:"marketid"`
Epoch uint64 `json:"epoch"`
Preimages []Bytes `json:"preimages"`
Misses []Bytes `json:"misses"`
CSum Bytes `json:"csum"`
Seed Bytes `json:"seed"`
}
// TradeSuspension is the SuspensionRoute notification payload. It is part of
// the orderbook subscription.
type TradeSuspension struct {
MarketID string `json:"marketid"`
FinalEpoch uint64 `json:"finalepoch"`
SuspendTime uint64 `json:"suspendtime"`
Persist bool `json:"persistbook"`
}
// TradeResumption is the ResumptionRoute notification payload. It is part of
// the orderbook subscription. EpochLen is specified if the market configuration
// change, and the client should also hit the 'config' route for full details.
type TradeResumption struct {
MarketID string `json:"marketid"`
StartEpoch uint64 `json:"startepoch"`
EpochLen uint64 `json:"epochlen,omitempty"` // maybe just ConfigChange bool `json:"configchange"`
}
// PreimageRequest is the server-originating preimage request payload.
type PreimageRequest struct {
OrderID Bytes `json:"orderid"`
CommitChecksum Bytes `json:"csum"`
}
// PreimageResponse is the client-originating preimage response payload.
type PreimageResponse struct {
Preimage Bytes `json:"pimg"`
}
// Connect is the payload for a client-originating ConnectRoute request.
type Connect struct {
signable
AccountID Bytes `json:"accountid"`
APIVersion uint16 `json:"apiver"`
Time uint64 `json:"timestamp"`
}
// Serialize serializes the Connect data.
func (c *Connect) Serialize() ([]byte, error) {
// serialization: account ID (32) + api version (2) + timestamp (8) = 42 bytes
s := make([]byte, 0, 42)
s = append(s, c.AccountID...)
s = append(s, uint16Bytes(c.APIVersion)...)
s = append(s, uint64Bytes(c.Time)...)
return s, nil
}
// ConnectResult is the result result for the ConnectRoute request.
type ConnectResult struct {
Matches []*Match `json:"matches"`
}
// Register is the payload for the RegisterRoute request.
type Register struct {
signable
PubKey Bytes `json:"pubkey"`
Time uint64 `json:"timestamp"`
}
// Serialize serializes the Register data.
func (r *Register) Serialize() ([]byte, error) {
// serialization: pubkey (33) + time (8) = 41
s := make([]byte, 0, 41)
s = append(s, r.PubKey...)
s = append(s, uint64Bytes(r.Time)...)
return s, nil
}
// RegisterResult is the result for the response to Register.
type RegisterResult struct {
signable
DEXPubKey Bytes `json:"pubkey"`
ClientPubKey Bytes `json:"-"`
Address string `json:"address"`
Fee uint64 `json:"fee"`
Time uint64 `json:"timestamp"`
}
// Serialize serializes the RegisterResult data.
func (r *RegisterResult) Serialize() ([]byte, error) {
// serialization: pubkey (33) + client pubkey (33) + time (8) + fee (8) +
// address (35-ish) = 117
b := make([]byte, 0, 117)
b = append(b, r.DEXPubKey...)
b = append(b, r.ClientPubKey...)
b = append(b, uint64Bytes(r.Time)...)
b = append(b, uint64Bytes(r.Fee)...)
b = append(b, []byte(r.Address)...)
return b, nil
}
// NotifyFee is the payload for a client-originating NotifyFeeRoute request.
type NotifyFee struct {
signable
AccountID Bytes `json:"accountid"`
CoinID Bytes `json:"coinid"`
Time uint64 `json:"timestamp"`
}
// Serialize serializes the NotifyFee data.
func (n *NotifyFee) Serialize() ([]byte, error) {
// serialization: account id (32) + coinID (variable, ~32+2) + vout (4) +
// time (8) = 78
b := make([]byte, 0, 68)
b = append(b, n.AccountID...)
b = append(b, n.CoinID...)
b = append(b, uint64Bytes(n.Time)...)
return b, nil
}
// Stamp satisfies the Stampable interface.
func (n *NotifyFee) Stamp(t uint64) {
n.Time = t
}
// NotifyFeeResult is the result for the response to NotifyFee. Though it embeds
//signable, it does not satisfy the Signable interface, as it has no need for
// serialization.
type NotifyFeeResult struct {
signable
}
// MarketStatus describes the status of the market, where StartEpoch is when the
// market started or will start. FinalEpoch is a when the market will suspend
// if it is running, or when the market suspended if it is presently stopped.
type MarketStatus struct {
StartEpoch uint64 `json:"startepoch"`
FinalEpoch uint64 `json:"finalepoch,omitempty"`
Persist *bool `json:"persistbook,omitempty"` // nil and omitted when finalepoch is omitted
}
// Market describes a market and its variables, and is returned as part of a
// ConfigResult. The market's status (running, start epoch, and any planned
// final epoch before suspend) are also provided.
type Market struct {
Name string `json:"name"`
Base uint32 `json:"base"`
Quote uint32 `json:"quote"`
EpochLen uint64 `json:"epochlen"`
MarketBuyBuffer float64 `json:"buybuffer"`
MarketStatus `json:"status"`
}
// Asset describes an asset and its variables, and is returned as part of a
// ConfigResult.
type Asset struct {
Symbol string `json:"symbol"`
ID uint32 `json:"id"`
LotSize uint64 `json:"lotsize"`
RateStep uint64 `json:"ratestep"`
FeeRate uint64 `json:"feerate"`
SwapSize uint64 `json:"swapsize"`
SwapConf uint16 `json:"swapconf"`
FundConf uint16 `json:"fundconf"`
}
// ConfigResult is the successful result for the ConfigRoute.
type ConfigResult struct {
CancelMax float64 `json:"cancelmax"`
BroadcastTimeout uint64 `json:"btimeout"`
RegFeeConfirms uint16 `json:"regfeeconfirms"`
Assets []*Asset `json:"assets"`
Markets []*Market `json:"markets"`
Fee uint64 `json:"fee"`
}
// Convert uint64 to 8 bytes.
func uint64Bytes(i uint64) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, i)
return b
}
// Convert uint32 to 4 bytes.
func uint32Bytes(i uint32) []byte {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, i)
return b
}
// Convert uint32 to 4 bytes.
func uint16Bytes(i uint16) []byte {
b := make([]byte, 2)
binary.BigEndian.PutUint16(b, i)
return b
}