blockchain 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +23 -6
- data/Rakefile +6 -7
- data/blockchain.gemspec +1 -3
- data/docs/blockexplorer.md +235 -64
- data/docs/createwallet.md +32 -15
- data/docs/exchangerates.md +42 -17
- data/docs/pushtx.md +17 -11
- data/docs/receive.md +49 -22
- data/docs/statistics.md +71 -8
- data/docs/wallet.md +61 -79
- data/lib/blockchain.rb +1 -0
- data/lib/blockchain/blockexplorer.rb +195 -91
- data/lib/blockchain/client.rb +56 -0
- data/lib/blockchain/createwallet.rb +34 -19
- data/lib/blockchain/exchangerates.rb +55 -28
- data/lib/blockchain/pushtx.rb +21 -9
- data/lib/blockchain/receive.rb +52 -26
- data/lib/blockchain/statistics.rb +90 -29
- data/lib/blockchain/version.rb +1 -1
- data/lib/blockchain/wallet.rb +39 -42
- data/test/test_block.rb +97 -0
- data/test/test_exchangerates.rb +31 -0
- data/test/test_pushtx.rb +11 -0
- data/test/test_statistics.rb +35 -0
- data/test/test_wallet.rb +44 -0
- metadata +14 -18
- data/lib/blockchain/util.rb +0 -35
@@ -1,9 +1,61 @@
|
|
1
1
|
require 'json'
|
2
|
-
require_relative '
|
2
|
+
require_relative 'client'
|
3
3
|
|
4
4
|
module Blockchain
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
class ExchangeRateExplorer
|
7
|
+
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize(base_url = nil, api_code = nil)
|
11
|
+
@client = Client.new(base_url, api_code)
|
12
|
+
end
|
13
|
+
|
14
|
+
def proxy(method_name, *args)
|
15
|
+
warn "[DEPRECATED] avoid use of static methods, use an instance of ExchangeRateExplorer class instead."
|
16
|
+
send(method_name, *args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_ticker()
|
20
|
+
params = {}
|
21
|
+
response = @client.call_api('ticker', data: params)
|
22
|
+
json_response = JSON.parse(response)
|
23
|
+
|
24
|
+
ticker = {}
|
25
|
+
json_response.each do |key,value|
|
26
|
+
json_ccy = json_response[key]
|
27
|
+
ccy = Currency.new(json_ccy['last'],
|
28
|
+
json_ccy['buy'],
|
29
|
+
json_ccy['sell'],
|
30
|
+
json_ccy['symbol'],
|
31
|
+
json_ccy['15m'])
|
32
|
+
ticker[key] = ccy
|
33
|
+
end
|
34
|
+
return ticker
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_btc(ccy, value)
|
38
|
+
params = { 'currency' => ccy, 'value' => value}
|
39
|
+
return @client.call_api('tobtc', data: params).to_f
|
40
|
+
end
|
41
|
+
|
42
|
+
def from_btc(ccy = nil, satoshi_value)
|
43
|
+
params = {'value' => satoshi_value}
|
44
|
+
if !ccy.nil? then params['currency'] = ccy end
|
45
|
+
return @client.call_api('frombtc', data: params).to_f
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.get_ticker(api_code = nil)
|
51
|
+
Blockchain::ExchangeRateExplorer.new(nil, api_code).proxy(__method__)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.to_btc(ccy, value, api_code = nil)
|
55
|
+
Blockchain::ExchangeRateExplorer.new(nil, api_code).proxy(__method__, ccy, value)
|
56
|
+
end
|
57
|
+
|
58
|
+
class Currency
|
7
59
|
attr_reader :last
|
8
60
|
attr_reader :buy
|
9
61
|
attr_reader :sell
|
@@ -19,29 +71,4 @@ module Blockchain
|
|
19
71
|
end
|
20
72
|
end
|
21
73
|
|
22
|
-
def self.get_ticker(api_code = nil)
|
23
|
-
params = {}
|
24
|
-
if !api_code.nil? then params['api_code'] = api_code end
|
25
|
-
response = Blockchain::call_api('ticker', data: params)
|
26
|
-
json_response = JSON.parse(response)
|
27
|
-
|
28
|
-
ticker = {}
|
29
|
-
json_response.each do |key,value|
|
30
|
-
json_ccy = json_response[key]
|
31
|
-
ccy = Currency.new(json_ccy['last'],
|
32
|
-
json_ccy['buy'],
|
33
|
-
json_ccy['sell'],
|
34
|
-
json_ccy['symbol'],
|
35
|
-
json_ccy['15m'])
|
36
|
-
ticker[key] = ccy
|
37
|
-
end
|
38
|
-
return ticker
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.to_btc(ccy, value, api_code = nil)
|
42
|
-
params = { 'currency' => ccy, 'value' => value}
|
43
|
-
if !api_code.nil? then params['api_code'] = api_code end
|
44
|
-
return Blockchain::call_api('tobtc', data: params).to_f
|
45
|
-
end
|
46
|
-
|
47
74
|
end
|
data/lib/blockchain/pushtx.rb
CHANGED
@@ -1,16 +1,28 @@
|
|
1
1
|
require 'json'
|
2
|
-
require_relative '
|
2
|
+
require_relative 'client'
|
3
3
|
|
4
4
|
module Blockchain
|
5
5
|
|
6
|
+
class PushTx
|
7
|
+
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize(base_url = nil, api_code = nil)
|
11
|
+
@client = Client.new(base_url, api_code)
|
12
|
+
end
|
13
|
+
|
14
|
+
def proxy(method_name, tx)
|
15
|
+
warn "[DEPRECATED] avoid use of static methods, use an instance of PushTx class instead."
|
16
|
+
send(method_name, tx)
|
17
|
+
end
|
18
|
+
|
19
|
+
def pushtx(tx)
|
20
|
+
params = { 'tx' => tx }
|
21
|
+
@client.call_api('pushtx', method: 'post', data: params)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
6
25
|
def self.pushtx(tx, api_code = nil)
|
7
|
-
|
8
|
-
|
9
|
-
if !api_code.nil?
|
10
|
-
params['api_code'] = api_code
|
11
|
-
end
|
12
|
-
|
13
|
-
Blockchain::call_api('pushtx', method: 'post', data: params)
|
26
|
+
Blockchain::PushTx.new(nil, api_code).proxy(__method__, tx)
|
14
27
|
end
|
15
|
-
|
16
28
|
end
|
data/lib/blockchain/receive.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'client'
|
4
4
|
|
5
5
|
module Blockchain
|
6
6
|
|
@@ -19,7 +19,57 @@ module Blockchain
|
|
19
19
|
end
|
20
20
|
|
21
21
|
module V2
|
22
|
-
|
22
|
+
|
23
|
+
class Receive
|
24
|
+
|
25
|
+
attr_reader :client
|
26
|
+
|
27
|
+
def initialize(base_url = nil)
|
28
|
+
base_url = base_url.nil? ? 'https://api.blockchain.info/v2/' : base_url
|
29
|
+
@client = Client.new(base_url)
|
30
|
+
end
|
31
|
+
|
32
|
+
def proxy(method_name, *args)
|
33
|
+
warn "[DEPRECATED] avoid use of static methods, use an instance of Receive class instead."
|
34
|
+
send(method_name, *args)
|
35
|
+
end
|
36
|
+
|
37
|
+
def receive(xpub, callback, api_key, gap_limit = nil)
|
38
|
+
params = { 'xpub' => xpub, 'callback' => callback, 'key' => api_key }
|
39
|
+
if !gap_limit.nil? then params['gap_limit'] = gap_limit end
|
40
|
+
response = @client.call_api('receive', method: 'get', data: params)
|
41
|
+
return ReceiveResponse.new(JSON.parse(response))
|
42
|
+
end
|
43
|
+
|
44
|
+
def callback_log(callback, api_key = nil)
|
45
|
+
params = {'callback' => callback }
|
46
|
+
params['key'] = api_key unless api_key.nil?
|
47
|
+
response = @client.call_api('receive/callback_log', method: 'get', data: params)
|
48
|
+
json_resp = JSON.parse(response)
|
49
|
+
log_entries = json_resp.map do |entry|
|
50
|
+
LogEntry.new(JSON.parse(entry))
|
51
|
+
end
|
52
|
+
return log_entries
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_gap(xpub, api_key = nil)
|
56
|
+
params = {'xpub' => xpub}
|
57
|
+
params['key'] = api_key unless api_key.nil?
|
58
|
+
response = @client.call_api('receive/checkgap', method: 'get', data: params)
|
59
|
+
return JSON.parse(response)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.receive(xpub, callback, api_key)
|
65
|
+
Blockchain::V2::Receive.new.proxy(__method__, xpub, callback, api_key)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.callback_log(callback, api_key = nil)
|
69
|
+
Blockchain::V2::Receive.new.proxy(__method__, callback, api_key)
|
70
|
+
end
|
71
|
+
|
72
|
+
class ReceiveResponse
|
23
73
|
attr_reader :address
|
24
74
|
attr_reader :index
|
25
75
|
attr_reader :callback_url
|
@@ -44,30 +94,6 @@ module Blockchain
|
|
44
94
|
@response_code = response_code
|
45
95
|
end
|
46
96
|
end
|
47
|
-
|
48
|
-
def self.receive(xpub, callback, api_key)
|
49
|
-
params = { 'xpub' => xpub, 'callback' => callback, 'key' => api_key }
|
50
|
-
resp = Blockchain::call_api('v2/receive', method: 'get', data: params, base_url: 'https://api.blockchain.info/')
|
51
|
-
json_resp = JSON.parse(resp)
|
52
|
-
receive_response = ReceiveResponse.new(json_resp['address'],
|
53
|
-
json_resp['index'],
|
54
|
-
json_resp['callback'])
|
55
|
-
receive_response
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.callback_log(callback, api_key = nil)
|
59
|
-
params = {'callback' => callback }
|
60
|
-
params['key'] = api_key unless api_key.nil?
|
61
|
-
resp = Blockchain::call_api('v2/receive/callback_log', method: 'get', data: params, base_url: 'https://api.blockchain.info/')
|
62
|
-
json_resp = JSON.parse(resp)
|
63
|
-
receive_response = json_resp.map do |entry|
|
64
|
-
LogEntry.new(entry['callback'],
|
65
|
-
entry['called_at'],
|
66
|
-
entry['raw_response'],
|
67
|
-
entry['response_code'])
|
68
|
-
end
|
69
|
-
receive_response
|
70
|
-
end
|
71
97
|
end
|
72
98
|
|
73
99
|
|
@@ -1,38 +1,74 @@
|
|
1
1
|
require 'json'
|
2
|
-
require_relative '
|
2
|
+
require_relative 'client'
|
3
3
|
|
4
4
|
module Blockchain
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
class StatisticsExplorer
|
7
|
+
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize(base_url = nil, api_code = nil)
|
11
|
+
@client = Client.new(base_url, api_code)
|
12
|
+
end
|
13
|
+
|
14
|
+
def proxy(method_name, *args)
|
15
|
+
warn "[DEPRECATED] avoid use of static methods, use an instance of StatisticsExplorer class instead."
|
16
|
+
send(method_name, *args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_statistics()
|
20
|
+
params = { 'format' => 'json' }
|
21
|
+
resource = 'stats'
|
22
|
+
response = @client.call_api(resource, method: 'get', data: params)
|
23
|
+
return StatisticsResponse.new(JSON.parse(response))
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_chart(chart_type, timespan = nil, rolling_average = nil)
|
27
|
+
params = { 'format' => 'json' }
|
28
|
+
if !timespan.nil? then params['timespan'] = timespan end
|
29
|
+
if !rolling_average.nil? then params['rollingAverage'] = rolling_average end
|
30
|
+
resource = 'charts/' + chart_type
|
31
|
+
response = @client.call_api(resource, method: 'get', data: params)
|
32
|
+
return ChartResponse.new(JSON.parse(response))
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_pools(timespan = 4)
|
36
|
+
if timespan < 1 || timespan > 10
|
37
|
+
raise ArgumentError, 'timespan must be between 1 and 10'
|
38
|
+
end
|
39
|
+
params = { 'format' => 'json', 'timespan' => timespan.to_s + 'days' }
|
40
|
+
resource = 'pools'
|
41
|
+
response = @client.call_api(resource, method: 'get', data: params)
|
42
|
+
return JSON.parse(response)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.get(api_code = nil)
|
47
|
+
Blockchain::StatisticsExplorer.new(nil, api_code).proxy('get_statistics')
|
12
48
|
end
|
13
49
|
|
14
50
|
class StatisticsResponse
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
51
|
+
attr_reader :trade_volume_btc
|
52
|
+
attr_reader :miners_revenue_usd
|
53
|
+
attr_reader :btc_mined
|
54
|
+
attr_reader :trade_volume_usd
|
55
|
+
attr_reader :difficulty
|
56
|
+
attr_reader :minutes_between_blocks
|
57
|
+
attr_reader :number_of_transactions
|
58
|
+
attr_reader :hash_rate
|
59
|
+
attr_reader :timestamp
|
60
|
+
attr_reader :mined_blocks
|
61
|
+
attr_reader :blocks_size
|
62
|
+
attr_reader :total_fees_btc
|
63
|
+
attr_reader :total_btc_sent
|
64
|
+
attr_reader :estimated_btc_sent
|
65
|
+
attr_reader :total_btc
|
66
|
+
attr_reader :total_blocks
|
67
|
+
attr_reader :next_retarget
|
68
|
+
attr_reader :estimated_transaction_volume_usd
|
69
|
+
attr_reader :miners_revenue_btc
|
70
|
+
attr_reader :market_price_usd
|
71
|
+
|
36
72
|
def initialize(s)
|
37
73
|
@trade_volume_btc = s['trade_volume_btc']
|
38
74
|
@miners_revenue_usd = s['miners_revenue_usd']
|
@@ -56,5 +92,30 @@ module Blockchain
|
|
56
92
|
@market_price_usd = s['market_price_usd']
|
57
93
|
end
|
58
94
|
end
|
59
|
-
|
95
|
+
|
96
|
+
class ChartResponse
|
97
|
+
attr_reader :chart_name
|
98
|
+
attr_reader :unit
|
99
|
+
attr_reader :timespan
|
100
|
+
attr_reader :description
|
101
|
+
attr_reader :values
|
102
|
+
|
103
|
+
def initialize(cr)
|
104
|
+
@chart_name = cr['name']
|
105
|
+
@unit = cr['unit']
|
106
|
+
@timespan = cr['period']
|
107
|
+
@description = cr['description']
|
108
|
+
@values = cr['values'].map{ |value| ChartValue.new(value) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class ChartValue
|
113
|
+
attr_reader :x
|
114
|
+
attr_reader :y
|
115
|
+
|
116
|
+
def initialize(cv)
|
117
|
+
@x = cv['x']
|
118
|
+
@y = cv['y']
|
119
|
+
end
|
120
|
+
end
|
60
121
|
end
|
data/lib/blockchain/version.rb
CHANGED
data/lib/blockchain/wallet.rb
CHANGED
@@ -1,27 +1,32 @@
|
|
1
1
|
require 'json'
|
2
|
-
require_relative '
|
2
|
+
require_relative 'client'
|
3
3
|
|
4
4
|
module Blockchain
|
5
5
|
|
6
6
|
class Wallet
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize(identifier, password, url = 'http://localhost:3000/', second_password = nil, api_code = nil)
|
11
|
+
@client = Client.new(url, api_code)
|
12
|
+
@identifier = identifier
|
10
13
|
@password = password
|
11
14
|
@second_password = second_password
|
12
|
-
@api_code = api_code
|
13
|
-
@url = url
|
14
15
|
end
|
15
|
-
|
16
|
-
def send(to, amount, from_address: nil, fee: nil
|
16
|
+
|
17
|
+
def send(to, amount, from_address: nil, fee: nil)
|
17
18
|
recipient = { to => amount }
|
18
|
-
return send_many(recipient, from_address: from_address, fee: fee
|
19
|
+
return send_many(recipient, from_address: from_address, fee: fee)
|
19
20
|
end
|
20
|
-
|
21
|
-
def send_many(recipients, from_address: nil, fee: nil
|
21
|
+
|
22
|
+
def send_many(recipients, from_address: nil, fee: nil)
|
22
23
|
params = build_basic_request()
|
23
24
|
method = ''
|
24
|
-
|
25
|
+
|
26
|
+
if recipients.nil? || recipients.size == 0
|
27
|
+
raise ArgumentError, 'Sending bitcoin from your wallet requires at least one receipient'
|
28
|
+
end
|
29
|
+
|
25
30
|
if recipients.size == 1
|
26
31
|
params['to'] = recipients.keys[0]
|
27
32
|
params['amount'] = recipients.values[0]
|
@@ -30,37 +35,33 @@ module Blockchain
|
|
30
35
|
params['recipients'] = JSON.dump(recipients)
|
31
36
|
method = 'sendmany'
|
32
37
|
end
|
33
|
-
|
38
|
+
|
34
39
|
if !from_address.nil?
|
35
40
|
params['from'] = from_address
|
36
41
|
end
|
37
42
|
if !fee.nil?
|
38
43
|
params['fee'] = fee
|
39
44
|
end
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
response = Blockchain::call_api("merchant/#{@identifier}/#{method}", method: 'post', data: params, base_url: @url)
|
45
|
+
|
46
|
+
response = @client.call_api("merchant/#{@identifier}/#{method}", method: 'post', data: params)
|
45
47
|
json_response = parse_json(response)
|
46
48
|
return PaymentResponse.new(
|
47
49
|
json_response['message'],
|
48
50
|
json_response['tx_hash'],
|
49
51
|
json_response['notice'])
|
50
52
|
end
|
51
|
-
|
53
|
+
|
52
54
|
def get_balance()
|
53
|
-
response =
|
55
|
+
response = @client.call_api("merchant/#{@identifier}/balance", method: 'get', data: build_basic_request())
|
54
56
|
json_response = parse_json(response)
|
55
57
|
return json_response['balance']
|
56
58
|
end
|
57
|
-
|
58
|
-
def list_addresses(
|
59
|
+
|
60
|
+
def list_addresses()
|
59
61
|
params = build_basic_request()
|
60
|
-
|
61
|
-
response = Blockchain::call_api("merchant/#{@identifier}/list", method: 'get', data: params)
|
62
|
+
response = @client.call_api("merchant/#{@identifier}/list", method: 'get', data: params)
|
62
63
|
json_response = parse_json(response)
|
63
|
-
|
64
|
+
|
64
65
|
addresses = []
|
65
66
|
json_response['addresses'].each do |a|
|
66
67
|
addr = WalletAddress.new(a['balance'],
|
@@ -71,57 +72,53 @@ module Blockchain
|
|
71
72
|
end
|
72
73
|
return addresses
|
73
74
|
end
|
74
|
-
|
75
|
-
def get_address(address
|
75
|
+
|
76
|
+
def get_address(address)
|
76
77
|
params = build_basic_request()
|
77
78
|
params['address'] = address
|
78
|
-
|
79
|
-
response = Blockchain::call_api("merchant/#{@identifier}/address_balance", method: 'get', data: params)
|
79
|
+
response = @client.call_api("merchant/#{@identifier}/address_balance", method: 'get', data: params)
|
80
80
|
json_response = parse_json(response)
|
81
81
|
return WalletAddress.new(json_response['balance'],
|
82
82
|
json_response['address'],
|
83
83
|
nil,
|
84
84
|
json_response['total_received'])
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
def new_address(label = nil)
|
88
88
|
params = build_basic_request()
|
89
89
|
if !label.nil? then params['label'] = label end
|
90
|
-
response =
|
90
|
+
response = @client.call_api("merchant/#{@identifier}/new_address", method: 'post', data: params)
|
91
91
|
json_response = parse_json(response)
|
92
92
|
return WalletAddress.new(0,
|
93
93
|
json_response['address'],
|
94
94
|
json_response['label'],
|
95
95
|
0)
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
98
|
def archive_address(address)
|
99
99
|
params = build_basic_request()
|
100
100
|
params['address'] = address
|
101
|
-
response =
|
101
|
+
response = @client.call_api("merchant/#{@identifier}/archive_address", method: 'post', data: params)
|
102
102
|
json_response = parse_json(response)
|
103
103
|
return json_response['archived']
|
104
104
|
end
|
105
|
-
|
105
|
+
|
106
106
|
def unarchive_address(address)
|
107
107
|
params = build_basic_request()
|
108
108
|
params['address'] = address
|
109
|
-
response =
|
109
|
+
response = @client.call_api("merchant/#{@identifier}/unarchive_address", method: 'post', data: params)
|
110
110
|
json_response = parse_json(response)
|
111
111
|
return json_response['active']
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
def build_basic_request()
|
115
115
|
params = { 'password' => @password }
|
116
116
|
if !@second_password.nil?
|
117
117
|
params['second_password'] = @second_password
|
118
118
|
end
|
119
|
-
if !@api_code.nil?
|
120
|
-
params['api_code'] = @api_code
|
121
|
-
end
|
122
119
|
return params
|
123
120
|
end
|
124
|
-
|
121
|
+
|
125
122
|
# convenience method that parses a response into json AND makes sure there are no errors
|
126
123
|
def parse_json(response)
|
127
124
|
json_response = JSON.parse(response)
|
@@ -136,7 +133,7 @@ module Blockchain
|
|
136
133
|
attr_reader :address
|
137
134
|
attr_reader :label
|
138
135
|
attr_reader :total_received
|
139
|
-
|
136
|
+
|
140
137
|
def initialize(balance, address, label, total_received)
|
141
138
|
@balance = balance
|
142
139
|
@address = address
|
@@ -149,12 +146,12 @@ module Blockchain
|
|
149
146
|
attr_reader :message
|
150
147
|
attr_reader :tx_hash
|
151
148
|
attr_reader :notice
|
152
|
-
|
149
|
+
|
153
150
|
def initialize(message, tx_hash, notice)
|
154
151
|
@message = message
|
155
152
|
@tx_hash = tx_hash
|
156
153
|
@notice = notice
|
157
154
|
end
|
158
155
|
end
|
159
|
-
|
156
|
+
|
160
157
|
end
|