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.
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.
42 43 44 45 46 47 48 49 |
# File 'lib/xrpl/client.rb', line 42 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 |
Instance Method Details
#account_channels(**params) ⇒ Object
185 186 187 |
# File 'lib/xrpl/client.rb', line 185 def account_channels(**params) request('account_channels', **params) end |
#account_currencies(**params) ⇒ Object
189 190 191 |
# File 'lib/xrpl/client.rb', line 189 def account_currencies(**params) request('account_currencies', **params) end |
#account_info(**params) ⇒ Object
193 194 195 |
# File 'lib/xrpl/client.rb', line 193 def account_info(**params) request('account_info', **params) end |
#account_info_response(**params) ⇒ Object
197 198 199 |
# File 'lib/xrpl/client.rb', line 197 def account_info_response(**params) request_with_retry('account_info', params) end |
#account_lines(**params) ⇒ Object
201 202 203 |
# File 'lib/xrpl/client.rb', line 201 def account_lines(**params) request('account_lines', **params) end |
#account_nfts(**params) ⇒ Object
205 206 207 |
# File 'lib/xrpl/client.rb', line 205 def account_nfts(**params) request('account_nfts', **params) end |
#account_objects(**params) ⇒ Object
209 210 211 |
# File 'lib/xrpl/client.rb', line 209 def account_objects(**params) request('account_objects', **params) end |
#account_offers(**params) ⇒ Object
213 214 215 |
# File 'lib/xrpl/client.rb', line 213 def account_offers(**params) request('account_offers', **params) end |
#account_tx(**params) ⇒ Object
217 218 219 |
# File 'lib/xrpl/client.rb', line 217 def account_tx(**params) request('account_tx', **params) end |
#account_tx_all(**params) ⇒ Object
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/xrpl/client.rb', line 225 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
221 222 223 |
# File 'lib/xrpl/client.rb', line 221 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.
312 313 314 315 316 317 318 |
# File 'lib/xrpl/client.rb', line 312 def autofill(transaction, signers_count: 0) tx = 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".
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 |
# File 'lib/xrpl/client.rb', line 61 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.
99 100 101 |
# File 'lib/xrpl/client.rb', line 99 def connect!(timeout: 10) connect(wait: true, timeout: timeout) end |
#disconnect ⇒ Object
127 128 129 |
# File 'lib/xrpl/client.rb', line 127 def disconnect @connection&.close end |
#fee(**params) ⇒ Object
288 289 290 |
# File 'lib/xrpl/client.rb', line 288 def fee(**params) request('fee', **params) end |
#fee_response(**params) ⇒ Object
292 293 294 |
# File 'lib/xrpl/client.rb', line 292 def fee_response(**params) request_with_retry('fee', params) end |
#gateway_balances(**params) ⇒ Object
260 261 262 |
# File 'lib/xrpl/client.rb', line 260 def gateway_balances(**params) request('gateway_balances', **params) end |
#ledger(**params) ⇒ Object
268 269 270 |
# File 'lib/xrpl/client.rb', line 268 def ledger(**params) request('ledger', **params) end |
#ledger_closed(**params) ⇒ Object
272 273 274 |
# File 'lib/xrpl/client.rb', line 272 def ledger_closed(**params) request('ledger_closed', **params) end |
#ledger_current(**params) ⇒ Object
276 277 278 |
# File 'lib/xrpl/client.rb', line 276 def ledger_current(**params) request('ledger_current', **params) end |
#ledger_data(**params) ⇒ Object
280 281 282 |
# File 'lib/xrpl/client.rb', line 280 def ledger_data(**params) request('ledger_data', **params) end |
#ledger_entry(**params) ⇒ Object
284 285 286 |
# File 'lib/xrpl/client.rb', line 284 def ledger_entry(**params) request('ledger_entry', **params) end |
#noripple_check(**params) ⇒ Object
264 265 266 |
# File 'lib/xrpl/client.rb', line 264 def noripple_check(**params) request('noripple_check', **params) end |
#open? ⇒ Boolean
Returns whether the WebSocket connection is currently open.
104 105 106 |
# File 'lib/xrpl/client.rb', line 104 def open? @open end |
#request(command, params = {}) ⇒ Object
131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/xrpl/client.rb', line 131 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
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/xrpl/client.rb', line 144 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
162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/xrpl/client.rb', line 162 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.
327 328 329 330 |
# File 'lib/xrpl/client.rb', line 327 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).
342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/xrpl/client.rb', line 342 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
177 178 179 |
# File 'lib/xrpl/client.rb', line 177 def subscribe(**params) request('subscribe', **params) end |
#summarize_account_tx(response) ⇒ Object
247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/xrpl/client.rb', line 247 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
296 297 298 |
# File 'lib/xrpl/client.rb', line 296 def tx(**params) request('tx', **params) end |
#tx_response(**params) ⇒ Object
300 301 302 |
# File 'lib/xrpl/client.rb', line 300 def tx_response(**params) request_with_retry('tx', params) end |
#unsubscribe(**params) ⇒ Object
181 182 183 |
# File 'lib/xrpl/client.rb', line 181 def unsubscribe(**params) request('unsubscribe', **params) end |
#wait_until_open(timeout: 10) ⇒ true
Blocks the calling thread until the connection is open.
114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/xrpl/client.rb', line 114 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 |