-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathbtcavg.go
171 lines (146 loc) · 3.6 KB
/
btcavg.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
package ticker
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"sync"
"time"
)
const (
btcavgFiatEndpoint = "https://apiv2.bitcoinaverage.com/indices/global/ticker/all?crypto=BTC"
btcavgCryptoEndpoint = "https://apiv2.bitcoinaverage.com/indices/crypto/ticker/all"
)
type btcavgFetcher struct {
pubkey string
privkey string
}
func NewBTCAVGFetcher(pubkey string, privkey string) fetchFn {
return func() (exchangeRates, error) {
output := exchangeRates{}
errCh := make(chan error, 2)
// Request both endpoints and save their responses
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
rates, err := fetchBTCAVGResource(btcavgFiatEndpoint, pubkey, privkey)
if err != nil {
errCh <- err
return
}
formatBTCAVGFiatOutput(output, rates)
}()
go func() {
defer wg.Done()
rates, err := fetchBTCAVGResource(btcavgCryptoEndpoint, pubkey, privkey)
if err != nil {
errCh <- err
return
}
err = formatBTCAVGCryptoOutput(output, rates)
if err != nil {
errCh <- err
return
}
}()
wg.Wait()
close(errCh)
for err := range errCh {
return nil, err
}
return output, nil
}
}
// fetchBTCAVGResource gets the response for a given BitcoinAverage endpoint
func fetchBTCAVGResource(url string, pubkey string, privkey string) (exchangeRates, error) {
// Create signed request
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
if privkey != "" {
req.Header.Add("X-signature", createBTCAVGSignature(pubkey, privkey))
} else {
req.Header.Add("X-testing", "testing")
}
// Send the requests
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Read the response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Return an error if no success, otherwise deserialize and return our body
if resp.StatusCode != 200 {
return nil, err
}
rates := make(exchangeRates)
err = json.Unmarshal(body, &rates)
if err != nil {
return nil, err
}
return rates, nil
}
// formatBTCAVGFiatOutput formats BTC->fiat pairs
func formatBTCAVGFiatOutput(outgoing exchangeRates, incoming exchangeRates) {
for k, v := range incoming {
if strings.HasPrefix(k, "BTC") {
v.Type = exchangeRateTypeFiat.String()
outgoing[strings.TrimPrefix(k, "BTC")] = v
}
}
}
// formatBTCAVGCryptoOutput formats BTC->crypto pairs
func formatBTCAVGCryptoOutput(outgoing exchangeRates, incoming exchangeRates) error {
for symbol, entry := range incoming {
trimmedSymbol := strings.TrimSuffix(symbol, "BTC")
if symbol == trimmedSymbol {
continue
}
symbol := CanonicalizeSymbol(trimmedSymbol)
if !IsCorrectIDForSymbol(symbol, entry.ID) {
continue
}
if entry.Ask == "" || entry.Bid == "" || entry.Last == "" {
continue
}
ask, err := invertAndFormatPrice(entry.Ask)
if err != nil {
return err
}
bid, err := invertAndFormatPrice(entry.Bid)
if err != nil {
return err
}
last, err := invertAndFormatPrice(entry.Last)
if err != nil {
return err
}
outgoing[symbol] = exchangeRate{
Ask: ask,
Bid: bid,
Last: last,
Type: exchangeRateTypeCrypto.String(),
}
}
return nil
}
func createBTCAVGSignature(pubkey string, privkey string) string {
// Build payload
payload := fmt.Sprintf("%d.%s", time.Now().Unix(), pubkey)
// Generate the HMAC-sha256 signature
mac := hmac.New(sha256.New, []byte(privkey))
mac.Write([]byte(payload))
signature := hex.EncodeToString(mac.Sum(nil))
// Return the final payload
return fmt.Sprintf("%s.%s", payload, signature)
}