stellar-horizon 0.29.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 +7 -0
- data/CHANGELOG.md +13 -0
- data/LICENSE +0 -0
- data/README.md +0 -0
- data/lib/stellar/horizon/client.rb +298 -0
- data/lib/stellar-horizon.rb +9 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7f21b25ec3403a5aa4a6d118fb428e7e4de949ea8530ab7fc10d99f96de75477
|
4
|
+
data.tar.gz: 79258881494240f18edaaba909e7afb2602ebdeaadd0cf47959383745d8d7870
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 42d1ebd3df2be5f4d436e6d83e2d04fd643aa751bc490e546420e844b7b3085fb9124139d47a0ee87d6cb41fc3a743f654b52ac8f0c0334c758f10b972da2d91
|
7
|
+
data.tar.gz: 7658aac83aa40803f806eb95e7d3728c2a9bada248f0330883efe75cc99b4eaf76a0d7c4f64d9aa4557015553649e3d6f0e25025faf6e2e5b432196fcb33963d
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this
|
4
|
+
file. The format is based on [Keep a Changelog](https://keepachangelog.com/)
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
6
|
+
|
7
|
+
As this project is pre 1.0, breaking changes may happen for minor version
|
8
|
+
bumps. A breaking change will get clearly notified in this log.
|
9
|
+
|
10
|
+
## [0.29.0](https://www.github.com/astroband/ruby-stellar-sdk/compare/v0.28.0...v0.29.0) (2021-09-07)
|
11
|
+
|
12
|
+
### Added
|
13
|
+
* Initial setup of the gem, copying all Horizon-related features from `sdk` gem
|
data/LICENSE
ADDED
File without changes
|
data/README.md
ADDED
File without changes
|
@@ -0,0 +1,298 @@
|
|
1
|
+
require "hyperclient"
|
2
|
+
require "active_support/core_ext/object/blank"
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
module Stellar::Horizon
|
6
|
+
class AccountRequiresMemoError < StandardError
|
7
|
+
attr_reader :account_id, :operation_index
|
8
|
+
|
9
|
+
def initialize(message, account_id, operation_index)
|
10
|
+
super(message)
|
11
|
+
@account_id = account_id
|
12
|
+
@operation_index = operation_index
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Client
|
17
|
+
DEFAULT_FEE = 100
|
18
|
+
|
19
|
+
HORIZON_LOCALHOST_URL = "http://127.0.0.1:8000"
|
20
|
+
HORIZON_MAINNET_URL = "https://horizon.stellar.org"
|
21
|
+
HORIZON_TESTNET_URL = "https://horizon-testnet.stellar.org"
|
22
|
+
FRIENDBOT_URL = "https://friendbot.stellar.org".freeze
|
23
|
+
|
24
|
+
def self.default(options = {})
|
25
|
+
new options.merge(
|
26
|
+
horizon: HORIZON_MAINNET_URL
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.default_testnet(options = {})
|
31
|
+
new options.merge(
|
32
|
+
horizon: HORIZON_TESTNET_URL,
|
33
|
+
friendbot: HORIZON_TESTNET_URL
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.localhost(options = {})
|
38
|
+
new options.merge(
|
39
|
+
horizon: HORIZON_LOCALHOST_URL
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :horizon
|
44
|
+
|
45
|
+
# @option options [String] :horizon The Horizon server URL.
|
46
|
+
def initialize(options)
|
47
|
+
@options = options
|
48
|
+
@horizon = Hyperclient.new(options[:horizon]) { |client|
|
49
|
+
client.faraday_block = lambda do |conn|
|
50
|
+
conn.use Faraday::Response::RaiseError
|
51
|
+
conn.use FaradayMiddleware::FollowRedirects
|
52
|
+
conn.request :url_encoded
|
53
|
+
conn.response :hal_json, content_type: /\bjson$/
|
54
|
+
conn.adapter :excon
|
55
|
+
end
|
56
|
+
client.headers = {
|
57
|
+
"Accept" => "application/hal+json,application/problem+json,application/json",
|
58
|
+
"X-Client-Name" => "ruby-stellar-sdk",
|
59
|
+
"X-Client-Version" => Stellar::Horizon::VERSION
|
60
|
+
}
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param [Stellar::Account|String] account_or_address
|
65
|
+
def account_info(account_or_address)
|
66
|
+
account_id = if account_or_address.is_a?(Stellar::Account)
|
67
|
+
account_or_address.address
|
68
|
+
else
|
69
|
+
account_or_address
|
70
|
+
end
|
71
|
+
@horizon.account(account_id: account_id)._get
|
72
|
+
end
|
73
|
+
|
74
|
+
# @option options [Stellar::Account] :account
|
75
|
+
# @option options [Stellar::Account] :destination
|
76
|
+
def account_merge(options = {})
|
77
|
+
account = options[:account]
|
78
|
+
destination = options[:destination]
|
79
|
+
sequence = options[:sequence] || (account_info(account).sequence.to_i + 1)
|
80
|
+
|
81
|
+
transaction = Stellar::TransactionBuilder.account_merge(
|
82
|
+
source_account: destination.keypair,
|
83
|
+
sequence_number: sequence,
|
84
|
+
destination: destination.keypair
|
85
|
+
)
|
86
|
+
|
87
|
+
envelope = transaction.to_envelope(account.keypair)
|
88
|
+
submit_transaction(tx_envelope: envelope)
|
89
|
+
end
|
90
|
+
|
91
|
+
def friendbot(account)
|
92
|
+
uri = URI.parse(FRIENDBOT_URL)
|
93
|
+
uri.query = "addr=#{account.address}"
|
94
|
+
Faraday.post(uri.to_s)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @option options [Stellar::Account] :account
|
98
|
+
# @option options [Stellar::Account] :funder
|
99
|
+
# @option options [Integer] :starting_balance
|
100
|
+
def create_account(options = {})
|
101
|
+
funder = options[:funder]
|
102
|
+
sequence = options[:sequence] || (account_info(funder).sequence.to_i + 1)
|
103
|
+
# In the future, the fee should be grabbed from the network's last transactions,
|
104
|
+
# instead of using a hard-coded default value.
|
105
|
+
fee = options[:fee] || DEFAULT_FEE
|
106
|
+
|
107
|
+
payment = Stellar::TransactionBuilder.create_account(
|
108
|
+
source_account: funder.keypair,
|
109
|
+
sequence_number: sequence,
|
110
|
+
base_fee: fee,
|
111
|
+
destination: options[:account].keypair,
|
112
|
+
starting_balance: options[:starting_balance]
|
113
|
+
)
|
114
|
+
envelope = payment.to_envelope(funder.keypair)
|
115
|
+
submit_transaction(tx_envelope: envelope)
|
116
|
+
end
|
117
|
+
|
118
|
+
# @option options [Stellar::Account] :from The source account
|
119
|
+
# @option options [Stellar::Account] :to The destination account
|
120
|
+
# @option options [Stellar::Amount] :amount The amount to send
|
121
|
+
def send_payment(options = {})
|
122
|
+
from_account = options[:from]
|
123
|
+
tx_source_account = options[:transaction_source] || from_account
|
124
|
+
op_source_account = from_account if tx_source_account.present?
|
125
|
+
|
126
|
+
sequence = options[:sequence] ||
|
127
|
+
(account_info(tx_source_account).sequence.to_i + 1)
|
128
|
+
|
129
|
+
payment = Stellar::TransactionBuilder.new(
|
130
|
+
source_account: tx_source_account.keypair,
|
131
|
+
sequence_number: sequence
|
132
|
+
).add_operation(
|
133
|
+
Stellar::Operation.payment(
|
134
|
+
source_account: op_source_account.keypair,
|
135
|
+
destination: options[:to].keypair,
|
136
|
+
amount: options[:amount].to_payment
|
137
|
+
)
|
138
|
+
).set_memo(options[:memo]).set_timeout(0).build
|
139
|
+
|
140
|
+
signers = [tx_source_account, op_source_account].uniq(&:address)
|
141
|
+
to_envelope_args = signers.map(&:keypair)
|
142
|
+
|
143
|
+
envelope = payment.to_envelope(*to_envelope_args)
|
144
|
+
submit_transaction(tx_envelope: envelope)
|
145
|
+
end
|
146
|
+
|
147
|
+
# @option options [Stellar::Account] :account
|
148
|
+
# @option options [Integer] :limit
|
149
|
+
# @option options [Integer] :cursor
|
150
|
+
# @return [Stellar::TransactionPage]
|
151
|
+
def transactions(options = {})
|
152
|
+
args = options.slice(:limit, :cursor)
|
153
|
+
|
154
|
+
resource = if options[:account]
|
155
|
+
args = args.merge(account_id: options[:account].address)
|
156
|
+
@horizon.account_transactions(args)
|
157
|
+
else
|
158
|
+
@horizon.transactions(args)
|
159
|
+
end
|
160
|
+
|
161
|
+
Stellar::TransactionPage.new(resource)
|
162
|
+
end
|
163
|
+
|
164
|
+
# @param [Array(Symbol,String,Stellar::KeyPair|Stellar::Account)] asset
|
165
|
+
# @param [Stellar::Account] source
|
166
|
+
# @param [Integer] sequence
|
167
|
+
# @param [Integer] fee
|
168
|
+
# @param [Integer] limit
|
169
|
+
def change_trust(
|
170
|
+
asset:,
|
171
|
+
source:,
|
172
|
+
sequence: nil,
|
173
|
+
fee: DEFAULT_FEE,
|
174
|
+
limit: nil
|
175
|
+
)
|
176
|
+
sequence ||= (account_info(source).sequence.to_i + 1)
|
177
|
+
|
178
|
+
op_args = {
|
179
|
+
account: source.keypair,
|
180
|
+
sequence: sequence,
|
181
|
+
line: asset
|
182
|
+
}
|
183
|
+
op_args[:limit] = limit unless limit.nil?
|
184
|
+
|
185
|
+
tx = Stellar::TransactionBuilder.change_trust(
|
186
|
+
source_account: source.keypair,
|
187
|
+
sequence_number: sequence,
|
188
|
+
**op_args
|
189
|
+
)
|
190
|
+
|
191
|
+
envelope = tx.to_envelope(source.keypair)
|
192
|
+
submit_transaction(tx_envelope: envelope)
|
193
|
+
end
|
194
|
+
|
195
|
+
# @param [Stellar::TransactionEnvelope] tx_envelope
|
196
|
+
# @option options [Boolean] :skip_memo_required_check (false)
|
197
|
+
def submit_transaction(tx_envelope:, options: {skip_memo_required_check: false})
|
198
|
+
unless options[:skip_memo_required_check]
|
199
|
+
check_memo_required(tx_envelope)
|
200
|
+
end
|
201
|
+
@horizon.transactions._post(tx: tx_envelope.to_xdr(:base64))
|
202
|
+
end
|
203
|
+
|
204
|
+
# Required by SEP-0029
|
205
|
+
# @param [Stellar::TransactionEnvelope] tx_envelope
|
206
|
+
def check_memo_required(tx_envelope)
|
207
|
+
tx = tx_envelope.tx
|
208
|
+
|
209
|
+
if tx.is_a?(Stellar::FeeBumpTransaction)
|
210
|
+
tx = tx.inner_tx.v1!.tx
|
211
|
+
end
|
212
|
+
|
213
|
+
# Check transactions where the .memo field is nil or of type MemoType.memo_none
|
214
|
+
if !tx.memo.nil? && tx.memo.type != Stellar::MemoType.memo_none
|
215
|
+
return
|
216
|
+
end
|
217
|
+
|
218
|
+
destinations = Set.new
|
219
|
+
ot = Stellar::OperationType
|
220
|
+
|
221
|
+
tx.operations.each_with_index do |op, idx|
|
222
|
+
destination = case op.body.type
|
223
|
+
when ot.payment, ot.path_payment_strict_receive, ot.path_payment_strict_send
|
224
|
+
op.body.value.destination
|
225
|
+
when ot.account_merge
|
226
|
+
# There is no AccountMergeOp, op.body is an Operation object
|
227
|
+
# and op.body.value is a PublicKey (or AccountID) object.
|
228
|
+
op.body.value
|
229
|
+
else
|
230
|
+
next
|
231
|
+
end
|
232
|
+
|
233
|
+
if destinations.include?(destination) || destination.switch == Stellar::CryptoKeyType.key_type_muxed_ed25519
|
234
|
+
next
|
235
|
+
end
|
236
|
+
|
237
|
+
destinations.add(destination)
|
238
|
+
kp = Stellar::KeyPair.from_public_key(destination.value)
|
239
|
+
|
240
|
+
begin
|
241
|
+
info = account_info(kp.address)
|
242
|
+
rescue Faraday::ResourceNotFound
|
243
|
+
# Don't raise an error if its a 404, but throw one otherwise
|
244
|
+
next
|
245
|
+
end
|
246
|
+
if info.data["config.memo_required"] == "MQ=="
|
247
|
+
# MQ== is the base64 encoded string for the string "1"
|
248
|
+
raise AccountRequiresMemoError.new("account requires memo", destination, idx)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# DEPRECATED: this function has been moved Stellar::SEP10.build_challenge_tx and
|
254
|
+
# will be removed in the next major version release.
|
255
|
+
#
|
256
|
+
# A wrapper function for Stellar::SEP10::build_challenge_tx.
|
257
|
+
#
|
258
|
+
# @param server [Stellar::KeyPair] Keypair for server's signing account.
|
259
|
+
# @param client [Stellar::KeyPair] Keypair for the account whishing to authenticate with the server.
|
260
|
+
# @param anchor_name [String] Anchor's name to be used in the manage_data key.
|
261
|
+
# @param timeout [Integer] Challenge duration (default to 5 minutes).
|
262
|
+
#
|
263
|
+
# @return [String] A base64 encoded string of the raw TransactionEnvelope xdr struct for the transaction.
|
264
|
+
def build_challenge_tx(server:, client:, anchor_name:, timeout: 300)
|
265
|
+
Stellar::SEP10.build_challenge_tx(
|
266
|
+
server: server, client: client, anchor_name: anchor_name, timeout: timeout
|
267
|
+
)
|
268
|
+
end
|
269
|
+
|
270
|
+
# DEPRECATED: this function has been moved to Stellar::SEP10::read_challenge_tx and
|
271
|
+
# will be removed in the next major version release.
|
272
|
+
#
|
273
|
+
# A wrapper function for Stellar::SEP10.verify_challenge_transaction
|
274
|
+
#
|
275
|
+
# @param challenge [String] SEP0010 transaction challenge in base64.
|
276
|
+
# @param server [Stellar::KeyPair] Stellar::KeyPair for server where the challenge was generated.
|
277
|
+
#
|
278
|
+
# @return [Boolean]
|
279
|
+
def verify_challenge_tx(challenge:, server:)
|
280
|
+
Stellar::SEP10.verify_challenge_tx(challenge_xdr: challenge, server: server)
|
281
|
+
true
|
282
|
+
end
|
283
|
+
|
284
|
+
# DEPRECATED: this function has been moved to Stellar::SEP10::verify_tx_signed_by and
|
285
|
+
# will be removed in the next major version release.
|
286
|
+
#
|
287
|
+
# @param transaction_envelope [Stellar::TransactionEnvelope]
|
288
|
+
# @param keypair [Stellar::KeyPair]
|
289
|
+
#
|
290
|
+
# @return [Boolean]
|
291
|
+
#
|
292
|
+
def verify_tx_signed_by(transaction_envelope:, keypair:)
|
293
|
+
Stellar::SEP10.verify_tx_signed_by(
|
294
|
+
tx_envelope: transaction_envelope, keypair: keypair
|
295
|
+
)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
metadata
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stellar-horizon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.29.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergey Nebolsin
|
8
|
+
- Timur Ramazanov
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2021-09-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: stellar-sdk
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.29.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.29.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: excon
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.71.0
|
35
|
+
- - "<"
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 0.71.0
|
45
|
+
- - "<"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: hyperclient
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.7.0
|
55
|
+
- - "<"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '2.0'
|
58
|
+
type: :runtime
|
59
|
+
prerelease: false
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 0.7.0
|
65
|
+
- - "<"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: toml-rb
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.1.1
|
75
|
+
- - "<"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.0'
|
78
|
+
type: :runtime
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 1.1.1
|
85
|
+
- - "<"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '3.0'
|
88
|
+
- !ruby/object:Gem::Dependency
|
89
|
+
name: bundler
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '2.0'
|
95
|
+
type: :development
|
96
|
+
prerelease: false
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '2.0'
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: rake
|
104
|
+
requirement: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '13'
|
109
|
+
type: :development
|
110
|
+
prerelease: false
|
111
|
+
version_requirements: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '13'
|
116
|
+
- !ruby/object:Gem::Dependency
|
117
|
+
name: rspec
|
118
|
+
requirement: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '3.9'
|
123
|
+
type: :development
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '3.9'
|
130
|
+
description:
|
131
|
+
email:
|
132
|
+
executables: []
|
133
|
+
extensions: []
|
134
|
+
extra_rdoc_files:
|
135
|
+
- README.md
|
136
|
+
- LICENSE
|
137
|
+
- CHANGELOG.md
|
138
|
+
files:
|
139
|
+
- CHANGELOG.md
|
140
|
+
- LICENSE
|
141
|
+
- README.md
|
142
|
+
- lib/stellar-horizon.rb
|
143
|
+
- lib/stellar/horizon/client.rb
|
144
|
+
homepage: https://github.com/stellar/ruby-stellar-sdk/tree/master/horizon
|
145
|
+
licenses:
|
146
|
+
- Apache-2.0
|
147
|
+
metadata:
|
148
|
+
github_repo: ssh://github.com/astroband/ruby-stellar-sdk
|
149
|
+
documentation_uri: https://rubydoc.info/gems/stellar-sdk/0.29.0/
|
150
|
+
changelog_uri: https://github.com/astroband/ruby-stellar-sdk/blob/v0.29.0/horizon/CHANGELOG.md
|
151
|
+
source_code_uri: https://github.com/astroband/ruby-stellar-sdk/tree/v0.29.0/horizon
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: 2.5.0
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubygems_version: 3.1.4
|
168
|
+
signing_key:
|
169
|
+
specification_version: 4
|
170
|
+
summary: Stellar Horizon client library
|
171
|
+
test_files: []
|