import { createHash } from 'crypto'; import rippleBinaryCodec from 'ripple-binary-codec'; import { RippleAPI } from 'ripple-lib'; // tslint:disable-next-line:no-submodule-imports import { Payment } from 'ripple-lib/dist/npm/transaction/payment'; // tslint:disable-next-line:no-submodule-imports import { Instructions, TransactionJSON } from 'ripple-lib/dist/npm/transaction/types'; import { Key } from '../../derivation'; enum HashPrefix { // transaction plus signature to give transaction ID livenet = 0x54584e00, mainnet = 0x54584e00, testnet = 0x73747800 } export class XRPTxProvider { create(params: { recipients: Array<{ address: string; amount: string }>; tag?: number; from: string; invoiceID?: string; fee: number; nonce: number; }) { const { recipients, tag, from, invoiceID, fee, nonce } = params; const { address, amount } = recipients[0]; const rippleAPI = new RippleAPI(); const { schemaValidate } = RippleAPI._PRIVATE.schemaValidator; const Flags = 2147483648; const amountStr = rippleAPI.dropsToXrp(amount); const feeStr = rippleAPI.dropsToXrp(fee.toString()); const payment: Payment = { source: { address: from, maxAmount: { value: amountStr, currency: 'XRP' } }, destination: { address, amount: { value: amountStr, currency: 'XRP' } } }; const instructions: Instructions = { fee: feeStr, sequence: nonce, maxLedgerVersion: null }; const txJSON: TransactionJSON = { TransactionType: 'Payment', Account: from, Destination: address, Amount: amount.toString(), Flags, Fee: fee.toString(), Sequence: nonce }; if (invoiceID) { payment.invoiceID = invoiceID; txJSON.InvoiceID = invoiceID; } if (tag) { payment.destination.tag = tag; txJSON.DestinationTag = tag; } schemaValidate('preparePaymentParameters', { address: from, payment, instructions }); schemaValidate('tx-json', txJSON); return rippleBinaryCodec.encode(txJSON); } getSignatureObject(params: { tx: string; key: Key }) { const { tx, key } = params; const txJSON = rippleBinaryCodec.decode(tx); let rippleAPI = new RippleAPI(); const signedTx = rippleAPI.sign(JSON.stringify(txJSON), { privateKey: key.privKey.toUpperCase(), publicKey: key.pubKey.toUpperCase() }); return signedTx; } getSignature(params: { tx: string; key: Key }): string { const { signedTransaction } = this.getSignatureObject(params); return signedTransaction; } getHash(params: { tx: string; network?: string }): string { const { tx, network = 'mainnet' } = params; const prefix = HashPrefix[network].toString(16).toUpperCase(); return this.sha512Half(prefix + tx); } applySignature(params: { tx: string; signature: string }): string { const { signature } = params; return signature; } sign(params: { tx: string; key: Key }): string { const { tx, key } = params; const signature = this.getSignature({ tx, key }); return this.applySignature({ tx, signature }); } sha512Half(hex: string): string { return createHash('sha512') .update(Buffer.from(hex, 'hex')) .digest('hex') .toUpperCase() .slice(0, 64); } }