luno 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/README.md +36 -2
- data/lib/luno.rb +1 -0
- data/lib/luno/accounts.rb +24 -0
- data/lib/luno/beneficiaries.rb +4 -1
- data/lib/luno/client.rb +50 -24
- data/lib/luno/constants.rb +70 -0
- data/lib/luno/lightning.rb +6 -0
- data/lib/luno/other_data.rb +42 -12
- data/lib/luno/version.rb +1 -1
- data/luno.gemspec +2 -2
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e664e47e7a6037bf7e4ff9af7e7e40146cc8d393b0566e77f7f401c51433fb4
|
4
|
+
data.tar.gz: e46c10799631a0fafad6419a176a89c44917d90e4d99b152c7cf91fbd2075778
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d33e2d46da2c3827164b7b217cda126b44410ee2c4beefdcf67ddba3bb43ff7ff56f15b943e45729e9b94c047fdefa1cd4abbeae89048516656b98bda1c9fdae
|
7
|
+
data.tar.gz: e7da2ac7785e87dbc147ce645a9dbbecf04ee01aceb003d7b8fc1dde7cfaecbbff7fd40e9552e10ec3f4b83fe47bed14231baa3394f43246050a76d68321fb73
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
luno (0.
|
4
|
+
luno (0.2.0)
|
5
5
|
active_attr (~> 0.15)
|
6
6
|
httparty (~> 0.18)
|
7
7
|
nokogiri (~> 1.10.9)
|
@@ -82,7 +82,7 @@ GEM
|
|
82
82
|
nokogiri (>= 1.6)
|
83
83
|
rails-html-sanitizer (1.3.0)
|
84
84
|
loofah (~> 2.3)
|
85
|
-
rake (
|
85
|
+
rake (13.0.1)
|
86
86
|
ruby-progressbar (1.10.1)
|
87
87
|
safe_yaml (1.0.5)
|
88
88
|
thread_safe (0.3.6)
|
@@ -106,7 +106,7 @@ DEPENDENCIES
|
|
106
106
|
minitest-reporters (~> 1.4.2)
|
107
107
|
mocha (~> 1.11.2)
|
108
108
|
pry (~> 0.13)
|
109
|
-
rake (~>
|
109
|
+
rake (~> 13.0)
|
110
110
|
timecop (~> 0.9.1)
|
111
111
|
webmock (~> 3.8.3)
|
112
112
|
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Luno
|
2
|
+
A client for the Luno API. See: https://www.luno.com/en/developers/api
|
2
3
|
|
3
|
-
This is a
|
4
|
+
This is an unofficial project and still a work in progress (WIP) ... more to come soon.
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -20,7 +21,35 @@ Or install it yourself as:
|
|
20
21
|
|
21
22
|
## Usage
|
22
23
|
|
23
|
-
|
24
|
+
```ruby
|
25
|
+
require 'luno'
|
26
|
+
client = Luno::Client.new(key: 'your key', secret: 'your secret')
|
27
|
+
```
|
28
|
+
|
29
|
+
### Endpoints
|
30
|
+
- Accounts
|
31
|
+
- Beneficiaries
|
32
|
+
- Markets
|
33
|
+
- Orders
|
34
|
+
- Quotes
|
35
|
+
- Receiving
|
36
|
+
- Sending
|
37
|
+
- Transactions
|
38
|
+
- Withdrawals
|
39
|
+
|
40
|
+
### Beta Endpoints
|
41
|
+
- Lightning
|
42
|
+
- Streaming
|
43
|
+
|
44
|
+
### Other Endpoints
|
45
|
+
- ping(limit: 4, paths: ['https://www.luno.com/', 'https://api.mybitx.com/api/1/'])
|
46
|
+
- countries
|
47
|
+
- currencies
|
48
|
+
- permissions
|
49
|
+
- changelog
|
50
|
+
|
51
|
+
### Constants
|
52
|
+
Constants
|
24
53
|
|
25
54
|
## Development
|
26
55
|
|
@@ -28,6 +57,11 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
28
57
|
|
29
58
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
30
59
|
|
60
|
+
### Tests
|
61
|
+
To run tests execute:
|
62
|
+
|
63
|
+
$ rake test
|
64
|
+
|
31
65
|
## Contributing
|
32
66
|
|
33
67
|
Bug reports and pull requests are welcome on GitHub at https://github.com/trex22/luno. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/lib/luno.rb
CHANGED
data/lib/luno/accounts.rb
CHANGED
@@ -1,8 +1,32 @@
|
|
1
1
|
module Luno
|
2
2
|
module Accounts
|
3
|
+
# GET paths
|
3
4
|
def list_accounts
|
4
5
|
path = 'accounts'
|
5
6
|
authorise_and_send(http_method: :get, path: path)
|
6
7
|
end
|
8
|
+
|
9
|
+
def list_pending_account_transactions(account_id)
|
10
|
+
path = "accounts/#{account_id}/pending"
|
11
|
+
authorise_and_send(http_method: :get, path: path)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Path params: min_row=1&max_row=1000
|
15
|
+
def list_account_transactions(account_id, min_row: -1000, max_row: 0)
|
16
|
+
path = "accounts/#{account_id}/transactions"
|
17
|
+
path_params = { min_row: min_row, max_row: max_row }
|
18
|
+
authorise_and_send(http_method: :get, path: path, params: path_params)
|
19
|
+
end
|
20
|
+
|
21
|
+
# List balances has been moved and retired as an endpoint
|
22
|
+
|
23
|
+
# POST paths
|
24
|
+
# TODO:
|
25
|
+
# Create account: /api/1/accounts body: { name: '', currency: '' }
|
26
|
+
# TODO: Check internally that inputs are valid
|
27
|
+
|
28
|
+
# PUT paths
|
29
|
+
# TODO:
|
30
|
+
# Update Account name: /api/1/accounts/{id}/name body: { name: '' }
|
7
31
|
end
|
8
32
|
end
|
data/lib/luno/beneficiaries.rb
CHANGED
data/lib/luno/client.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Luno
|
2
2
|
class Client
|
3
|
+
include ::Luno::Constants
|
4
|
+
|
3
5
|
# Endpoints
|
4
6
|
include ::Luno::Accounts
|
5
7
|
include ::Luno::Beneficiaries
|
@@ -38,12 +40,12 @@ module Luno
|
|
38
40
|
|
39
41
|
private
|
40
42
|
|
41
|
-
def unauthorised_and_send(http_method:, path:, payload: {})
|
43
|
+
def unauthorised_and_send(http_method:, path:, payload: {}, params: {})
|
42
44
|
start_time = get_micro_second_time
|
43
45
|
|
44
46
|
response = HTTParty.send(
|
45
47
|
http_method.to_sym,
|
46
|
-
construct_base_path(path),
|
48
|
+
construct_base_path(path, params),
|
47
49
|
body: payload,
|
48
50
|
headers: { 'Content-Type': 'application/json' },
|
49
51
|
port: port,
|
@@ -51,17 +53,17 @@ module Luno
|
|
51
53
|
)
|
52
54
|
|
53
55
|
end_time = get_micro_second_time
|
54
|
-
|
56
|
+
construct_response_object(response, path, start_time, end_time)
|
55
57
|
end
|
56
58
|
|
57
|
-
def authorise_and_send(http_method:, path:, payload: {})
|
59
|
+
def authorise_and_send(http_method:, path:, payload: {}, params: {})
|
58
60
|
auth = {username: key, password: secret}
|
59
61
|
|
60
62
|
start_time = get_micro_second_time
|
61
63
|
|
62
64
|
response = HTTParty.send(
|
63
65
|
http_method.to_sym,
|
64
|
-
construct_base_path(path),
|
66
|
+
construct_base_path(path, params),
|
65
67
|
body: payload,
|
66
68
|
headers: { 'Content-Type': 'application/json' },
|
67
69
|
port: port,
|
@@ -70,39 +72,63 @@ module Luno
|
|
70
72
|
)
|
71
73
|
|
72
74
|
end_time = get_micro_second_time
|
73
|
-
|
75
|
+
construct_response_object(response, path, start_time, end_time)
|
74
76
|
end
|
75
77
|
|
76
|
-
def
|
77
|
-
|
78
|
-
response
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
{
|
83
|
-
body: response.body,
|
84
|
-
headers: response.headers,
|
85
|
-
metadata: construct_metadata(start_time, end_time)
|
86
|
-
}
|
87
|
-
end
|
78
|
+
def construct_response_object(response, path, start_time, end_time)
|
79
|
+
{
|
80
|
+
'body' => parse_body(response, path),
|
81
|
+
'headers' => response.headers,
|
82
|
+
'metadata' => construct_metadata(response, start_time, end_time)
|
83
|
+
}
|
88
84
|
end
|
89
85
|
|
90
|
-
def construct_metadata(start_time, end_time)
|
86
|
+
def construct_metadata(response, start_time, end_time)
|
91
87
|
total_time = end_time - start_time
|
92
88
|
|
93
89
|
{
|
94
|
-
start_time
|
95
|
-
end_time
|
96
|
-
total_time
|
90
|
+
'start_time' => start_time,
|
91
|
+
'end_time' => end_time,
|
92
|
+
'total_time' => total_time
|
97
93
|
}
|
98
94
|
end
|
99
95
|
|
96
|
+
def body_is_present?(response)
|
97
|
+
!body_is_missing?(response)
|
98
|
+
end
|
99
|
+
|
100
|
+
def body_is_missing?(response)
|
101
|
+
response.body.nil? || response.body.empty?
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_body(response, path)
|
105
|
+
parsed_response = JSON.parse(response.body) # Purposely not using HTTParty
|
106
|
+
|
107
|
+
if parsed_response.dig(path.to_s)
|
108
|
+
parsed_response.dig(path.to_s)
|
109
|
+
else
|
110
|
+
parsed_response
|
111
|
+
end
|
112
|
+
rescue JSON::ParserError => _e
|
113
|
+
response.body
|
114
|
+
end
|
115
|
+
|
100
116
|
def get_micro_second_time
|
101
117
|
(Time.now.to_f * 1000000).to_i
|
102
118
|
end
|
103
119
|
|
104
|
-
def construct_base_path(path)
|
105
|
-
"#{base_path}/#{path}"
|
120
|
+
def construct_base_path(path, params)
|
121
|
+
constructed_path = "#{base_path}/#{path}"
|
122
|
+
|
123
|
+
if params != {}
|
124
|
+
constructed_path
|
125
|
+
else
|
126
|
+
"#{constructed_path}?#{process_params(params)}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def process_params(params)
|
131
|
+
params.keys.map { |key| "#{key}=#{params[key]}" }.join('&')
|
106
132
|
end
|
107
133
|
end
|
108
134
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Luno
|
2
|
+
module Constants
|
3
|
+
CURRENCIES = {
|
4
|
+
XBT: 'Bitcoin',
|
5
|
+
BCH: 'Bitcoin Cash',
|
6
|
+
ETH: 'Ethereum',
|
7
|
+
EUR: 'Euro',
|
8
|
+
MYR: 'Malaysian Ringgit',
|
9
|
+
NGN: 'Nigerian Naira',
|
10
|
+
UGX: 'Ugandan Shilling',
|
11
|
+
ZAR: 'South African Rand',
|
12
|
+
ZMW: 'Zambian Kwacha'
|
13
|
+
}
|
14
|
+
|
15
|
+
CURRENCY_PAIRS = [
|
16
|
+
'XBTEUR',
|
17
|
+
'XBTZAR',
|
18
|
+
'XBTUGX',
|
19
|
+
'XBTZMW',
|
20
|
+
'ETHXBT',
|
21
|
+
'BCHXBT'
|
22
|
+
]
|
23
|
+
|
24
|
+
WITHDRAWL_CURRENCIES = [
|
25
|
+
BTC: 'Bitcoin',
|
26
|
+
BCH: 'Bitcoin Cash',
|
27
|
+
ETH: 'Ethereum',
|
28
|
+
BTC_LN: 'Lightning'
|
29
|
+
]
|
30
|
+
|
31
|
+
WITHDRAWL_CURRENCY_PAIR_METHODS = {
|
32
|
+
ZAR_EFT: 'EFT',
|
33
|
+
NAD_EFT: 'EFT',
|
34
|
+
KES_EFT: 'EFT',
|
35
|
+
KES_MPESA: 'M-Pesa',
|
36
|
+
MYR_IBG: 'Interbank GIRO / IBFT',
|
37
|
+
IDR_LLG: 'Bank transfer, Lalu Lintas Giro',
|
38
|
+
NGN_EFT: 'Bank transfer',
|
39
|
+
ZMW_EFT: 'Bank transfer',
|
40
|
+
SGD_GIRO: 'GIRO / FAST',
|
41
|
+
SGD_WIRE: 'International Wire',
|
42
|
+
EUR_SEPA: 'SEPA transfer',
|
43
|
+
UGX_EFT: 'Bank transfer',
|
44
|
+
}
|
45
|
+
|
46
|
+
PERMISSIONS = {
|
47
|
+
Perm_R_Balance: { id: 1, description: '(View balance)' },
|
48
|
+
Perm_R_Transactions: { id: 2, description: '(View transactions)' },
|
49
|
+
Perm_W_Send: { id: 4, description: '(Send to any address)' },
|
50
|
+
Perm_R_Addresses: { id: 8, description: '(View addresses)' },
|
51
|
+
Perm_W_Addresses: { id: 16, description: '(Create addresses)' },
|
52
|
+
Perm_R_Orders: { id: 32, description: '(View orders)' },
|
53
|
+
Perm_W_Orders: { id: 64, description: '(Create orders)' },
|
54
|
+
Perm_R_Withdrawals: { id: 128, description: '(View withdrawals)' },
|
55
|
+
Perm_W_Withdrawals: { id: 256, description: '(Create withdrawals)' },
|
56
|
+
Perm_R_Merchant: { id: 512, description: '(View merchant invoices)' },
|
57
|
+
Perm_W_Merchant: { id: 1024, description: '(Create merchant invoices)' },
|
58
|
+
Perm_W_ClientDebit: { id: 8192, description: '(Debit accounts)' },
|
59
|
+
Perm_W_ClientCredit: { id: 16384, description: '(Credit accounts)' },
|
60
|
+
Perm_R_Beneficiaries: { id: 32768, description: '(View beneficiaries)' },
|
61
|
+
Perm_W_Beneficiaries: { id: 65536, description: '(Create and delete beneficiaries)' },
|
62
|
+
}
|
63
|
+
|
64
|
+
MARKET_API_RATE_LIMIT = 1 # per second per ip
|
65
|
+
MARKET_API_BURST_RATE_LIMIT = 5 # per second per ip
|
66
|
+
|
67
|
+
API_RATE_LIMIT = 5 # per second per ip
|
68
|
+
API_BURST_RATE_LIMIT = 25 # per second per ip
|
69
|
+
end
|
70
|
+
end
|
data/lib/luno/lightning.rb
CHANGED
data/lib/luno/other_data.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
|
4
4
|
module Luno
|
5
5
|
module OtherData
|
6
|
+
COUNTRY_ENDPOINT = 'https://www.luno.com/en/countries'
|
7
|
+
DOCUMENTATION_ENDPOINT = 'https://www.luno.com/en/developers/api'
|
8
|
+
|
6
9
|
def ping(limit: 4, paths: ['https://www.luno.com/', 'https://api.mybitx.com/api/1/'])
|
7
10
|
endpoint_metrics = paths.map do |path|
|
8
11
|
responses = []
|
@@ -12,45 +15,58 @@ module Luno
|
|
12
15
|
end
|
13
16
|
|
14
17
|
avg_time = responses
|
15
|
-
.map { |r| r.dig(
|
18
|
+
.map { |r| r.dig('metadata')&.dig('total_time') }
|
16
19
|
.reduce(&:+) / limit
|
17
20
|
|
18
21
|
{
|
19
|
-
path
|
20
|
-
average_time
|
22
|
+
'path' => path,
|
23
|
+
'average_time' => avg_time
|
21
24
|
}
|
22
25
|
end
|
23
26
|
|
24
27
|
{
|
25
|
-
metadata
|
26
|
-
endpoint_metrics
|
28
|
+
'metadata' => {
|
29
|
+
'endpoint_metrics' => endpoint_metrics
|
27
30
|
}
|
28
31
|
}
|
29
32
|
end
|
30
33
|
|
34
|
+
# TODO
|
31
35
|
def countries
|
32
|
-
call_countries
|
36
|
+
response = call_countries
|
33
37
|
end
|
34
38
|
|
35
39
|
# From API Documentation
|
40
|
+
# [WIP]
|
36
41
|
def currencies
|
37
42
|
lines = []
|
38
43
|
|
39
|
-
call_api_documentation
|
44
|
+
response = call_api_documentation
|
45
|
+
|
46
|
+
output = parse_html(response['body'])
|
40
47
|
.at('div#tag\/Currency')
|
41
48
|
.search('li')
|
42
49
|
.map(&:text)
|
43
50
|
.map { |text| text.split.join(" ") }
|
51
|
+
|
52
|
+
{
|
53
|
+
'body' => output,
|
54
|
+
'headers' => response['headers'],
|
55
|
+
'metadata' => response['metadata']
|
56
|
+
}
|
44
57
|
end
|
45
58
|
|
59
|
+
# TODO
|
46
60
|
def permissions
|
47
|
-
call_api_documentation
|
61
|
+
response = call_api_documentation
|
48
62
|
end
|
49
63
|
|
50
64
|
def changelog
|
51
65
|
lines = []
|
52
66
|
|
53
|
-
call_api_documentation
|
67
|
+
response = call_api_documentation
|
68
|
+
|
69
|
+
parse_html(response['body'])
|
54
70
|
.at('div#tag\/Changelog')
|
55
71
|
.search('li')
|
56
72
|
.map(&:text)
|
@@ -73,17 +89,31 @@ module Luno
|
|
73
89
|
output.merge!(key => text)
|
74
90
|
end
|
75
91
|
|
76
|
-
|
92
|
+
{
|
93
|
+
'body' => output,
|
94
|
+
'headers' => response['headers'],
|
95
|
+
'metadata' => response['metadata']
|
96
|
+
}
|
77
97
|
end
|
78
98
|
|
79
99
|
private
|
80
100
|
|
81
101
|
def call_countries
|
82
|
-
|
102
|
+
start_time = get_micro_second_time
|
103
|
+
|
104
|
+
response = HTTParty.get(COUNTRY_ENDPOINT)
|
105
|
+
|
106
|
+
end_time = get_micro_second_time
|
107
|
+
construct_response_obejct(response, start_time, end_time)
|
83
108
|
end
|
84
109
|
|
85
110
|
def call_api_documentation
|
86
|
-
|
111
|
+
start_time = get_micro_second_time
|
112
|
+
|
113
|
+
response = HTTParty.get(DOCUMENTATION_ENDPOINT)
|
114
|
+
|
115
|
+
end_time = get_micro_second_time
|
116
|
+
construct_response_obejct(response, start_time, end_time)
|
87
117
|
end
|
88
118
|
|
89
119
|
def parse_html(raw_html)
|
data/lib/luno/version.rb
CHANGED
data/luno.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.email = ["contact@jasonchalom.com"]
|
10
10
|
|
11
11
|
spec.summary = "A client for using the Luno API in Ruby."
|
12
|
-
spec.description = "A client for using the Luno API in Ruby.
|
12
|
+
spec.description = "A client for using the Luno API in Ruby. Built form their api documentation. https://www.luno.com/en/developers/api. This is an unofficial project."
|
13
13
|
spec.homepage = "https://github.com/TRex22/luno"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
|
29
29
|
# Development dependancies
|
30
30
|
spec.add_development_dependency "bundler", "~> 1.17"
|
31
|
-
spec.add_development_dependency "rake", "~>
|
31
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
32
32
|
spec.add_development_dependency "minitest", "~> 5.0"
|
33
33
|
spec.add_development_dependency "minitest-focus", "~> 1.1.2"
|
34
34
|
spec.add_development_dependency "minitest-reporters", "~> 1.4.2"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: luno
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- trex22
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '13.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '13.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: minitest
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,8 +178,8 @@ dependencies:
|
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: 3.8.3
|
181
|
-
description: A client for using the Luno API in Ruby.
|
182
|
-
|
181
|
+
description: A client for using the Luno API in Ruby. Built form their api documentation.
|
182
|
+
https://www.luno.com/en/developers/api. This is an unofficial project.
|
183
183
|
email:
|
184
184
|
- contact@jasonchalom.com
|
185
185
|
executables: []
|
@@ -201,6 +201,7 @@ files:
|
|
201
201
|
- lib/luno/accounts.rb
|
202
202
|
- lib/luno/beneficiaries.rb
|
203
203
|
- lib/luno/client.rb
|
204
|
+
- lib/luno/constants.rb
|
204
205
|
- lib/luno/lightning.rb
|
205
206
|
- lib/luno/markets.rb
|
206
207
|
- lib/luno/orders.rb
|