-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathaddresses.go
281 lines (246 loc) · 7.95 KB
/
addresses.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
package whatsonchain
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
)
// AddressInfo this endpoint retrieves various address info.
//
// For more information: https://developers.whatsonchain.com/#address
func (c *Client) AddressInfo(ctx context.Context, address string) (addressInfo *AddressInfo, err error) {
var resp string
// https://api.whatsonchain.com/v1/bsv/<network>/address/<address>/info
if resp, err = c.request(
ctx,
fmt.Sprintf("%s%s/address/%s/info", apiEndpoint, c.Network(), address),
http.MethodGet, nil,
); err != nil {
return
}
if len(resp) == 0 {
return nil, ErrAddressNotFound
}
err = json.Unmarshal([]byte(resp), &addressInfo)
return
}
// AddressBalance this endpoint retrieves confirmed and unconfirmed address balance.
//
// For more information: https://developers.whatsonchain.com/#get-balance
func (c *Client) AddressBalance(ctx context.Context, address string) (balance *AddressBalance, err error) {
var resp string
// https://api.whatsonchain.com/v1/bsv/<network>/address/<address>/balance
if resp, err = c.request(
ctx,
fmt.Sprintf("%s%s/address/%s/balance", apiEndpoint, c.Network(), address),
http.MethodGet, nil,
); err != nil {
return
}
if len(resp) == 0 {
return nil, ErrAddressNotFound
}
err = json.Unmarshal([]byte(resp), &balance)
return
}
// AddressHistory this endpoint retrieves confirmed and unconfirmed address transactions.
//
// For more information: https://developers.whatsonchain.com/#get-history
func (c *Client) AddressHistory(ctx context.Context, address string) (history AddressHistory, err error) {
var resp string
// https://api.whatsonchain.com/v1/bsv/<network>/address/<address>/history
if resp, err = c.request(
ctx,
fmt.Sprintf("%s%s/address/%s/history", apiEndpoint, c.Network(), address),
http.MethodGet, nil,
); err != nil {
return
}
if len(resp) == 0 {
return nil, ErrAddressNotFound
}
err = json.Unmarshal([]byte(resp), &history)
return
}
// AddressUnspentTransactions this endpoint retrieves ordered list of UTXOs.
//
// For more information: https://developers.whatsonchain.com/#get-unspent-transactions
func (c *Client) AddressUnspentTransactions(ctx context.Context, address string) (history AddressHistory, err error) {
var resp string
// https://api.whatsonchain.com/v1/bsv/<network>/address/<address>/unspent
if resp, err = c.request(
ctx,
fmt.Sprintf("%s%s/address/%s/unspent", apiEndpoint, c.Network(), address),
http.MethodGet, nil,
); err != nil {
return
}
if len(resp) == 0 {
return nil, ErrAddressNotFound
}
err = json.Unmarshal([]byte(resp), &history)
return
}
// AddressUnspentTransactionDetails this endpoint retrieves transaction details for a given address
// Use max transactions to filter if there are more UTXOs returned than needed by the user
//
// For more information: (custom request for this go package)
func (c *Client) AddressUnspentTransactionDetails(ctx context.Context, address string, maxTransactions int) (history AddressHistory, err error) {
// Get the address UTXO history
var utxos AddressHistory
if utxos, err = c.AddressUnspentTransactions(ctx, address); err != nil {
return
} else if len(utxos) == 0 {
return
}
// Do we have a "custom max" amount?
if maxTransactions > 0 {
total := len(utxos)
if total > maxTransactions {
utxos = utxos[:total-(total-maxTransactions)]
}
}
// Break up the UTXOs into batches
var batches []AddressHistory
chunkSize := MaxTransactionsUTXO
for i := 0; i < len(utxos); i += chunkSize {
end := i + chunkSize
if end > len(utxos) {
end = len(utxos)
}
batches = append(batches, utxos[i:end])
}
// todo: use channels/wait group to fire all requests at the same time (rate limiting)
// Loop Batches - and get each batch (multiple batches of MaxTransactionsUTXO)
for _, batch := range batches {
txHashes := new(TxHashes)
// Loop the batch (max MaxTransactionsUTXO)
for _, utxo := range batch {
// Append to the list to send and return
txHashes.TxIDs = append(txHashes.TxIDs, utxo.TxHash)
history = append(history, utxo)
}
// Get the tx details (max of MaxTransactionsUTXO)
var txList TxList
if txList, err = c.BulkTransactionDetails(ctx, txHashes); err != nil {
return
}
// Add to the history list
for index, tx := range txList {
for _, utxo := range history {
if utxo.TxHash == tx.TxID {
utxo.Info = txList[index]
continue
}
}
}
}
return
}
// DownloadStatement this endpoint downloads an address statement (PDF)
// The contents will be returned in plain-text and need to be converted to a file.pdf
//
// For more information: https://developers.whatsonchain.com/#download-statement
func (c *Client) DownloadStatement(ctx context.Context, address string) (string, error) {
// https://<network>.whatsonchain.com/statement/<hash>
// todo: this endpoint does not follow the convention of the WOC API v1
return c.request(
ctx,
fmt.Sprintf("https://%s.whatsonchain.com/statement/%s", c.Network(), address),
http.MethodGet, nil,
)
}
// bulkRequest is the common parts of the bulk requests
func bulkRequest(list *AddressList) ([]byte, error) {
// The max limit by WOC
if len(list.Addresses) > MaxAddressesForLookup {
return nil, fmt.Errorf(
"max limit of addresses is %d and you sent %d",
MaxAddressesForLookup, len(list.Addresses),
)
}
// Convert to JSON
return json.Marshal(list)
}
// BulkBalance this endpoint retrieves confirmed and unconfirmed address balances
// Max of 20 addresses at a time
//
// For more information: https://developers.whatsonchain.com/#bulk-balance
func (c *Client) BulkBalance(ctx context.Context, list *AddressList) (balances AddressBalances, err error) {
// Get the JSON
var postData []byte
if postData, err = bulkRequest(list); err != nil {
return
}
var resp string
// https://api.whatsonchain.com/v1/bsv/<network>/addresses/balance
if resp, err = c.request(
ctx,
fmt.Sprintf("%s%s/addresses/balance", apiEndpoint, c.Network()),
http.MethodPost, postData,
); err != nil {
return
}
if len(resp) == 0 {
return nil, ErrAddressNotFound
}
err = json.Unmarshal([]byte(resp), &balances)
return
}
// BulkUnspentTransactionsProcessor will fetch UTXOs for multiple addresses in a single request while automatically batching
// Max of 20 addresses at a time
//
// For more information: https://developers.whatsonchain.com/#bulk-unspent-transactions
func (c *Client) BulkUnspentTransactionsProcessor(ctx context.Context, list *AddressList) (responseList BulkUnspentResponse, err error) {
var batches [][]string
chunkSize := MaxTransactionsUTXO
for i := 0; i < len(list.Addresses); i += chunkSize {
end := i + chunkSize
if end > len(list.Addresses) {
end = len(list.Addresses)
}
batches = append(batches, list.Addresses[i:end])
}
var currentRateLimit int
for _, batch := range batches {
addressList := new(AddressList)
addressList.Addresses = append(addressList.Addresses, batch...)
var returnedList BulkUnspentResponse
if returnedList, err = c.BulkUnspentTransactions(ctx, addressList); err != nil {
return
}
responseList = append(responseList, returnedList...)
currentRateLimit++
if currentRateLimit >= c.RateLimit() {
time.Sleep(1 * time.Second)
currentRateLimit = 0
}
}
return
}
// BulkUnspentTransactions will fetch UTXOs for multiple addresses in a single request
// Max of 20 addresses at a time
//
// For more information: https://developers.whatsonchain.com/#bulk-unspent-transactions
func (c *Client) BulkUnspentTransactions(ctx context.Context, list *AddressList) (response BulkUnspentResponse, err error) {
// Get the JSON
var postData []byte
if postData, err = bulkRequest(list); err != nil {
return
}
var resp string
// https://api.whatsonchain.com/v1/bsv/<network>/addresses/unspent
if resp, err = c.request(
ctx,
fmt.Sprintf("%s%s/addresses/unspent", apiEndpoint, c.Network()),
http.MethodPost, postData,
); err != nil {
return
}
if len(resp) == 0 {
return nil, ErrAddressNotFound
}
err = json.Unmarshal([]byte(resp), &response)
return
}