|  | @@ -1,5 +1,5 @@
 | 
		
	
		
			
			| 1 | 1 |  import { defaultOptions, Memo, Options } from '../util/types'
 | 
		
	
		
			
			| 2 |  | -import { Client, Payment, TxResponse, Wallet } from 'xrpl'
 | 
		
	
		
			
			|  | 2 | +import { Client, Payment, RippledError, TxResponse, Wallet } from 'xrpl'
 | 
		
	
		
			
			| 3 | 3 |  
 | 
		
	
		
			
			| 4 | 4 |  import * as zlib from 'zlib'
 | 
		
	
		
			
			| 5 | 5 |  import * as util from 'util'
 | 
		
	
	
		
			
			|  | @@ -29,6 +29,7 @@ export class xrpIO {
 | 
		
	
		
			
			| 29 | 29 |      this.options.connectionTimeout = options.connectionTimeout ? Number(options.connectionTimeout) : defaultOptions.connectionTimeout
 | 
		
	
		
			
			| 30 | 30 |      this.options.readMaxRetry = options.readMaxRetry ? Number(options.readMaxRetry) : defaultOptions.readMaxRetry
 | 
		
	
		
			
			| 31 | 31 |      this.options.readRetryTimeout = options.readRetryTimeout ? Number(options.readRetryTimeout) : defaultOptions.readRetryTimeout
 | 
		
	
		
			
			|  | 32 | +    this.options.readFreshApi = options.readFreshApi ? Boolean(options.readFreshApi) : defaultOptions.readFreshApi
 | 
		
	
		
			
			| 32 | 33 |  
 | 
		
	
		
			
			| 33 | 34 |      this.api = new Client(server, {
 | 
		
	
		
			
			| 34 | 35 |        connectionTimeout: this.options.connectionTimeout
 | 
		
	
	
		
			
			|  | @@ -67,36 +68,36 @@ export class xrpIO {
 | 
		
	
		
			
			| 67 | 68 |      }
 | 
		
	
		
			
			| 68 | 69 |    }
 | 
		
	
		
			
			| 69 | 70 |  
 | 
		
	
		
			
			| 70 |  | -  private async sendPayment(data: Memo, to: string, secret: string, sequence?: number, amount: string = "1"): Promise<TxResponse> {
 | 
		
	
		
			
			|  | 71 | +  public async sendPayment(data: Memo, to: string, secret: string, sequence?: number, amount: string = "1"): Promise<TxResponse> {
 | 
		
	
		
			
			| 71 | 72 |      const wallet = Wallet.fromSecret(secret)
 | 
		
	
		
			
			| 72 | 73 |      this.dbg("Sending payment", wallet.address, '->', to)
 | 
		
	
		
			
			| 73 | 74 |  
 | 
		
	
		
			
			| 74 | 75 |      const _api = await this.cloneApi()
 | 
		
	
		
			
			| 75 |  | -    try {
 | 
		
	
		
			
			| 76 |  | -      const payment: Payment = await _api.autofill({
 | 
		
	
		
			
			| 77 |  | -        TransactionType: 'Payment',
 | 
		
	
		
			
			| 78 |  | -        Account: wallet.address,
 | 
		
	
		
			
			| 79 |  | -        Destination: to,
 | 
		
	
		
			
			| 80 |  | -        Sequence: sequence,
 | 
		
	
		
			
			| 81 |  | -        Amount: amount,
 | 
		
	
		
			
			| 82 |  | -        Memos: [{
 | 
		
	
		
			
			| 83 |  | -          Memo: {
 | 
		
	
		
			
			| 84 |  | -            MemoData: hexEncode(data.data || ""),
 | 
		
	
		
			
			| 85 |  | -            MemoFormat: hexEncode(data.format || ""),
 | 
		
	
		
			
			| 86 |  | -            MemoType: hexEncode(data.type || "")
 | 
		
	
		
			
			| 87 |  | -          }
 | 
		
	
		
			
			| 88 |  | -        }]
 | 
		
	
		
			
			| 89 |  | -      })
 | 
		
	
		
			
			| 90 |  | -
 | 
		
	
		
			
			|  | 76 | +    const payment: Payment = await _api.autofill({
 | 
		
	
		
			
			|  | 77 | +      TransactionType: 'Payment',
 | 
		
	
		
			
			|  | 78 | +      Account: wallet.address,
 | 
		
	
		
			
			|  | 79 | +      Destination: to,
 | 
		
	
		
			
			|  | 80 | +      Sequence: sequence,
 | 
		
	
		
			
			|  | 81 | +      Amount: amount,
 | 
		
	
		
			
			|  | 82 | +      Memos: [{
 | 
		
	
		
			
			|  | 83 | +        Memo: {
 | 
		
	
		
			
			|  | 84 | +          MemoData: hexEncode(data.data || ""),
 | 
		
	
		
			
			|  | 85 | +          MemoFormat: hexEncode(data.format || ""),
 | 
		
	
		
			
			|  | 86 | +          MemoType: hexEncode(data.type || "")
 | 
		
	
		
			
			|  | 87 | +        }
 | 
		
	
		
			
			|  | 88 | +      }]
 | 
		
	
		
			
			|  | 89 | +    })
 | 
		
	
		
			
			| 91 | 90 |  
 | 
		
	
		
			
			|  | 91 | +    try {
 | 
		
	
		
			
			| 92 | 92 |        const response = await _api.submitAndWait(payment, { wallet })
 | 
		
	
		
			
			| 93 |  | -      await _api.disconnect()
 | 
		
	
		
			
			| 94 | 93 |        this.dbg("Tx finalized", response.result.hash, response.result.Sequence)
 | 
		
	
		
			
			| 95 | 94 |        return response
 | 
		
	
		
			
			| 96 | 95 |      } catch (error: any) {
 | 
		
	
		
			
			| 97 | 96 |        this.dbg("SENDPAYMENT ERROR", error)
 | 
		
	
		
			
			| 98 |  | -      await _api.disconnect()
 | 
		
	
		
			
			|  | 97 | +      console.log("SENDPAYMENT ERROR", error)
 | 
		
	
		
			
			| 99 | 98 |        throw error
 | 
		
	
		
			
			|  | 99 | +    }finally{
 | 
		
	
		
			
			|  | 100 | +      await _api.disconnect()
 | 
		
	
		
			
			| 100 | 101 |      }
 | 
		
	
		
			
			| 101 | 102 |    }
 | 
		
	
		
			
			| 102 | 103 |  
 | 
		
	
	
		
			
			|  | @@ -106,22 +107,57 @@ export class xrpIO {
 | 
		
	
		
			
			| 106 | 107 |      return tx.result.hash
 | 
		
	
		
			
			| 107 | 108 |    }
 | 
		
	
		
			
			| 108 | 109 |  
 | 
		
	
		
			
			| 109 |  | -  private async getTransaction(hash: string, retry = 0): Promise<TxResponse> {
 | 
		
	
		
			
			|  | 110 | +  public async getTransaction(hash: string, retry = 0): Promise<TxResponse> {
 | 
		
	
		
			
			|  | 111 | +    if (!NON_ZERO_TX_HASH.test(hash)) {
 | 
		
	
		
			
			|  | 112 | +      throw ERR_BAD_TX_HASH(hash)
 | 
		
	
		
			
			|  | 113 | +    }
 | 
		
	
		
			
			|  | 114 | +
 | 
		
	
		
			
			| 110 | 115 |      this.dbg("Getting Tx", hash)
 | 
		
	
		
			
			|  | 116 | +    
 | 
		
	
		
			
			|  | 117 | +    const _api = this.options.readFreshApi ? await this.cloneApi() : this.api
 | 
		
	
		
			
			|  | 118 | +
 | 
		
	
		
			
			| 111 | 119 |      try {
 | 
		
	
		
			
			| 112 |  | -      return await this.api.request({
 | 
		
	
		
			
			|  | 120 | +      return await _api.request({
 | 
		
	
		
			
			| 113 | 121 |          command: 'tx',
 | 
		
	
		
			
			| 114 | 122 |          transaction: hash,
 | 
		
	
		
			
			| 115 | 123 |        })
 | 
		
	
		
			
			| 116 |  | -    } catch (e) {
 | 
		
	
		
			
			| 117 |  | -      this.dbg(e)
 | 
		
	
		
			
			| 118 |  | -      if (this.options.readMaxRetry != -1) {
 | 
		
	
		
			
			| 119 |  | -        if (retry >= this.options.readMaxRetry)
 | 
		
	
		
			
			| 120 |  | -          console.error("Retry limit exceeded for", hash, ". This is an irrecoverable error")
 | 
		
	
		
			
			|  | 124 | +    } catch (e: any) {
 | 
		
	
		
			
			|  | 125 | +      this.dbg("getTransaction err", e)
 | 
		
	
		
			
			|  | 126 | +      
 | 
		
	
		
			
			|  | 127 | +      if(e.data){ //RippledError 
 | 
		
	
		
			
			|  | 128 | +        switch(e.data.error){
 | 
		
	
		
			
			|  | 129 | +          //irrecoverable errors
 | 
		
	
		
			
			|  | 130 | +          case 'amendmentBlocked': //server is amendment blocked and needs to be updated to the latest version to stay synced with the XRP Ledger network.
 | 
		
	
		
			
			|  | 131 | +          case 'invalid_API_version': //The server does not support the API version number from the request.
 | 
		
	
		
			
			|  | 132 | +          case 'jsonInvalid': //(WebSocket only) The request is not a proper JSON object.
 | 
		
	
		
			
			|  | 133 | +          case 'missingCommand': //(WebSocket only) The request did not specify a command field
 | 
		
	
		
			
			|  | 134 | +          case 'noClosed': //The server does not have a closed ledger, typically because it has not finished starting up.
 | 
		
	
		
			
			|  | 135 | +          case 'txnNotFound': //Either the transaction does not exist, or it was part of an ledger version that rippled does not have available
 | 
		
	
		
			
			|  | 136 | +          case 'unknownCmd': //The request does not contain a command that the rippled server recognizes
 | 
		
	
		
			
			|  | 137 | +          case 'wsTextRequired': //(WebSocket only) The request's opcode  is not text.
 | 
		
	
		
			
			|  | 138 | +          case 'invalidParams': //One or more fields are specified incorrectly, or one or more required fields are missing.
 | 
		
	
		
			
			|  | 139 | +          case 'excessiveLgrRange': //The min_ledger and max_ledger fields of the request are more than 1000 apart
 | 
		
	
		
			
			|  | 140 | +          case 'invalidLgrRange': //The specified min_ledger is larger than the max_ledger, or one of those parameters is not a valid ledger index
 | 
		
	
		
			
			|  | 141 | +            throw e
 | 
		
	
		
			
			|  | 142 | +
 | 
		
	
		
			
			|  | 143 | +          //potentially recoverable errors
 | 
		
	
		
			
			|  | 144 | +          case 'failedToForward': //(Reporting Mode servers only) The server tried to forward this request to a P2P Mode server, but the connection failed
 | 
		
	
		
			
			|  | 145 | +          case 'noCurrent': //The server does not know what the current ledger is, due to high load, network problems, validator failures, incorrect configuration, or some other problem.
 | 
		
	
		
			
			|  | 146 | +          case 'noNetwork': //The server is having trouble connecting to the rest of the XRP Ledger peer-to-peer network (and is not running in stand-alone mode).
 | 
		
	
		
			
			|  | 147 | +          case 'tooBusy': //The server is under too much load to do this command right now. Generally not returned if you are connected as an admin
 | 
		
	
		
			
			|  | 148 | +          default: //some undocumented error, might as well give it a re-try
 | 
		
	
		
			
			|  | 149 | +            //fall through
 | 
		
	
		
			
			|  | 150 | +        }
 | 
		
	
		
			
			|  | 151 | +      }
 | 
		
	
		
			
			|  | 152 | +      //some other error, potentially recoverable
 | 
		
	
		
			
			|  | 153 | +      if (this.options.readMaxRetry != -1 && retry >= this.options.readMaxRetry) { //not doing infinite retries and exhausted retry quota
 | 
		
	
		
			
			| 121 | 154 |          throw e
 | 
		
	
		
			
			|  | 155 | +      }else{
 | 
		
	
		
			
			|  | 156 | +        await new Promise(res => setTimeout(res, this.options.readRetryTimeout))
 | 
		
	
		
			
			|  | 157 | +        return await this.getTransaction(hash, retry + 1)
 | 
		
	
		
			
			| 122 | 158 |        }
 | 
		
	
		
			
			| 123 |  | -      await new Promise(res => setTimeout(res, this.options.readRetryTimeout))
 | 
		
	
		
			
			| 124 |  | -      return await this.getTransaction(hash, retry + 1)
 | 
		
	
		
			
			|  | 159 | +    }finally{
 | 
		
	
		
			
			|  | 160 | +      if(this.options.readFreshApi) await _api.disconnect()
 | 
		
	
		
			
			| 125 | 161 |      }
 | 
		
	
		
			
			| 126 | 162 |    }
 | 
		
	
		
			
			| 127 | 163 |  
 |