Class: XRPL::Client
- Inherits:
-
Object
- Object
- XRPL::Client
- Defined in:
- lib/xrpl/client.rb
Constant Summary collapse
- MAINNET_URL =
'wss://s1.ripple.com'- TESTNET_URL =
'wss://s.altnet.rippletest.net:51233'- DEVNET_URL =
'wss://s.devnet.rippletest.net:51233'- NETWORK_URLS =
{ 'mainnet' => MAINNET_URL, 'testnet' => TESTNET_URL, 'devnet' => DEVNET_URL }.freeze
- LEDGER_OFFSET =
Added to the current ledger index to set LastLedgerSequence during autofill.
20- LEDGER_CLOSE_TIME =
Approximate seconds between validated ledgers; used when polling for finality.
3- DEFAULT_FEE_DROPS =
Default fee (drops) if the server's fee cannot be determined.
10
Instance Attribute Summary collapse
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
-
#url ⇒ Object
readonly
Returns the value of attribute url.
Class Method Summary collapse
-
.to_transaction_hash(transaction) ⇒ Hash, Object
Accepts a transaction as a plain Hash or as an XRPL::Transaction and hands back the Hash the rest of the pipeline works with.
Instance Method Summary collapse
- #account_channels(**params) ⇒ Object
- #account_currencies(**params) ⇒ Object
- #account_info(**params) ⇒ Object
- #account_info_response(**params) ⇒ Object
- #account_lines(**params) ⇒ Object
- #account_nfts(**params) ⇒ Object
- #account_objects(**params) ⇒ Object
- #account_offers(**params) ⇒ Object
- #account_tx(**params) ⇒ Object
- #account_tx_all(**params) ⇒ Object
- #account_tx_response(**params) ⇒ Object
-
#autofill(transaction, signers_count: 0) ⇒ Hash
Fills in the fields a transaction needs before signing:
Sequence,FeeandLastLedgerSequence. -
#connect(wait: false, timeout: 10) ⇒ self
Opens the WebSocket connection.
-
#connect!(timeout: 10) ⇒ self
Opens the connection and blocks until it is ready to accept requests.
- #disconnect ⇒ Object
- #fee(**params) ⇒ Object
- #fee_response(**params) ⇒ Object
- #gateway_balances(**params) ⇒ Object
-
#initialize(url, logger: nil) ⇒ Client
constructor
A new instance of Client.
- #ledger(**params) ⇒ Object
- #ledger_closed(**params) ⇒ Object
- #ledger_current(**params) ⇒ Object
- #ledger_data(**params) ⇒ Object
- #ledger_entry(**params) ⇒ Object
- #noripple_check(**params) ⇒ Object
-
#open? ⇒ Boolean
Whether the WebSocket connection is currently open.
- #request(command, params = {}) ⇒ Object
- #request_with_response(command, params = {}, timeout: 10) ⇒ Object
- #request_with_retry(command, params = {}, max_attempts: 3, timeout: 10, retry_exceptions: [RuntimeError, Timeout::Error], **keyword_params) ⇒ Object
-
#submit(transaction, wallet:, autofill: true, fail_hard: false) ⇒ Hash
Autofills (optional), signs with the given wallet and submits a transaction.
-
#submit_and_wait(transaction, wallet:, autofill: true, fail_hard: false, timeout: 20) ⇒ Hash
Like #submit, but then polls the ledger until the transaction is final (included in a validated ledger, or definitively failed/expired).
- #subscribe(**params) ⇒ Object
- #summarize_account_tx(response) ⇒ Object
- #tx(**params) ⇒ Object
- #tx_response(**params) ⇒ Object
- #unsubscribe(**params) ⇒ Object
-
#wait_until_open(timeout: 10) ⇒ true
Blocks the calling thread until the connection is open.
Constructor Details
#initialize(url, logger: nil) ⇒ Client
Returns a new instance of Client.
55 56 57 58 59 60 61 62 |
# File 'lib/xrpl/client.rb', line 55 def initialize(url, logger: nil) @url = resolve_url(url) @connection = nil @requests = {} @open = false @ready_queue = Queue.new @logger = logger end |
Instance Attribute Details
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
36 37 38 |
# File 'lib/xrpl/client.rb', line 36 def connection @connection end |
#url ⇒ Object (readonly)
Returns the value of attribute url.
36 37 38 |
# File 'lib/xrpl/client.rb', line 36 def url @url end |
Class Method Details
.to_transaction_hash(transaction) ⇒ Hash, Object
Accepts a transaction as a plain Hash or as an XRPL::Transaction and hands back the Hash the rest of the pipeline works with. Anything else is passed through untouched, so a pre-signed blob still reaches submit.
44 45 46 47 48 49 |
# File 'lib/xrpl/client.rb', line 44 def self.to_transaction_hash(transaction) return transaction if transaction.is_a?(Hash) return transaction.to_h if transaction.is_a?(XRPL::Transaction) transaction end |
Instance Method Details
#account_channels(**params) ⇒ Object
198 199 200 |
# File 'lib/xrpl/client.rb', line 198 def account_channels(**params) request('account_channels', **params) end |
#account_currencies(**params) ⇒ Object
202 203 204 |
# File 'lib/xrpl/client.rb', line 202 def account_currencies(**params) request('account_currencies', **params) end |
#account_info(**params) ⇒ Object
206 207 208 |
# File 'lib/xrpl/client.rb', line 206 def account_info(**params) request('account_info', **params) end |
#account_info_response(**params) ⇒ Object
210 211 212 |
# File 'lib/xrpl/client.rb', line 210 def account_info_response(**params) request_with_retry('account_info', params) end |
#account_lines(**params) ⇒ Object
214 215 216 |
# File 'lib/xrpl/client.rb', line 214 def account_lines(**params) request('account_lines', **params) end |
#account_nfts(**params) ⇒ Object
218 219 220 |
# File 'lib/xrpl/client.rb', line 218 def account_nfts(**params) request('account_nfts', **params) end |
#account_objects(**params) ⇒ Object
222 223 224 |
# File 'lib/xrpl/client.rb', line 222 def account_objects(**params) request('account_objects', **params) end |
#account_offers(**params) ⇒ Object
226 227 228 |
# File 'lib/xrpl/client.rb', line 226 def account_offers(**params) request('account_offers', **params) end |
#account_tx(**params) ⇒ Object
230 231 232 |
# File 'lib/xrpl/client.rb', line 230 def account_tx(**params) request('account_tx', **params) end |
#account_tx_all(**params) ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/xrpl/client.rb', line 238 def account_tx_all(**params) page_limit = params.delete(:page_limit) max_attempts = params.delete(:max_attempts) || 3 timeout = params.delete(:timeout) || 10 current_params = params.dup responses = [] loop do response = request_with_retry('account_tx', current_params, max_attempts: max_attempts, timeout: timeout) responses << response marker = response.dig('result', 'marker') break unless marker break if page_limit && responses.size >= page_limit current_params = current_params.merge(marker: marker) end responses end |
#account_tx_response(**params) ⇒ Object
234 235 236 |
# File 'lib/xrpl/client.rb', line 234 def account_tx_response(**params) request_with_retry('account_tx', params) end |
#autofill(transaction, signers_count: 0) ⇒ Hash
Fills in the fields a transaction needs before signing: Sequence, Fee
and LastLedgerSequence. Existing values are never overwritten.
325 326 327 328 329 330 331 |
# File 'lib/xrpl/client.rb', line 325 def autofill(transaction, signers_count: 0) tx = self.class.to_transaction_hash(transaction).dup tx['Sequence'] ||= fetch_sequence(tx.fetch('Account')) tx['Fee'] ||= calculate_fee(signers_count) tx['LastLedgerSequence'] ||= current_ledger_index + LEDGER_OFFSET tx end |
#connect(wait: false, timeout: 10) ⇒ self
Opens the WebSocket connection.
By default this is non-blocking (preserving the previous behaviour) and
returns self. Pass wait: true (or use #connect!) to block
until the socket is actually open, so a following request can't race with
connection setup and hit "Not connected".
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 |
# File 'lib/xrpl/client.rb', line 74 def connect(wait: false, timeout: 10) @open = false @ready_queue = Queue.new Thread.new { EM.run } unless EM.reactor_running? EM.next_tick do @connection = Faye::WebSocket::Client.new(@url) @connection.on :open do |event| @open = true @ready_queue.push(:open) log("Connected to #{@url}") end @connection.on :message do |event| (JSON.parse(event.data)) end @connection.on :error do |event| @ready_queue.push([:error, event.]) end @connection.on :close do |event| @open = false @connection = nil log("Connection closed: #{event.code} #{event.reason}") end end wait_until_open(timeout: timeout) if wait self end |
#connect!(timeout: 10) ⇒ self
Opens the connection and blocks until it is ready to accept requests.
112 113 114 |
# File 'lib/xrpl/client.rb', line 112 def connect!(timeout: 10) connect(wait: true, timeout: timeout) end |
#disconnect ⇒ Object
140 141 142 |
# File 'lib/xrpl/client.rb', line 140 def disconnect @connection&.close end |
#fee(**params) ⇒ Object
301 302 303 |
# File 'lib/xrpl/client.rb', line 301 def fee(**params) request('fee', **params) end |
#fee_response(**params) ⇒ Object
305 306 307 |
# File 'lib/xrpl/client.rb', line 305 def fee_response(**params) request_with_retry('fee', params) end |
#gateway_balances(**params) ⇒ Object
273 274 275 |
# File 'lib/xrpl/client.rb', line 273 def gateway_balances(**params) request('gateway_balances', **params) end |
#ledger(**params) ⇒ Object
281 282 283 |
# File 'lib/xrpl/client.rb', line 281 def ledger(**params) request('ledger', **params) end |
#ledger_closed(**params) ⇒ Object
285 286 287 |
# File 'lib/xrpl/client.rb', line 285 def ledger_closed(**params) request('ledger_closed', **params) end |
#ledger_current(**params) ⇒ Object
289 290 291 |
# File 'lib/xrpl/client.rb', line 289 def ledger_current(**params) request('ledger_current', **params) end |
#ledger_data(**params) ⇒ Object
293 294 295 |
# File 'lib/xrpl/client.rb', line 293 def ledger_data(**params) request('ledger_data', **params) end |
#ledger_entry(**params) ⇒ Object
297 298 299 |
# File 'lib/xrpl/client.rb', line 297 def ledger_entry(**params) request('ledger_entry', **params) end |
#noripple_check(**params) ⇒ Object
277 278 279 |
# File 'lib/xrpl/client.rb', line 277 def noripple_check(**params) request('noripple_check', **params) end |
#open? ⇒ Boolean
Returns whether the WebSocket connection is currently open.
117 118 119 |
# File 'lib/xrpl/client.rb', line 117 def open? @open end |
#request(command, params = {}) ⇒ Object
144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/xrpl/client.rb', line 144 def request(command, params = {}) id = SecureRandom.uuid register_pending_request(id) payload = { id: id, command: command }.merge(params) (payload) # TODO: Implement promise/future or callback for response id end |
#request_with_response(command, params = {}, timeout: 10) ⇒ Object
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/xrpl/client.rb', line 157 def request_with_response(command, params = {}, timeout: 10) id = SecureRandom.uuid queue = Queue.new register_pending_request(id, queue: queue) payload = { id: id, command: command }.merge(params) (payload) Timeout.timeout(timeout) { queue.pop } rescue Timeout::Error @requests.delete(id) raise Timeout::Error, "Request timed out after #{timeout} seconds" end |
#request_with_retry(command, params = {}, max_attempts: 3, timeout: 10, retry_exceptions: [RuntimeError, Timeout::Error], **keyword_params) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/xrpl/client.rb', line 175 def request_with_retry(command, params = {}, max_attempts: 3, timeout: 10, retry_exceptions: [RuntimeError, Timeout::Error], **keyword_params) attempt = 0 request_params = keyword_params.empty? ? params : params.merge(keyword_params) begin attempt += 1 request_with_response(command, request_params, timeout: timeout) rescue *retry_exceptions => error raise error if attempt >= max_attempts retry end end |
#submit(transaction, wallet:, autofill: true, fail_hard: false) ⇒ Hash
Autofills (optional), signs with the given wallet and submits a transaction.
340 341 342 343 |
# File 'lib/xrpl/client.rb', line 340 def submit(transaction, wallet:, autofill: true, fail_hard: false) prepared = prepare_for_submit(transaction, wallet: wallet, autofill: autofill) submit_blob(prepared[:tx_blob], fail_hard: fail_hard) end |
#submit_and_wait(transaction, wallet:, autofill: true, fail_hard: false, timeout: 20) ⇒ Hash
Like #submit, but then polls the ledger until the transaction is final (included in a validated ledger, or definitively failed/expired).
355 356 357 358 359 360 361 362 363 364 365 366 |
# File 'lib/xrpl/client.rb', line 355 def submit_and_wait(transaction, wallet:, autofill: true, fail_hard: false, timeout: 20) prepared = prepare_for_submit(transaction, wallet: wallet, autofill: autofill) last_ledger = prepared[:tx]['LastLedgerSequence'] unless last_ledger raise ArgumentError, 'Transaction must contain a LastLedgerSequence for reliable submission' end response = submit_blob(prepared[:tx_blob], fail_hard: fail_hard) preliminary = response.dig('result', 'engine_result') wait_for_final_outcome(prepared[:hash], last_ledger, preliminary, timeout: timeout) end |
#subscribe(**params) ⇒ Object
190 191 192 |
# File 'lib/xrpl/client.rb', line 190 def subscribe(**params) request('subscribe', **params) end |
#summarize_account_tx(response) ⇒ Object
260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/xrpl/client.rb', line 260 def summarize_account_tx(response) result = response.fetch('result', {}) transactions = Array(result['transactions']) { 'ledger_index_min' => result['ledger_index_min'], 'ledger_index_max' => result['ledger_index_max'], 'transaction_count' => transactions.size, 'validated' => result['validated'] == true, 'marker_present' => !result['marker'].nil? } end |
#tx(**params) ⇒ Object
309 310 311 |
# File 'lib/xrpl/client.rb', line 309 def tx(**params) request('tx', **params) end |
#tx_response(**params) ⇒ Object
313 314 315 |
# File 'lib/xrpl/client.rb', line 313 def tx_response(**params) request_with_retry('tx', params) end |
#unsubscribe(**params) ⇒ Object
194 195 196 |
# File 'lib/xrpl/client.rb', line 194 def unsubscribe(**params) request('unsubscribe', **params) end |
#wait_until_open(timeout: 10) ⇒ true
Blocks the calling thread until the connection is open.
127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/xrpl/client.rb', line 127 def wait_until_open(timeout: 10) return true if @open signal = Timeout.timeout(timeout) { @ready_queue.pop } if signal.is_a?(Array) && signal.first == :error raise ConnectionError, "WebSocket connection failed: #{signal.last}" end true rescue Timeout::Error raise Timeout::Error, "Connection did not open within #{timeout} seconds" end |