move to new xrpl library
This commit is contained in:
@@ -13,3 +13,7 @@ export type Secret = string
|
||||
export type PublicKey = string
|
||||
export type Amount = number
|
||||
export type TxHash = string
|
||||
export type Options = {
|
||||
debug: boolean,
|
||||
connectionTimeout: number
|
||||
}
|
||||
@@ -150,6 +150,7 @@ export const readRaw = async (api: RippleAPI, hash: string): Promise<Memo> => {
|
||||
} catch (e) {
|
||||
// if(debug){
|
||||
console.log("READRAW ERR", e)
|
||||
api.isConnected
|
||||
// }
|
||||
throw e
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
import { Memo, Options } from '../util/types'
|
||||
import { Client, Payment, TxResponse, Wallet } from 'xrpl'
|
||||
|
||||
import * as zlib from 'zlib'
|
||||
import * as util from 'util'
|
||||
|
||||
const compressB64 = async (data: string) => (await util.promisify(zlib.deflate)(Buffer.from(data, 'utf-8'))).toString('base64')
|
||||
const decompressB64 = async (data: string) => (await util.promisify(zlib.inflate)(Buffer.from(data, 'base64'))).toString('utf-8')
|
||||
const hexDecode = (str: string) => Buffer.from(str, 'hex').toString('utf8')
|
||||
const hexEncode = (str: string) => Buffer.from(str, 'utf8').toString('hex').toUpperCase()
|
||||
const chunkString = (str: string, length: number) => str.match(new RegExp('.{1,' + length + '}', 'gs'));
|
||||
const PAYLOAD_SIZE = 925
|
||||
|
||||
|
||||
|
||||
export class xrpIO {
|
||||
private api: Client
|
||||
|
||||
constructor(
|
||||
private server: string,
|
||||
private options: Options = {
|
||||
debug: false,
|
||||
connectionTimeout: 100000
|
||||
}
|
||||
) {
|
||||
this.api = new Client(server, {
|
||||
connectionTimeout: this.options.connectionTimeout
|
||||
})
|
||||
}
|
||||
|
||||
public async connect(): Promise<void> {
|
||||
if (!this.api.isConnected())
|
||||
await this.api.connect()
|
||||
}
|
||||
|
||||
public async disconnect(): Promise<void> {
|
||||
try{
|
||||
await this.api.disconnect()
|
||||
}catch(e){
|
||||
console.log("DISCONNECT ERROR", e)
|
||||
}
|
||||
}
|
||||
|
||||
private async cloneApi(): Promise<Client> {
|
||||
let _api = new Client(this.server, {
|
||||
connectionTimeout: this.options.connectionTimeout
|
||||
})
|
||||
|
||||
while(!_api.isConnected()){
|
||||
try{
|
||||
await _api.connect()
|
||||
return _api
|
||||
}catch(e){
|
||||
this.dbg('CLONEAPI ERR', 'Connection failed', String(e['message']))
|
||||
await _api.disconnect()
|
||||
_api = new Client(this.server, {
|
||||
connectionTimeout: this.options.connectionTimeout
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async sendPayment(data: Memo, to: string, secret: string, sequence?: number): Promise<TxResponse> {
|
||||
const wallet = Wallet.fromSecret(secret)
|
||||
this.dbg("Sending payment", wallet.address, '->', to)
|
||||
|
||||
const _api = await this.cloneApi()
|
||||
try {
|
||||
const payment: Payment = await _api.autofill({
|
||||
TransactionType: 'Payment',
|
||||
Account: wallet.address,
|
||||
Destination: to,
|
||||
Sequence: sequence,
|
||||
Amount: "1",
|
||||
Memos: [{
|
||||
Memo: {
|
||||
MemoData: hexEncode(data.data || ""),
|
||||
MemoFormat: hexEncode(data.format || ""),
|
||||
MemoType: hexEncode(data.type || "")
|
||||
}
|
||||
}]
|
||||
})
|
||||
|
||||
|
||||
const response = await _api.submitAndWait(payment, { wallet })
|
||||
await _api.disconnect()
|
||||
this.dbg("Tx finalized", response.result.hash, response.result.Sequence)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
this.dbg("SENDPAYMENT ERROR", error)
|
||||
await _api.disconnect()
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async writeRaw(data: Memo, to: string, secret: string, sequence?: number): Promise<string> {
|
||||
this.dbg("Writing data", data)
|
||||
const tx = await this.sendPayment(data, to, secret, sequence)
|
||||
return tx.result.hash
|
||||
}
|
||||
|
||||
private async getTransaction(hash: string): Promise<TxResponse> {
|
||||
this.dbg("Getting Tx", hash)
|
||||
let _api = await this.cloneApi()
|
||||
|
||||
while(true){
|
||||
try{
|
||||
const response = await _api.request({
|
||||
command: 'tx',
|
||||
transaction: hash,
|
||||
})
|
||||
await _api.disconnect()
|
||||
return response
|
||||
}catch(e){
|
||||
this.dbg("Retrying to get", hash)
|
||||
await _api.disconnect()
|
||||
_api = await this.cloneApi()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async readRaw(hash: string): Promise<Memo> {
|
||||
const tx = await this.getTransaction(hash)
|
||||
const memo = tx.result.Memos[0].Memo
|
||||
const memoParsed = {
|
||||
data: hexDecode(memo.MemoData),
|
||||
format: hexDecode(memo.MemoFormat),
|
||||
type: hexDecode(memo.MemoType)
|
||||
}
|
||||
this.dbg(hash, "data", memoParsed)
|
||||
return memoParsed
|
||||
}
|
||||
|
||||
public async treeWrite(data: string, to: string, secret: string, format: 'L' | 'N' = 'L'): Promise<string> {
|
||||
const wallet = Wallet.fromSecret(secret)
|
||||
data = await compressB64(data)
|
||||
const chunks = chunkString(data, PAYLOAD_SIZE)
|
||||
const latestSequence = await this.getAccountSequence(wallet.address)
|
||||
const hashes = await Promise.all(Object.entries(chunks).map(([i, chunk]) => this.writeRaw({ data: chunk, format: format }, to, secret, latestSequence+Number(i))))
|
||||
|
||||
if (hashes.length === 1) {
|
||||
return hashes[0]
|
||||
}
|
||||
|
||||
return await this.treeWrite(JSON.stringify(hashes), to, secret, 'N')
|
||||
}
|
||||
|
||||
public async treeRead(hashes: string[]): Promise<string>{
|
||||
const memos = await Promise.all(hashes.map(hash => this.readRaw(hash)))
|
||||
const payload: string = await decompressB64(memos.map(memo => memo.data).join(''))
|
||||
|
||||
if (memos.some(memo => memo.format === 'N')) {
|
||||
return await this.treeRead(JSON.parse(payload))
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
public async getAccountSequence(address: string): Promise<number>{
|
||||
this.dbg("Getting acc info for", address)
|
||||
const accountInfo = await this.api.request({
|
||||
command: 'account_info',
|
||||
account: address,
|
||||
strict: true,
|
||||
})
|
||||
this.dbg("Got account_info", accountInfo)
|
||||
return Number(accountInfo.result.account_data.Sequence)
|
||||
}
|
||||
|
||||
private dbg(...args: any[]) {
|
||||
if (this.options.debug) {
|
||||
console.log.apply(console, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user