-
Notifications
You must be signed in to change notification settings - Fork 1
/
gemini.go
131 lines (109 loc) · 3.74 KB
/
gemini.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
package gemini
import (
"bytes"
"crypto/hmac"
"crypto/sha512"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/claudiocandio/gemini-api/logger"
)
type Api struct {
url string
key string
secret string
}
// buildHeader handles the conversion of post parameters into headers formatted
// according to Gemini specification. Resulting headers include the API key,
// the payload and the signature.
func (api *Api) buildHeader(req *map[string]interface{}) http.Header {
reqStr, _ := json.Marshal(req)
payload := base64.StdEncoding.EncodeToString([]byte(reqStr))
mac := hmac.New(sha512.New384, []byte(api.secret))
if _, err := mac.Write([]byte(payload)); err != nil {
panic(err)
}
signature := hex.EncodeToString(mac.Sum(nil))
header := http.Header{}
header.Set("X-GEMINI-APIKEY", api.key)
header.Set("X-GEMINI-PAYLOAD", payload)
header.Set("X-GEMINI-SIGNATURE", signature)
return header
}
// request makes the HTTP request to Gemini and handles any returned errors
func (api *Api) request(verb, url string, params map[string]interface{}) ([]byte, error) {
logger.Debug("func request: http.NewRequest",
fmt.Sprintf("verb:%s", verb),
fmt.Sprintf("url:%s", url),
fmt.Sprintf("params:%v", params),
)
req, err := http.NewRequest(verb, url, bytes.NewBuffer([]byte{}))
if err != nil {
return nil, err
}
if params != nil {
if verb == "GET" {
q := req.URL.Query()
for key, val := range params {
q.Add(key, val.(string))
}
req.URL.RawQuery = q.Encode()
} else {
req.Header = api.buildHeader(¶ms)
}
}
// this will also show gemini key and secret, pay attention
logger.Trace("func request: params",
fmt.Sprintf("req:%v", req),
)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
logger.Debug("func request: Http Client response",
fmt.Sprintf("resp:%v", resp),
)
if resp.StatusCode != 200 {
statusCode := fmt.Sprintf("HTTP Status Code: %d", resp.StatusCode)
if resp.StatusCode >= 300 && resp.StatusCode < 400 {
return nil, fmt.Errorf("%s\n%s", statusCode, "API entry point has moved, see Location: header. Most likely an http: to https: redirect.")
} else if resp.StatusCode == 400 {
return nil, fmt.Errorf("%s\n%s", statusCode, "Auction not open or paused, ineligible timing, market not open, or the request was malformed; in the case of a private API request, missing or malformed Gemini private API authentication headers")
} else if resp.StatusCode == 403 {
return nil, fmt.Errorf("%s\n%s", statusCode, "The API key is missing the role necessary to access this private API endpoint")
} else if resp.StatusCode == 404 {
return nil, fmt.Errorf("%s\n%s", statusCode, "Unknown API entry point or Order not found")
} else if resp.StatusCode == 406 {
return nil, fmt.Errorf("%s\n%s", statusCode, "Insufficient Funds")
} else if resp.StatusCode == 429 {
return nil, fmt.Errorf("%s\n%s", statusCode, "Rate Limiting was applied")
} else if resp.StatusCode == 500 {
return nil, fmt.Errorf("%s\n%s", statusCode, "The server encountered an error")
} else if resp.StatusCode == 502 {
return nil, fmt.Errorf("%s\n%s", statusCode, "Technical issues are preventing the request from being satisfied")
} else if resp.StatusCode == 503 {
return nil, fmt.Errorf("%s\n%s", statusCode, "The exchange is down for maintenance")
}
}
// read response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
logger.Debug("func request: Http Client body",
fmt.Sprintf("body:%s", body),
)
return body, nil
}
func New(live bool, key, secret string) *Api {
var url string
if url = sandbox_URL; live {
url = base_URL
}
return &Api{url: url, key: key, secret: secret}
}