| 
				
			 | 
			
			
				
				@@ -3,14 +3,18 @@ import { Client, Payment, TxResponse, Wallet } from 'xrpl' 
			 | 
		
		
	
		
			
			| 
				3
			 | 
			
				3
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				4
			 | 
			
				4
			 | 
			
			
				
				 import * as zlib from 'zlib' 
			 | 
		
		
	
		
			
			| 
				5
			 | 
			
				5
			 | 
			
			
				
				 import * as util from 'util' 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				6
			 | 
			
			
				
				+import { NON_ZERO_TX_HASH } from '../util/protocol.constants' 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				7
			 | 
			
			
				
				+import { ERR_BAD_TX_HASH } from '../util/errors' 
			 | 
		
		
	
		
			
			| 
				6
			 | 
			
				8
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				7
			 | 
			
				9
			 | 
			
			
				
				 const compressB64 = async (data: string) => (await util.promisify(zlib.deflate)(Buffer.from(data, 'utf-8'))).toString('base64') 
			 | 
		
		
	
		
			
			| 
				8
			 | 
			
				10
			 | 
			
			
				
				 const decompressB64 = async (data: string) => (await util.promisify(zlib.inflate)(Buffer.from(data, 'base64'))).toString('utf-8') 
			 | 
		
		
	
		
			
			| 
				9
			 | 
			
				11
			 | 
			
			
				
				 const hexDecode = (str: string) => Buffer.from(str, 'hex').toString('utf8') 
			 | 
		
		
	
		
			
			| 
				10
			 | 
			
				12
			 | 
			
			
				
				 const hexEncode = (str: string) => Buffer.from(str, 'utf8').toString('hex').toUpperCase() 
			 | 
		
		
	
		
			
			| 
				11
			 | 
			
				13
			 | 
			
			
				
				 const chunkString = (str: string, length: number) => str.match(new RegExp('.{1,' + length + '}', 'gs')); 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				14
			 | 
			
			
				
				+const genRandHex = size => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(''); 
			 | 
		
		
	
		
			
			| 
				12
			 | 
			
				15
			 | 
			
			
				
				 const PAYLOAD_SIZE = 925 
			 | 
		
		
	
		
			
			| 
				13
			 | 
			
				
			 | 
			
			
				
				- 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				16
			 | 
			
			
				
				+const XRP_PER_DROP = 0.000001 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				17
			 | 
			
			
				
				+const DROP_PER_XRP = 1000000 
			 | 
		
		
	
		
			
			| 
				14
			 | 
			
				18
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				15
			 | 
			
				19
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				16
			 | 
			
				20
			 | 
			
			
				
				 export class xrpIO { 
			 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			
				
				@@ -63,7 +67,7 @@ export class xrpIO { 
			 | 
		
		
	
		
			
			| 
				63
			 | 
			
				67
			 | 
			
			
				
				     } 
			 | 
		
		
	
		
			
			| 
				64
			 | 
			
				68
			 | 
			
			
				
				   } 
			 | 
		
		
	
		
			
			| 
				65
			 | 
			
				69
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				66
			 | 
			
				
			 | 
			
			
				
				-  private async sendPayment(data: Memo, to: string, secret: string, sequence?: number): Promise<TxResponse> { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				70
			 | 
			
			
				
				+  private async sendPayment(data: Memo, to: string, secret: string, sequence?: number, amount: string = "1"): Promise<TxResponse> { 
			 | 
		
		
	
		
			
			| 
				67
			 | 
			
				71
			 | 
			
			
				
				     const wallet = Wallet.fromSecret(secret) 
			 | 
		
		
	
		
			
			| 
				68
			 | 
			
				72
			 | 
			
			
				
				     this.dbg("Sending payment", wallet.address, '->', to) 
			 | 
		
		
	
		
			
			| 
				69
			 | 
			
				73
			 | 
			
			
				
				  
			 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			
				
				@@ -74,7 +78,7 @@ export class xrpIO { 
			 | 
		
		
	
		
			
			| 
				74
			 | 
			
				78
			 | 
			
			
				
				         Account: wallet.address, 
			 | 
		
		
	
		
			
			| 
				75
			 | 
			
				79
			 | 
			
			
				
				         Destination: to, 
			 | 
		
		
	
		
			
			| 
				76
			 | 
			
				80
			 | 
			
			
				
				         Sequence: sequence, 
			 | 
		
		
	
		
			
			| 
				77
			 | 
			
				
			 | 
			
			
				
				-        Amount: "1", 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				81
			 | 
			
			
				
				+        Amount: amount, 
			 | 
		
		
	
		
			
			| 
				78
			 | 
			
				82
			 | 
			
			
				
				         Memos: [{ 
			 | 
		
		
	
		
			
			| 
				79
			 | 
			
				83
			 | 
			
			
				
				           Memo: { 
			 | 
		
		
	
		
			
			| 
				80
			 | 
			
				84
			 | 
			
			
				
				             MemoData: hexEncode(data.data || ""), 
			 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			
				
				@@ -96,32 +100,37 @@ export class xrpIO { 
			 | 
		
		
	
		
			
			| 
				96
			 | 
			
				100
			 | 
			
			
				
				     } 
			 | 
		
		
	
		
			
			| 
				97
			 | 
			
				101
			 | 
			
			
				
				   } 
			 | 
		
		
	
		
			
			| 
				98
			 | 
			
				102
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				99
			 | 
			
				
			 | 
			
			
				
				-  public async writeRaw(data: Memo, to: string, secret: string, sequence?: number): Promise<string> { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				103
			 | 
			
			
				
				+  public async writeRaw(data: Memo, to: string, secret: string, sequence?: number, amount: string = "1"): Promise<string> { 
			 | 
		
		
	
		
			
			| 
				100
			 | 
			
				104
			 | 
			
			
				
				     this.dbg("Writing data", data) 
			 | 
		
		
	
		
			
			| 
				101
			 | 
			
				
			 | 
			
			
				
				-    const tx = await this.sendPayment(data, to, secret, sequence) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				105
			 | 
			
			
				
				+    const tx = await this.sendPayment(data, to, secret, sequence, amount) 
			 | 
		
		
	
		
			
			| 
				102
			 | 
			
				106
			 | 
			
			
				
				     return tx.result.hash 
			 | 
		
		
	
		
			
			| 
				103
			 | 
			
				107
			 | 
			
			
				
				   } 
			 | 
		
		
	
		
			
			| 
				104
			 | 
			
				108
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				105
			 | 
			
				
			 | 
			
			
				
				-  private async getTransaction(hash: string, retry=0): Promise<TxResponse> { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				109
			 | 
			
			
				
				+  private async getTransaction(hash: string, retry = 0): Promise<TxResponse> { 
			 | 
		
		
	
		
			
			| 
				106
			 | 
			
				110
			 | 
			
			
				
				     this.dbg("Getting Tx", hash) 
			 | 
		
		
	
		
			
			| 
				107
			 | 
			
				
			 | 
			
			
				
				-    try{ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				111
			 | 
			
			
				
				+    try { 
			 | 
		
		
	
		
			
			| 
				108
			 | 
			
				112
			 | 
			
			
				
				       return await this.api.request({ 
			 | 
		
		
	
		
			
			| 
				109
			 | 
			
				113
			 | 
			
			
				
				         command: 'tx', 
			 | 
		
		
	
		
			
			| 
				110
			 | 
			
				114
			 | 
			
			
				
				         transaction: hash, 
			 | 
		
		
	
		
			
			| 
				111
			 | 
			
				115
			 | 
			
			
				
				       }) 
			 | 
		
		
	
		
			
			| 
				112
			 | 
			
				
			 | 
			
			
				
				-    }catch(e){ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				116
			 | 
			
			
				
				+    } catch (e) { 
			 | 
		
		
	
		
			
			| 
				113
			 | 
			
				117
			 | 
			
			
				
				       this.dbg(e) 
			 | 
		
		
	
		
			
			| 
				114
			 | 
			
				
			 | 
			
			
				
				-      if(this.options.readMaxRetry != -1){ 
			 | 
		
		
	
		
			
			| 
				115
			 | 
			
				
			 | 
			
			
				
				-        if(retry >= this.options.readMaxRetry) 
			 | 
		
		
	
		
			
			| 
				116
			 | 
			
				
			 | 
			
			
				
				-          console.error("Retry limit exceeded for", hash, ". this is an irrecoverable error") 
			 | 
		
		
	
		
			
			| 
				117
			 | 
			
				
			 | 
			
			
				
				-          throw 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") 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				121
			 | 
			
			
				
				+        throw e 
			 | 
		
		
	
		
			
			| 
				118
			 | 
			
				122
			 | 
			
			
				
				       } 
			 | 
		
		
	
		
			
			| 
				119
			 | 
			
				123
			 | 
			
			
				
				       await new Promise(res => setTimeout(res, this.options.readRetryTimeout)) 
			 | 
		
		
	
		
			
			| 
				120
			 | 
			
				
			 | 
			
			
				
				-      return await this.getTransaction(hash, retry+1) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				124
			 | 
			
			
				
				+      return await this.getTransaction(hash, retry + 1) 
			 | 
		
		
	
		
			
			| 
				121
			 | 
			
				125
			 | 
			
			
				
				     } 
			 | 
		
		
	
		
			
			| 
				122
			 | 
			
				126
			 | 
			
			
				
				   } 
			 | 
		
		
	
		
			
			| 
				123
			 | 
			
				127
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				124
			 | 
			
				128
			 | 
			
			
				
				   public async readRaw(hash: string): Promise<Memo> { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				129
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				130
			 | 
			
			
				
				+    if (!NON_ZERO_TX_HASH.test(hash)) { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				131
			 | 
			
			
				
				+      throw ERR_BAD_TX_HASH(hash) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				132
			 | 
			
			
				
				+    } 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				133
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				125
			 | 
			
				134
			 | 
			
			
				
				     const tx = await this.getTransaction(hash) 
			 | 
		
		
	
		
			
			| 
				126
			 | 
			
				135
			 | 
			
			
				
				     const memo = tx.result.Memos[0].Memo 
			 | 
		
		
	
		
			
			| 
				127
			 | 
			
				136
			 | 
			
			
				
				     const memoParsed = { 
			 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			
				
				@@ -148,6 +157,10 @@ export class xrpIO { 
			 | 
		
		
	
		
			
			| 
				148
			 | 
			
				157
			 | 
			
			
				
				   } 
			 | 
		
		
	
		
			
			| 
				149
			 | 
			
				158
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				150
			 | 
			
				159
			 | 
			
			
				
				   public async treeRead(hashes: string[]): Promise<string> { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				160
			 | 
			
			
				
				+    const bad_hash = hashes.find(hash => !NON_ZERO_TX_HASH.test(hash)) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				161
			 | 
			
			
				
				+    if (bad_hash) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				162
			 | 
			
			
				
				+      throw ERR_BAD_TX_HASH(bad_hash) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				163
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				151
			 | 
			
				164
			 | 
			
			
				
				     const memos = await Promise.all(hashes.map(hash => this.readRaw(hash))) 
			 | 
		
		
	
		
			
			| 
				152
			 | 
			
				165
			 | 
			
			
				
				     const payload: string = await decompressB64(memos.map(memo => memo.data).join('')) 
			 | 
		
		
	
		
			
			| 
				153
			 | 
			
				166
			 | 
			
			
				
				  
			 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			
				
				@@ -169,6 +182,25 @@ export class xrpIO { 
			 | 
		
		
	
		
			
			| 
				169
			 | 
			
				182
			 | 
			
			
				
				     return Number(accountInfo.result.account_data.Sequence) 
			 | 
		
		
	
		
			
			| 
				170
			 | 
			
				183
			 | 
			
			
				
				   } 
			 | 
		
		
	
		
			
			| 
				171
			 | 
			
				184
			 | 
			
			
				
				  
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				185
			 | 
			
			
				
				+  public async estimateFee(data: string, denomination: 'XRP' | 'DROPS' = 'DROPS', cost = 0): Promise<number> { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				186
			 | 
			
			
				
				+    data = await compressB64(data) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				187
			 | 
			
			
				
				+    const chunks = chunkString(data, PAYLOAD_SIZE) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				188
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				189
			 | 
			
			
				
				+    if (chunks.length === 1) { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				190
			 | 
			
			
				
				+      return (denomination === "DROPS" ? (cost + 1) : this.dropsToXrp(cost + 1)) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				191
			 | 
			
			
				
				+    } 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				192
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				193
			 | 
			
			
				
				+    return this.estimateFee(JSON.stringify(chunks.map(_ => genRandHex(64))), denomination, cost + chunks.length) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				194
			 | 
			
			
				
				+  } 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				195
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				196
			 | 
			
			
				
				+  public xrpToDrops(xrp: number): number { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				197
			 | 
			
			
				
				+    return xrp * DROP_PER_XRP 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				198
			 | 
			
			
				
				+  } 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				199
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				200
			 | 
			
			
				
				+  public dropsToXrp(drops: number): number { 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				201
			 | 
			
			
				
				+    return drops * XRP_PER_DROP 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				202
			 | 
			
			
				
				+  } 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				203
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				172
			 | 
			
				204
			 | 
			
			
				
				   private dbg(...args: any[]) { 
			 | 
		
		
	
		
			
			| 
				173
			 | 
			
				205
			 | 
			
			
				
				     if (this.options.debug) { 
			 | 
		
		
	
		
			
			| 
				174
			 | 
			
				206
			 | 
			
			
				
				       console.log.apply(console, args) 
			 |