init
This commit is contained in:
+105952
File diff suppressed because one or more lines are too long
+3
@@ -0,0 +1,3 @@
|
||||
export * from './util/types';
|
||||
export * from './util/protocol.constants';
|
||||
export * from './xrpIO/xrpl-binding';
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//export * from './xrpIO/ripple-binding'
|
||||
__exportStar(require("./util/types"), exports);
|
||||
__exportStar(require("./util/protocol.constants"), exports);
|
||||
//export * from 'ripple-lib'
|
||||
//export { RippleAPI } from 'ripple-lib'
|
||||
__exportStar(require("./xrpIO/xrpl-binding"), exports);
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export declare const ERR_BAD_TX_HASH: (hash: string) => Error;
|
||||
export declare const ERR_NO_VERIFY_OWNER: (hash: string, actualAccount: string, desiredAccount: string) => Error;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ERR_NO_VERIFY_OWNER = exports.ERR_BAD_TX_HASH = void 0;
|
||||
const ERR_BAD_TX_HASH = (hash) => new Error(`Bad tx hash format: "${hash}"`);
|
||||
exports.ERR_BAD_TX_HASH = ERR_BAD_TX_HASH;
|
||||
const ERR_NO_VERIFY_OWNER = (hash, actualAccount, desiredAccount) => new Error(`Expected tx "${hash}" to be initiated by ${desiredAccount} but was ${actualAccount}`);
|
||||
exports.ERR_NO_VERIFY_OWNER = ERR_NO_VERIFY_OWNER;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
export declare const MSG_DELIM: string;
|
||||
export declare const MSG_DATA_MAX: number;
|
||||
export declare const PUBKEY_LEN: number;
|
||||
export declare const NON_ZERO_TX_HASH: RegExp;
|
||||
export declare const PTR_FORMAT: RegExp;
|
||||
export declare const DATA_FORMAT: RegExp;
|
||||
export declare const SIGNATURE_FORMAT: RegExp;
|
||||
export declare const SIGNER_FORMAT: RegExp;
|
||||
export declare const MSG_FORMAT: RegExp;
|
||||
export declare const AMOUNT_DECIMALS = 18;
|
||||
export declare const MAX_SUPPLY = 20000000;
|
||||
export declare const AMOUNT_FORMAT: RegExp;
|
||||
export declare const MIN_XRP_FEE = "0.00001";
|
||||
export declare const MIN_XRP_TX_VALUE = "0.000001";
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MIN_XRP_TX_VALUE = exports.MIN_XRP_FEE = exports.AMOUNT_FORMAT = exports.MAX_SUPPLY = exports.AMOUNT_DECIMALS = exports.MSG_FORMAT = exports.SIGNER_FORMAT = exports.SIGNATURE_FORMAT = exports.DATA_FORMAT = exports.PTR_FORMAT = exports.NON_ZERO_TX_HASH = exports.PUBKEY_LEN = exports.MSG_DATA_MAX = exports.MSG_DELIM = void 0;
|
||||
exports.MSG_DELIM = ' ';
|
||||
exports.MSG_DATA_MAX = 925;
|
||||
exports.PUBKEY_LEN = 66;
|
||||
exports.NON_ZERO_TX_HASH = new RegExp(`[0-9A-F]{64}`);
|
||||
exports.PTR_FORMAT = new RegExp(`^((${exports.NON_ZERO_TX_HASH.source})|0)`);
|
||||
exports.DATA_FORMAT = new RegExp(`(.{1,${exports.MSG_DATA_MAX}})`);
|
||||
exports.SIGNATURE_FORMAT = new RegExp(`(\\S{140}|\\S{142})$`);
|
||||
exports.SIGNER_FORMAT = new RegExp(`(\\S{${exports.PUBKEY_LEN}})`);
|
||||
exports.MSG_FORMAT = new RegExp(`${exports.PTR_FORMAT.source}${exports.MSG_DELIM}${exports.DATA_FORMAT.source}`, 'm');
|
||||
exports.AMOUNT_DECIMALS = 18;
|
||||
exports.MAX_SUPPLY = 20_000_000;
|
||||
exports.AMOUNT_FORMAT = new RegExp(`\d+(\.\d{1,${exports.AMOUNT_DECIMALS}})?`);
|
||||
exports.MIN_XRP_FEE = "0.00001";
|
||||
exports.MIN_XRP_TX_VALUE = "0.000001";
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
export declare type Memo = {
|
||||
type?: string;
|
||||
format?: string;
|
||||
data?: string;
|
||||
};
|
||||
export declare type Wallet = {
|
||||
secret: string;
|
||||
address: string;
|
||||
};
|
||||
export declare type Signature = {
|
||||
signature: string;
|
||||
signer: PublicKey;
|
||||
};
|
||||
export declare type Address = string;
|
||||
export declare type Secret = string;
|
||||
export declare type PublicKey = string;
|
||||
export declare type Amount = number;
|
||||
export declare type TxHash = string;
|
||||
export declare type Options = {
|
||||
debug?: boolean;
|
||||
connectionTimeout?: number;
|
||||
readMaxRetry?: number;
|
||||
readRetryTimeout?: number;
|
||||
};
|
||||
export declare const defaultOptions: {
|
||||
debug: boolean;
|
||||
connectionTimeout: number;
|
||||
readFreshApi: boolean;
|
||||
readMaxRetry: number;
|
||||
readRetryTimeout: number;
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.defaultOptions = void 0;
|
||||
exports.defaultOptions = {
|
||||
debug: false,
|
||||
connectionTimeout: 100000,
|
||||
readFreshApi: true,
|
||||
readMaxRetry: -1,
|
||||
readRetryTimeout: 1000
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { Memo, Wallet } from '../util/types';
|
||||
import { RippleAPI } from 'ripple-lib';
|
||||
export declare const getLatestSequence: (api: RippleAPI, accountAddress: string) => Promise<number>;
|
||||
export declare const sendPayment: (api: RippleAPI, data: Memo[], from: string, to: string, secret: string, sequence: number) => Promise<import("ripple-lib/dist/npm/transaction/submit").FormattedSubmitResponse>;
|
||||
export declare const getTransactions: (api: RippleAPI, address: string, minLedgerVersion?: number) => Promise<any[]>;
|
||||
export declare const writeRaw: (api: RippleAPI, data: Memo, from: string, to: string, secret: string, sequence?: number) => Promise<string>;
|
||||
export declare const readRaw: (api: RippleAPI, hash: string) => Promise<Memo>;
|
||||
export declare const subscribe: (api: RippleAPI, address: string, callback: (tx: any) => any) => Promise<void>;
|
||||
export declare const treeWrite: (api: RippleAPI, data: string, wallet: Wallet, to: string, format?: 'L' | 'N') => Promise<string>;
|
||||
export declare const treeRead: (api: RippleAPI, hashes: string[]) => Promise<string>;
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.treeRead = exports.treeWrite = exports.subscribe = exports.readRaw = exports.writeRaw = exports.getTransactions = exports.sendPayment = exports.getLatestSequence = void 0;
|
||||
const protocol_constants_1 = require("../util/protocol.constants");
|
||||
const ripple_lib_1 = require("ripple-lib");
|
||||
const zlib = __importStar(require("zlib"));
|
||||
const util = __importStar(require("util"));
|
||||
const chunkString = (str, length) => str.match(new RegExp('.{1,' + length + '}', 'gs'));
|
||||
const PAYLOAD_SIZE = 925;
|
||||
const debug = false;
|
||||
const cloneApi = async (api) => {
|
||||
try {
|
||||
const subApi = new ripple_lib_1.RippleAPI({ server: api.connection['_url'] });
|
||||
await subApi.connect();
|
||||
return subApi;
|
||||
}
|
||||
catch (e) {
|
||||
if (debug) {
|
||||
console.log("CLONEAPI ERR", e);
|
||||
}
|
||||
return await cloneApi(api);
|
||||
}
|
||||
};
|
||||
const getLatestSequence = async (api, accountAddress) => {
|
||||
if (debug)
|
||||
console.log("Getting acc info for", accountAddress);
|
||||
const accountInfo = await api.getAccountInfo(accountAddress, {});
|
||||
return Number(accountInfo.sequence - 1);
|
||||
};
|
||||
exports.getLatestSequence = getLatestSequence;
|
||||
const compressB64 = async (data) => (await util.promisify(zlib.deflate)(Buffer.from(data, 'utf-8'))).toString('base64');
|
||||
const decompressB64 = async (data) => (await util.promisify(zlib.inflate)(Buffer.from(data, 'base64'))).toString('utf-8');
|
||||
const sendReliably = (api, signed, preparedPayment) => new Promise((res, rej) => {
|
||||
const ledgerClosedCallback = async (event) => {
|
||||
let status;
|
||||
try {
|
||||
status = await api.getTransaction(signed.id, {
|
||||
minLedgerVersion: 25235454
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
// Typical error when the tx hasn't been validated yet:
|
||||
if (e.name !== 'MissingLedgerHistoryError') {
|
||||
//console.log(e)
|
||||
}
|
||||
if (event.ledger_index > preparedPayment.instructions.maxLedgerVersion + 3) {
|
||||
// Assumptions:
|
||||
// - We are still connected to the same rippled server
|
||||
// - No ledger gaps occurred
|
||||
// - All ledgers between the time we submitted the tx and now have been checked for the tx
|
||||
status = {
|
||||
finalResult: 'Transaction was not, and never will be, included in a validated ledger'
|
||||
};
|
||||
return rej(status);
|
||||
}
|
||||
else {
|
||||
// Check again later:
|
||||
api.connection.once('ledgerClosed', ledgerClosedCallback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return res(status);
|
||||
};
|
||||
api.connection.once('ledgerClosed', ledgerClosedCallback);
|
||||
});
|
||||
const sendPayment = async (api, data, from, to, secret, sequence) => {
|
||||
if (debug)
|
||||
console.log("Sending payment with seq", sequence);
|
||||
const options = {
|
||||
maxLedgerVersionOffset: 5,
|
||||
fee: protocol_constants_1.MIN_XRP_FEE,
|
||||
sequence: sequence,
|
||||
};
|
||||
const payment = {
|
||||
source: {
|
||||
address: from,
|
||||
maxAmount: {
|
||||
value: protocol_constants_1.MIN_XRP_TX_VALUE,
|
||||
currency: 'XRP'
|
||||
},
|
||||
},
|
||||
destination: {
|
||||
address: to,
|
||||
amount: {
|
||||
value: protocol_constants_1.MIN_XRP_TX_VALUE,
|
||||
currency: 'XRP'
|
||||
},
|
||||
},
|
||||
memos: data,
|
||||
};
|
||||
const _api = await cloneApi(api);
|
||||
try {
|
||||
const prepared = await _api.preparePayment(from, payment, options);
|
||||
const signed = _api.sign(prepared.txJSON, secret);
|
||||
const txHash = await _api.submit(signed.signedTransaction);
|
||||
//if(debug) console.log("Transaction submitted", txHash)
|
||||
await sendReliably(_api, signed, prepared);
|
||||
return txHash;
|
||||
}
|
||||
catch (error) {
|
||||
if (debug)
|
||||
console.log("SENDPAYMENT ERROR", error);
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
_api.disconnect();
|
||||
}
|
||||
};
|
||||
exports.sendPayment = sendPayment;
|
||||
const getTransactions = async (api, address, minLedgerVersion = 25235454) => {
|
||||
const txs = await api.getTransactions(address, {
|
||||
minLedgerVersion: minLedgerVersion,
|
||||
earliestFirst: true,
|
||||
excludeFailures: true,
|
||||
});
|
||||
return txs;
|
||||
};
|
||||
exports.getTransactions = getTransactions;
|
||||
const writeRaw = async (api, data, from, to, secret, sequence) => {
|
||||
//if (memoSize(data) > 1000) throw new Error("data length exceeds capacity")
|
||||
try {
|
||||
if (!sequence) {
|
||||
const accountInfo = await (0, exports.getLatestSequence)(api, from);
|
||||
sequence = accountInfo + 1;
|
||||
}
|
||||
const resp = await (0, exports.sendPayment)(api, [data], from, to, secret, sequence);
|
||||
return resp['tx_json'].hash;
|
||||
}
|
||||
catch (error) {
|
||||
if (debug) {
|
||||
console.log("WRITERAW ERR", error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
exports.writeRaw = writeRaw;
|
||||
const readRaw = async (api, hash) => {
|
||||
api = await cloneApi(api);
|
||||
let tx;
|
||||
try {
|
||||
tx = await api.getTransaction(hash, {
|
||||
minLedgerVersion: 25235454
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
// if(debug){
|
||||
console.log("READRAW ERR", e);
|
||||
api.isConnected;
|
||||
// }
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
await api.disconnect();
|
||||
}
|
||||
if (!tx || !tx.specification || !tx.specification['memos'] || !tx.specification['memos'][0]) {
|
||||
console.log(tx);
|
||||
throw new Error('Invalid Transaction ' + hash);
|
||||
}
|
||||
return tx.specification['memos'][0];
|
||||
};
|
||||
exports.readRaw = readRaw;
|
||||
const subscribe = async (api, address, callback) => {
|
||||
api.connection.on('transaction', (tx) => callback(tx));
|
||||
await api.connection.request({
|
||||
command: 'subscribe',
|
||||
accounts: [address],
|
||||
});
|
||||
};
|
||||
exports.subscribe = subscribe;
|
||||
const treeWrite = async (api, data, wallet, to, format = 'L') => {
|
||||
data = await compressB64(data);
|
||||
const chunks = chunkString(data, PAYLOAD_SIZE);
|
||||
const latestSequence = await (0, exports.getLatestSequence)(api, wallet.address);
|
||||
const hashes = await Promise.all(Object.entries(chunks).map(([i, chunk]) => (0, exports.writeRaw)(api, { data: chunk, format: format }, wallet.address, to, wallet.secret, latestSequence + Number(i) + 1)));
|
||||
if (hashes.length === 1) {
|
||||
return hashes[0];
|
||||
}
|
||||
return await (0, exports.treeWrite)(api, JSON.stringify(hashes), wallet, to, 'N');
|
||||
};
|
||||
exports.treeWrite = treeWrite;
|
||||
const treeRead = async (api, hashes) => {
|
||||
const memos = await Promise.all(hashes.map(hash => (0, exports.readRaw)(api, hash)));
|
||||
const payload = await decompressB64(memos.map(memo => memo.data).join(''));
|
||||
if (memos.some(memo => memo.format === 'N')) {
|
||||
return await (0, exports.treeRead)(api, JSON.parse(payload));
|
||||
}
|
||||
return payload;
|
||||
};
|
||||
exports.treeRead = treeRead;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { Memo, Options } from '../util/types';
|
||||
export declare class xrpIO {
|
||||
private server;
|
||||
private options;
|
||||
private api;
|
||||
constructor(server: string, options?: Options);
|
||||
connect(): Promise<void>;
|
||||
disconnect(): Promise<void>;
|
||||
private cloneApi;
|
||||
private sendPayment;
|
||||
writeRaw(data: Memo, to: string, secret: string, sequence?: number, amount?: string): Promise<string>;
|
||||
private getTransaction;
|
||||
readRaw(hash: string, verifyOwner?: string): Promise<Memo>;
|
||||
treeWrite(data: string, to: string, secret: string, format?: 'L' | 'N'): Promise<string>;
|
||||
treeRead(hashes: string[], verifyOwner?: string): Promise<string>;
|
||||
getAccountSequence(address: string): Promise<number>;
|
||||
estimateFee(data: string, denomination?: 'XRP' | 'DROPS', cost?: number): Promise<number>;
|
||||
xrpToDrops(xrp: number): number;
|
||||
dropsToXrp(drops: number): number;
|
||||
private dbg;
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.xrpIO = void 0;
|
||||
const types_1 = require("../util/types");
|
||||
const xrpl_1 = require("xrpl");
|
||||
const zlib = __importStar(require("zlib"));
|
||||
const util = __importStar(require("util"));
|
||||
const protocol_constants_1 = require("../util/protocol.constants");
|
||||
const errors_1 = require("../util/errors");
|
||||
const compressB64 = async (data) => (await util.promisify(zlib.deflate)(Buffer.from(data, 'utf-8'))).toString('base64');
|
||||
const decompressB64 = async (data) => (await util.promisify(zlib.inflate)(Buffer.from(data, 'base64'))).toString('utf-8');
|
||||
const hexDecode = (str) => Buffer.from(str, 'hex').toString('utf8');
|
||||
const hexEncode = (str) => Buffer.from(str, 'utf8').toString('hex').toUpperCase();
|
||||
const chunkString = (str, length) => str.match(new RegExp('.{1,' + length + '}', 'gs'));
|
||||
const genRandHex = size => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
|
||||
const PAYLOAD_SIZE = 925;
|
||||
const XRP_PER_DROP = 0.000001;
|
||||
const DROP_PER_XRP = 1000000;
|
||||
class xrpIO {
|
||||
server;
|
||||
options;
|
||||
api;
|
||||
constructor(server, options = types_1.defaultOptions) {
|
||||
this.server = server;
|
||||
this.options = options;
|
||||
this.options.debug = options.debug ? Boolean(options.debug) : types_1.defaultOptions.debug;
|
||||
this.options.connectionTimeout = options.connectionTimeout ? Number(options.connectionTimeout) : types_1.defaultOptions.connectionTimeout;
|
||||
this.options.readMaxRetry = options.readMaxRetry ? Number(options.readMaxRetry) : types_1.defaultOptions.readMaxRetry;
|
||||
this.options.readRetryTimeout = options.readRetryTimeout ? Number(options.readRetryTimeout) : types_1.defaultOptions.readRetryTimeout;
|
||||
this.api = new xrpl_1.Client(server, {
|
||||
connectionTimeout: this.options.connectionTimeout
|
||||
});
|
||||
}
|
||||
async connect() {
|
||||
if (!this.api.isConnected())
|
||||
await this.api.connect();
|
||||
}
|
||||
async disconnect() {
|
||||
try {
|
||||
await this.api.disconnect();
|
||||
}
|
||||
catch (e) {
|
||||
console.log("DISCONNECT ERROR", e);
|
||||
}
|
||||
}
|
||||
async cloneApi() {
|
||||
let _api = new xrpl_1.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 xrpl_1.Client(this.server, {
|
||||
connectionTimeout: this.options.connectionTimeout
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
async sendPayment(data, to, secret, sequence, amount = "1") {
|
||||
const wallet = xrpl_1.Wallet.fromSecret(secret);
|
||||
this.dbg("Sending payment", wallet.address, '->', to);
|
||||
const _api = await this.cloneApi();
|
||||
try {
|
||||
const payment = await _api.autofill({
|
||||
TransactionType: 'Payment',
|
||||
Account: wallet.address,
|
||||
Destination: to,
|
||||
Sequence: sequence,
|
||||
Amount: amount,
|
||||
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) {
|
||||
this.dbg("SENDPAYMENT ERROR", error);
|
||||
await _api.disconnect();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async writeRaw(data, to, secret, sequence, amount = "1") {
|
||||
this.dbg("Writing data", data);
|
||||
const tx = await this.sendPayment(data, to, secret, sequence, amount);
|
||||
return tx.result.hash;
|
||||
}
|
||||
async getTransaction(hash, retry = 0) {
|
||||
this.dbg("Getting Tx", hash);
|
||||
try {
|
||||
return await this.api.request({
|
||||
command: 'tx',
|
||||
transaction: hash,
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
this.dbg(e);
|
||||
if (this.options.readMaxRetry != -1) {
|
||||
if (retry >= this.options.readMaxRetry)
|
||||
console.error("Retry limit exceeded for", hash, ". This is an irrecoverable error");
|
||||
throw e;
|
||||
}
|
||||
await new Promise(res => setTimeout(res, this.options.readRetryTimeout));
|
||||
return await this.getTransaction(hash, retry + 1);
|
||||
}
|
||||
}
|
||||
async readRaw(hash, verifyOwner) {
|
||||
if (!protocol_constants_1.NON_ZERO_TX_HASH.test(hash)) {
|
||||
throw (0, errors_1.ERR_BAD_TX_HASH)(hash);
|
||||
}
|
||||
const tx = await this.getTransaction(hash);
|
||||
if (verifyOwner && tx.result.Account != verifyOwner) {
|
||||
throw (0, errors_1.ERR_NO_VERIFY_OWNER)(hash, tx.result.Account, verifyOwner);
|
||||
}
|
||||
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;
|
||||
}
|
||||
async treeWrite(data, to, secret, format = 'L') {
|
||||
const wallet = xrpl_1.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');
|
||||
}
|
||||
async treeRead(hashes, verifyOwner) {
|
||||
const bad_hash = hashes.find(hash => !protocol_constants_1.NON_ZERO_TX_HASH.test(hash));
|
||||
if (bad_hash)
|
||||
throw (0, errors_1.ERR_BAD_TX_HASH)(bad_hash);
|
||||
const memos = await Promise.all(hashes.map(hash => this.readRaw(hash, verifyOwner)));
|
||||
const payload = await decompressB64(memos.map(memo => memo.data).join(''));
|
||||
if (memos.some(memo => memo.format === 'N')) {
|
||||
return await this.treeRead(JSON.parse(payload), verifyOwner);
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
async getAccountSequence(address) {
|
||||
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);
|
||||
}
|
||||
async estimateFee(data, denomination = 'DROPS', cost = 0) {
|
||||
data = await compressB64(data);
|
||||
const chunks = chunkString(data, PAYLOAD_SIZE);
|
||||
if (chunks.length === 1) {
|
||||
return (denomination === "DROPS" ? (cost + 1) : this.dropsToXrp(cost + 1));
|
||||
}
|
||||
return this.estimateFee(JSON.stringify(chunks.map(_ => genRandHex(64))), denomination, cost + chunks.length);
|
||||
}
|
||||
xrpToDrops(xrp) {
|
||||
return xrp * DROP_PER_XRP;
|
||||
}
|
||||
dropsToXrp(drops) {
|
||||
return drops * XRP_PER_DROP;
|
||||
}
|
||||
dbg(...args) {
|
||||
if (this.options.debug) {
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.xrpIO = xrpIO;
|
||||
Reference in New Issue
Block a user