move to new xrpl library

This commit is contained in:
nitowa
2022-03-27 18:55:14 +02:00
parent ca8498707b
commit 33201638ad
9 changed files with 401 additions and 113 deletions
+4
View File
@@ -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
}
+1
View File
@@ -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 {
+175
View File
@@ -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)
}
}
}