@faast/tron-payments
Version:
Library to assist in processing tron payments, such as deriving addresses and sweeping funds
862 lines (849 loc) • 32.1 kB
JavaScript
import { pick, cloneDeep, omit } from 'lodash';
import { FeeLevel, createUnitConverters, BaseConfig, BaseUnsignedTransaction, BaseSignedTransaction, BaseTransactionInfo, BaseBroadcastResult, NetworkType, Payport, FeeRateType, TransactionStatus, FeeOptionCustom, PaymentsError, PaymentsErrorCode, PaymentsFactory } from '@faast/payments-common';
export { CreateTransactionOptions } from '@faast/payments-common';
import { isMatchingError, extendCodec, assertType, DelegateLogger, isNil, isType } from '@faast/ts-common';
import TronWeb from 'tronweb';
import promiseRetry from 'promise-retry';
import { string, union, null as null$1, undefined as undefined$1, array, record, number, boolean } from 'io-ts';
import BigNumber from 'bignumber.js';
import { fromBase58, fromSeed } from 'bip32';
import { keccak256 } from 'js-sha3';
import jsSHA from 'jssha';
import { ec as ec$1 } from 'elliptic';
import crypto from 'crypto';
const PACKAGE_NAME = 'tron-payments';
const COIN_SYMBOL = 'TRX';
const COIN_NAME = 'Tron';
const DECIMAL_PLACES = 6;
const MIN_BALANCE_SUN = 100000;
const MIN_BALANCE_TRX = 0.1;
const DEFAULT_FULL_NODE = process.env.TRX_FULL_NODE_URL || 'https://api.trongrid.io';
const DEFAULT_SOLIDITY_NODE = process.env.TRX_SOLIDITY_NODE_URL || 'https://api.trongrid.io';
const DEFAULT_EVENT_SERVER = process.env.TRX_EVENT_SERVER_URL || 'https://api.trongrid.io';
const DEFAULT_FEE_LEVEL = FeeLevel.Medium;
const TX_EXPIRATION_EXTENSION_SECONDS = 59 * 60;
const EXPIRATION_FUDGE_MS = 10 * 1000;
const PUBLIC_CONFIG_OMIT_FIELDS = ['logger', 'fullNode', 'solidityNode', 'eventServer', 'hdKey', 'keyPairs'];
const { toMainDenominationBigNumber, toMainDenominationString, toMainDenominationNumber, toBaseDenominationBigNumber, toBaseDenominationString, toBaseDenominationNumber, } = createUnitConverters(DECIMAL_PLACES);
function isValidXprv(xprv) {
return xprv.startsWith('xprv');
}
function isValidXpub(xpub) {
return xpub.startsWith('xpub');
}
function isValidAddress(address) {
return TronWeb.isAddress(address);
}
function isValidExtraId(extraId) {
return false;
}
function isValidPrivateKey(privateKey) {
try {
privateKeyToAddress(privateKey);
return true;
}
catch (e) {
return false;
}
}
function privateKeyToAddress(privateKey) {
const address = TronWeb.address.fromPrivateKey(privateKey);
if (isValidAddress(address)) {
return address;
}
else {
throw new Error('Validation failed for address derived from private key');
}
}
function toError(e) {
if (typeof e === 'string') {
return new Error(e);
}
return e;
}
const RETRYABLE_ERRORS = [
'Request failed',
];
const MAX_RETRIES = 2;
function retryIfDisconnected(fn, logger) {
return promiseRetry((retry, attempt) => {
return fn().catch(async (e) => {
e = toError(e);
if (isMatchingError(e, RETRYABLE_ERRORS)) {
logger.log(`Retryable error during tron-payments call, retrying ${MAX_RETRIES - attempt} more times`, e.toString());
retry(e);
}
throw e;
});
}, {
retries: MAX_RETRIES,
});
}
const BaseTronPaymentsConfig = extendCodec(BaseConfig, {}, {
fullNode: string,
solidityNode: string,
eventServer: string,
}, 'BaseTronPaymentsConfig');
const HdTronPaymentsConfig = extendCodec(BaseTronPaymentsConfig, {
hdKey: string,
}, 'HdTronPaymentsConfig');
const NullableOptionalString = union([string, null$1, undefined$1]);
const KeyPairTronPaymentsConfig = extendCodec(BaseTronPaymentsConfig, {
keyPairs: union([array(NullableOptionalString), record(number, NullableOptionalString)]),
}, 'KeyPairTronPaymentsConfig');
const TronPaymentsConfig = union([HdTronPaymentsConfig, KeyPairTronPaymentsConfig], 'TronPaymentsConfig');
const TronUnsignedTransaction = extendCodec(BaseUnsignedTransaction, {
id: string,
amount: string,
fee: string,
}, 'TronUnsignedTransaction');
const TronSignedTransaction = extendCodec(BaseSignedTransaction, {}, {}, 'TronSignedTransaction');
const TronTransactionInfo = extendCodec(BaseTransactionInfo, {}, {}, 'TronTransactionInfo');
const TronBroadcastResult = extendCodec(BaseBroadcastResult, {
rebroadcast: boolean,
}, 'TronBroadcastResult');
class TronPaymentsUtils {
constructor(config = {}) {
this.coinSymbol = COIN_SYMBOL;
this.coinName = COIN_NAME;
this.coinDecimals = DECIMAL_PLACES;
this.isValidXprv = isValidXprv;
this.isValidXpub = isValidXpub;
this.isValidPrivateKey = isValidPrivateKey;
this.privateKeyToAddress = privateKeyToAddress;
assertType(BaseTronPaymentsConfig, config);
this.networkType = config.network || NetworkType.Mainnet;
this.logger = new DelegateLogger(config.logger, PACKAGE_NAME);
this.fullNode = config.fullNode || DEFAULT_FULL_NODE;
this.solidityNode = config.solidityNode || DEFAULT_SOLIDITY_NODE;
this.eventServer = config.eventServer || DEFAULT_EVENT_SERVER;
this.tronweb = new TronWeb(this.fullNode, this.solidityNode, this.eventServer);
}
async init() { }
async destroy() { }
isValidExtraId(extraId) {
return isValidExtraId();
}
isValidAddress(address) {
return isValidAddress(address);
}
standardizeAddress(address) {
if (!isValidAddress(address)) {
return null;
}
return address;
}
async _getPayportValidationMessage(payport) {
const { address, extraId } = payport;
if (!isValidAddress(address)) {
return 'Invalid payport address';
}
if (!isNil(extraId) && !isValidExtraId()) {
return 'Invalid payport extraId';
}
}
async getPayportValidationMessage(payport) {
try {
payport = assertType(Payport, payport, 'payport');
}
catch (e) {
return e.message;
}
return this._getPayportValidationMessage(payport);
}
async validatePayport(payport) {
payport = assertType(Payport, payport, 'payport');
const message = await this._getPayportValidationMessage(payport);
if (message) {
throw new Error(message);
}
}
async isValidPayport(payport) {
return Payport.is(payport) && !(await this._getPayportValidationMessage(payport));
}
toMainDenomination(amount) {
return toMainDenominationString(amount);
}
toBaseDenomination(amount) {
return toBaseDenominationString(amount);
}
getFeeRateRecommendation(level) {
return { feeRate: '0', feeRateType: FeeRateType.Base };
}
async _retryDced(fn) {
return retryIfDisconnected(fn, this.logger);
}
getCurrentBlockNumber() {
return this._retryDced(async () => (await this.tronweb.trx.getCurrentBlock()).block_header.raw_data.number);
}
async getAddressUtxos() {
return [];
}
async getAddressNextSequenceNumber() {
return null;
}
canSweepBalanceSun(balanceSun) {
return balanceSun > MIN_BALANCE_SUN;
}
isAddressBalanceSweepable(balanceTrx) {
return new BigNumber(balanceTrx).gt(MIN_BALANCE_TRX);
}
async getAddressBalance(address) {
try {
const balanceSun = await this._retryDced(() => this.tronweb.trx.getBalance(address));
const sweepable = this.canSweepBalanceSun(balanceSun);
const confirmedBalance = toMainDenominationBigNumber(balanceSun);
const spendableBalance = BigNumber.max(0, confirmedBalance.minus(MIN_BALANCE_TRX));
return {
confirmedBalance: confirmedBalance.toString(),
unconfirmedBalance: '0',
spendableBalance: spendableBalance.toString(),
sweepable,
requiresActivation: false,
minimumBalance: String(MIN_BALANCE_TRX),
};
}
catch (e) {
throw toError(e);
}
}
extractTxFields(tx) {
var _a, _b, _c, _d, _e;
const contractParam = (_e = (_d = (_c = (_b = (_a = tx.raw_data) === null || _a === void 0 ? void 0 : _a.contract) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.parameter) === null || _d === void 0 ? void 0 : _d.value) !== null && _e !== void 0 ? _e : null;
if (!(contractParam && typeof contractParam.amount === 'number')) {
throw new Error('Unable to get transaction');
}
const amountSun = contractParam.amount || 0;
const amountTrx = this.toMainDenomination(amountSun);
const toAddress = this.tronweb.address.fromHex(contractParam.to_address);
const fromAddress = this.tronweb.address.fromHex(contractParam.owner_address);
return {
amountTrx,
amountSun,
toAddress,
fromAddress,
};
}
async getTransactionInfo(txid) {
var _a, _b, _c, _d, _e;
try {
const [tx, txInfo, currentBlock] = await Promise.all([
this._retryDced(() => this.tronweb.trx.getTransaction(txid)),
this._retryDced(() => this.tronweb.trx.getTransactionInfo(txid)),
this._retryDced(() => this.tronweb.trx.getCurrentBlock()),
]);
const { amountTrx, fromAddress, toAddress } = this.extractTxFields(tx);
const contractRet = (_b = (_a = tx.ret) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.contractRet;
const isExecuted = contractRet === 'SUCCESS';
const block = txInfo.blockNumber || null;
const feeTrx = this.toMainDenomination(txInfo.fee || 0);
const currentBlockNumber = (_e = (_d = (_c = currentBlock.block_header) === null || _c === void 0 ? void 0 : _c.raw_data) === null || _d === void 0 ? void 0 : _d.number) !== null && _e !== void 0 ? _e : 0;
const confirmations = currentBlockNumber && block ? currentBlockNumber - block : 0;
const isConfirmed = confirmations > 0;
const confirmationTimestamp = txInfo.blockTimeStamp ? new Date(txInfo.blockTimeStamp) : null;
let status = TransactionStatus.Pending;
if (isConfirmed) {
if (!isExecuted) {
status = TransactionStatus.Failed;
}
status = TransactionStatus.Confirmed;
}
return {
id: tx.txID,
amount: amountTrx,
toAddress,
fromAddress,
toExtraId: null,
fromIndex: null,
toIndex: null,
fee: feeTrx,
sequenceNumber: null,
isExecuted,
isConfirmed,
confirmations,
confirmationId: block ? String(block) : null,
confirmationTimestamp,
status,
data: {
...tx,
...txInfo,
currentBlock: pick(currentBlock, 'block_header', 'blockID'),
},
};
}
catch (e) {
throw toError(e);
}
}
}
class BaseTronPayments extends TronPaymentsUtils {
constructor(config) {
super(config);
}
async init() { }
async destroy() { }
requiresBalanceMonitor() {
return false;
}
async getBalance(resolveablePayport) {
const payport = await this.resolvePayport(resolveablePayport);
return this.getAddressBalance(payport.address);
}
async resolveFeeOption(feeOption) {
let targetFeeLevel;
if (isType(FeeOptionCustom, feeOption)) {
if (feeOption.feeRate !== '0') {
throw new Error('tron-payments custom fees are unsupported');
}
targetFeeLevel = FeeLevel.Custom;
}
else {
targetFeeLevel = feeOption.feeLevel || DEFAULT_FEE_LEVEL;
}
return {
targetFeeLevel,
targetFeeRate: '0',
targetFeeRateType: FeeRateType.Base,
feeBase: '0',
feeMain: '0',
};
}
async buildUnsignedTx(toAddress, amountSun, fromAddress) {
let tx = await this.tronweb.transactionBuilder.sendTrx(toAddress, amountSun, fromAddress);
tx = await this.tronweb.transactionBuilder.extendExpiration(tx, TX_EXPIRATION_EXTENSION_SECONDS);
return tx;
}
async createServiceTransaction() {
return null;
}
async createSweepTransaction(from, to, options = {}) {
this.logger.debug('createSweepTransaction', from, to);
try {
const { fromAddress, fromIndex, fromPayport, toAddress, toIndex } = await this.resolveFromTo(from, to);
const { targetFeeLevel, targetFeeRate, targetFeeRateType, feeBase, feeMain } = await this.resolveFeeOption(options);
const feeSun = Number.parseInt(feeBase);
const { confirmedBalance: balanceTrx } = await this.getBalance(fromPayport);
const balanceSun = toBaseDenominationNumber(balanceTrx);
if (!this.canSweepBalanceSun(balanceSun)) {
throw new Error(`Insufficient balance (${balanceTrx}) to sweep with fee of ${feeMain} ` +
`while maintaining a minimum required balance of ${MIN_BALANCE_TRX}`);
}
const amountSun = balanceSun - feeSun - MIN_BALANCE_SUN;
const amountTrx = this.toMainDenomination(amountSun);
const tx = await this.buildUnsignedTx(toAddress, amountSun, fromAddress);
return {
status: TransactionStatus.Unsigned,
id: tx.txID,
fromAddress,
toAddress,
toExtraId: null,
fromIndex,
toIndex,
amount: amountTrx,
fee: feeMain,
targetFeeLevel,
targetFeeRate,
targetFeeRateType,
sequenceNumber: null,
data: tx,
};
}
catch (e) {
throw toError(e);
}
}
async createTransaction(from, to, amountTrx, options = {}) {
this.logger.debug('createTransaction', from, to, amountTrx);
try {
const { fromAddress, fromIndex, fromPayport, toAddress, toIndex } = await this.resolveFromTo(from, to);
const { targetFeeLevel, targetFeeRate, targetFeeRateType, feeBase, feeMain } = await this.resolveFeeOption(options);
const feeSun = Number.parseInt(feeBase);
const { confirmedBalance: balanceTrx } = await this.getBalance(fromPayport);
const balanceSun = toBaseDenominationNumber(balanceTrx);
const amountSun = toBaseDenominationNumber(amountTrx);
if (balanceSun - feeSun - MIN_BALANCE_SUN < amountSun) {
throw new Error(`Insufficient balance (${balanceTrx}) to send ${amountTrx} including fee of ${feeMain} ` +
`while maintaining a minimum required balance of ${MIN_BALANCE_TRX}`);
}
const tx = await this.buildUnsignedTx(toAddress, amountSun, fromAddress);
return {
status: TransactionStatus.Unsigned,
id: tx.txID,
fromAddress,
toAddress,
toExtraId: null,
fromIndex,
toIndex,
amount: amountTrx,
fee: feeMain,
targetFeeLevel,
targetFeeRate,
targetFeeRateType,
sequenceNumber: null,
data: tx,
};
}
catch (e) {
throw toError(e);
}
}
async signTransaction(unsignedTx) {
try {
const fromPrivateKey = await this.getPrivateKey(unsignedTx.fromIndex);
const unsignedRaw = cloneDeep(unsignedTx.data);
const signedTx = await this.tronweb.trx.sign(unsignedRaw, fromPrivateKey);
return {
...unsignedTx,
status: TransactionStatus.Signed,
data: signedTx,
};
}
catch (e) {
throw toError(e);
}
}
async broadcastTransaction(tx) {
try {
const status = await this._retryDced(() => this.tronweb.trx.sendRawTransaction(tx.data));
let success = false;
let rebroadcast = false;
if (status.result || status.code === 'SUCCESS') {
success = true;
}
else {
try {
await this._retryDced(() => this.tronweb.trx.getTransaction(tx.id));
success = true;
rebroadcast = true;
}
catch (e) {
const expiration = tx.data && tx.data.raw_data.expiration;
if (expiration && Date.now() > expiration + EXPIRATION_FUDGE_MS) {
throw new PaymentsError(PaymentsErrorCode.TxExpired, 'Transaction has expired');
}
}
}
if (success) {
return {
id: tx.id,
rebroadcast,
};
}
else {
let statusCode = status.code;
if (statusCode === 'TRANSACTION_EXPIRATION_ERROR') {
throw new PaymentsError(PaymentsErrorCode.TxExpired, `${statusCode} ${status.message || ''}`);
}
if (statusCode === 'DUP_TRANSACTION_ERROR') {
statusCode = 'DUP_TX_BUT_TX_NOT_FOUND_SO_PROBABLY_INVALID_TX_ERROR';
}
this.logger.warn(`Tron broadcast tx unsuccessful ${tx.id}`, status);
throw new Error(`Failed to broadcast transaction: ${statusCode} ${status.message}`);
}
}
catch (e) {
throw toError(e);
}
}
isSweepableBalance(balanceTrx) {
return this.isAddressBalanceSweepable(balanceTrx);
}
usesSequenceNumber() {
return false;
}
async getNextSequenceNumber() {
return null;
}
usesUtxos() {
return false;
}
async getUtxos() {
return [];
}
async resolvePayport(payport) {
if (typeof payport === 'number') {
return this.getPayport(payport);
}
else if (typeof payport === 'string') {
if (!isValidAddress(payport)) {
throw new Error(`Invalid TRON address: ${payport}`);
}
return { address: payport };
}
if (!this.isValidPayport(payport)) {
throw new Error(`Invalid TRON payport: ${JSON.stringify(payport)}`);
}
return payport;
}
async resolveFromTo(from, to) {
const fromPayport = await this.getPayport(from);
const toPayport = await this.resolvePayport(to);
return {
fromAddress: fromPayport.address,
fromIndex: from,
fromExtraId: fromPayport.extraId,
fromPayport,
toAddress: toPayport.address,
toIndex: typeof to === 'number' ? to : null,
toExtraId: toPayport.extraId,
toPayport,
};
}
async createMultiOutputTransaction(from, to, options = {}) {
return null;
}
}
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
const ALPHABET_MAP = {};
for (let i = 0; i < ALPHABET.length; i++) {
ALPHABET_MAP[ALPHABET.charAt(i)] = i;
}
const BASE = 58;
function encode58(buffer) {
if (buffer.length === 0) {
return '';
}
let i;
let j;
const digits = [0];
for (i = 0; i < buffer.length; i++) {
for (j = 0; j < digits.length; j++) {
digits[j] <<= 8;
}
digits[0] += buffer[i];
let carry = 0;
for (j = 0; j < digits.length; ++j) {
digits[j] += carry;
carry = (digits[j] / BASE) | 0;
digits[j] %= BASE;
}
while (carry) {
digits.push(carry % BASE);
carry = (carry / BASE) | 0;
}
}
for (i = 0; buffer[i] === 0 && i < buffer.length - 1; i++) {
digits.push(0);
}
return digits
.reverse()
.map(digit => ALPHABET[digit])
.join('');
}
function decode58(s) {
if (s.length === 0) {
return [];
}
let i;
let j;
const bytes = [0];
for (i = 0; i < s.length; i++) {
const c = s[i];
if (!(c in ALPHABET_MAP)) {
throw new Error('Non-base58 character');
}
for (j = 0; j < bytes.length; j++) {
bytes[j] *= BASE;
}
bytes[0] += ALPHABET_MAP[c];
let carry = 0;
for (j = 0; j < bytes.length; ++j) {
bytes[j] += carry;
carry = bytes[j] >> 8;
bytes[j] &= 0xff;
}
while (carry) {
bytes.push(carry & 0xff);
carry >>= 8;
}
}
for (i = 0; s[i] === '1' && i < s.length - 1; i++) {
bytes.push(0);
}
return bytes.reverse();
}
const ec = new ec$1('secp256k1');
const derivationPath = "m/44'/195'/0'";
const derivationPathParts = derivationPath.split('/').slice(1);
function deriveAddress(xpub, index) {
if (!isValidXpub(xpub)) {
throw new Error('Invalid xpub');
}
const key = fromBase58(xpub);
const derived = deriveBasePath(key)
.derive(0)
.derive(index);
return hdPublicKeyToAddress(derived);
}
function derivePrivateKey(xprv, index) {
if (!isValidXprv(xprv)) {
throw new Error('Invalid xprv');
}
const key = fromBase58(xprv);
const derived = deriveBasePath(key)
.derive(0)
.derive(index);
return hdPrivateKeyToPrivateKey(derived);
}
function xprvToXpub(xprv) {
const key = typeof xprv === 'string' ? fromBase58(xprv) : xprv;
const derivedPubKey = deriveBasePath(key);
return derivedPubKey.neutered().toBase58();
}
function generateNewKeys() {
const key = fromSeed(crypto.randomBytes(32));
const xprv = key.toBase58();
const xpub = xprvToXpub(xprv);
return {
xprv,
xpub,
};
}
function deriveBasePath(key) {
const parts = derivationPathParts.slice(key.depth);
if (parts.length > 0) {
return key.derivePath(`m/${parts.join('/')}`);
}
return key;
}
function hdPublicKeyToAddress(key) {
return addressBytesToB58CheckAddress(pubBytesToTronBytes(bip32PublicToTronPublic(key.publicKey)));
}
function hdPrivateKeyToPrivateKey(key) {
if (key.isNeutered() || typeof key.privateKey === 'undefined') {
throw new Error('Invalid HD private key, must not be neutered');
}
return bip32PrivateToTronPrivate(key.privateKey);
}
function bip32PublicToTronPublic(pubKey) {
const pubkey = ec.keyFromPublic(pubKey).getPublic();
const x = pubkey.getX();
const y = pubkey.getY();
let xHex = x.toString('hex');
while (xHex.length < 64) {
xHex = `0${xHex}`;
}
let yHex = y.toString('hex');
while (yHex.length < 64) {
yHex = `0${yHex}`;
}
const pubkeyHex = `04${xHex}${yHex}`;
const pubkeyBytes = hexStr2byteArray(pubkeyHex);
return pubkeyBytes;
}
function bip32PrivateToTronPrivate(priKeyBytes) {
const key = ec.keyFromPrivate(priKeyBytes, 'bytes');
let priKeyHex = key.getPrivate('hex');
while (priKeyHex.length < 64) {
priKeyHex = `0${priKeyHex}`;
}
let privArray = hexStr2byteArray(priKeyHex);
return byteArray2hexStr(privArray);
}
const ADDRESS_PREFIX = '41';
function byte2hexStr(byte) {
const hexByteMap = '0123456789ABCDEF';
let str = '';
str += hexByteMap.charAt(byte >> 4);
str += hexByteMap.charAt(byte & 0x0f);
return str;
}
function hexStr2byteArray(str) {
const byteArray = Array();
let d = 0;
let j = 0;
let k = 0;
for (let i = 0; i < str.length; i++) {
const c = str.charAt(i);
if (isHexChar(c)) {
d <<= 4;
d += hexChar2byte(c);
j++;
if (0 === j % 2) {
byteArray[k++] = d;
d = 0;
}
}
}
return byteArray;
}
function isHexChar(c) {
return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9');
}
function hexChar2byte(c) {
let d = 0;
if (c >= 'A' && c <= 'F') {
d = c.charCodeAt(0) - 'A'.charCodeAt(0) + 10;
}
else if (c >= 'a' && c <= 'f') {
d = c.charCodeAt(0) - 'a'.charCodeAt(0) + 10;
}
else if (c >= '0' && c <= '9') {
d = c.charCodeAt(0) - '0'.charCodeAt(0);
}
return d;
}
function byteArray2hexStr(byteArray) {
let str = '';
for (let i = 0; i < byteArray.length; i++) {
str += byte2hexStr(byteArray[i]);
}
return str;
}
function pubBytesToTronBytes(pubBytes) {
if (pubBytes.length === 65) {
pubBytes = pubBytes.slice(1);
}
const hash = keccak256(pubBytes).toString();
const addressHex = ADDRESS_PREFIX + hash.substring(24);
return hexStr2byteArray(addressHex);
}
function addressBytesToB58CheckAddress(addressBytes) {
const hash0 = SHA256(addressBytes);
const hash1 = SHA256(hash0);
let checkSum = hash1.slice(0, 4);
checkSum = addressBytes.concat(checkSum);
return encode58(checkSum);
}
function SHA256(msgBytes) {
const shaObj = new jsSHA('SHA-256', 'HEX');
const msgHex = byteArray2hexStr(msgBytes);
shaObj.update(msgHex);
const hashHex = shaObj.getHash('HEX');
return hexStr2byteArray(hashHex);
}
class HdTronPayments extends BaseTronPayments {
constructor(config) {
super(config);
this.config = config;
if (isValidXprv(config.hdKey)) {
this.xprv = config.hdKey;
this.xpub = xprvToXpub(this.xprv);
}
else if (isValidXpub(config.hdKey)) {
this.xprv = null;
this.xpub = config.hdKey;
}
else {
throw new Error('Account must be a valid xprv or xpub');
}
}
getXpub() {
return this.xpub;
}
getFullConfig() {
return this.config;
}
getPublicConfig() {
return {
...omit(this.config, PUBLIC_CONFIG_OMIT_FIELDS),
hdKey: this.getXpub(),
};
}
getAccountId(index) {
return this.getXpub();
}
getAccountIds() {
return [this.getXpub()];
}
async getPayport(index) {
const xpub = this.getXpub();
const address = deriveAddress(xpub, index);
if (!isValidAddress(address)) {
throw new Error(`Cannot get address ${index} - validation failed for derived address`);
}
return { address };
}
async getPrivateKey(index) {
if (!this.xprv) {
throw new Error(`Cannot get private key ${index} - HdTronPayments was created with an xpub`);
}
return derivePrivateKey(this.xprv, index);
}
}
HdTronPayments.generateNewKeys = generateNewKeys;
class KeyPairTronPayments extends BaseTronPayments {
constructor(config) {
super(config);
this.config = config;
this.addresses = {};
this.privateKeys = {};
this.addressIndices = {};
Object.entries(config.keyPairs).forEach(([iString, addressOrKey]) => {
if (typeof addressOrKey === 'undefined' || addressOrKey === null) {
return;
}
const i = Number.parseInt(iString);
if (isValidAddress(addressOrKey)) {
this.addresses[i] = addressOrKey;
this.privateKeys[i] = null;
this.addressIndices[addressOrKey] = i;
return;
}
if (isValidPrivateKey(addressOrKey)) {
const address = privateKeyToAddress(addressOrKey);
this.addresses[i] = address;
this.privateKeys[i] = addressOrKey;
this.addressIndices[address] = i;
return;
}
throw new Error(`KeyPairTronPaymentsConfig.keyPairs[${i}] is not a valid private key or address`);
});
}
getFullConfig() {
return this.config;
}
getPublicConfig() {
return {
...omit(this.config, PUBLIC_CONFIG_OMIT_FIELDS),
keyPairs: this.addresses,
};
}
getAccountId(index) {
const accountId = this.addresses[index];
if (!accountId) {
throw new Error(`No KeyPairTronPayments account configured at index ${index}`);
}
return accountId;
}
getAccountIds() {
return Object.keys(this.addressIndices);
}
async getPayport(index) {
const address = this.addresses[index];
if (typeof address === 'undefined') {
throw new Error(`Cannot get address ${index} - keyPair[${index}] is undefined`);
}
return { address };
}
async getPrivateKey(index) {
const privateKey = this.privateKeys[index];
if (typeof privateKey === 'undefined') {
throw new Error(`Cannot get private key ${index} - keyPair[${index}] is undefined`);
}
if (privateKey === null) {
throw new Error(`Cannot get private key ${index} - keyPair[${index}] is a public address`);
}
return privateKey;
}
}
class TronPaymentsFactory extends PaymentsFactory {
constructor() {
super(...arguments);
this.packageName = PACKAGE_NAME;
}
newPayments(config) {
if (HdTronPaymentsConfig.is(config)) {
return new HdTronPayments(config);
}
if (KeyPairTronPaymentsConfig.is(config)) {
return new KeyPairTronPayments(config);
}
throw new Error(`Cannot instantiate ${this.packageName} for unsupported config`);
}
newUtils(config) {
return new TronPaymentsUtils(assertType(BaseTronPaymentsConfig, config, 'config'));
}
}
export { BaseTronPayments, BaseTronPaymentsConfig, COIN_NAME, COIN_SYMBOL, DECIMAL_PLACES, DEFAULT_EVENT_SERVER, DEFAULT_FEE_LEVEL, DEFAULT_FULL_NODE, DEFAULT_SOLIDITY_NODE, EXPIRATION_FUDGE_MS, HdTronPayments, HdTronPaymentsConfig, KeyPairTronPayments, KeyPairTronPaymentsConfig, MIN_BALANCE_SUN, MIN_BALANCE_TRX, PACKAGE_NAME, PUBLIC_CONFIG_OMIT_FIELDS, TX_EXPIRATION_EXTENSION_SECONDS, TronBroadcastResult, TronPaymentsConfig, TronPaymentsFactory, TronPaymentsUtils, TronSignedTransaction, TronTransactionInfo, TronUnsignedTransaction, decode58, derivationPath, deriveAddress, derivePrivateKey, encode58, generateNewKeys, isValidAddress, isValidExtraId, isValidPrivateKey, isValidXprv, isValidXpub, privateKeyToAddress, toBaseDenominationBigNumber, toBaseDenominationNumber, toBaseDenominationString, toMainDenominationBigNumber, toMainDenominationNumber, toMainDenominationString, xprvToXpub };
//# sourceMappingURL=index.es.js.map