UNPKG

@thorwallet/xchain-litecoin

Version:

Custom Litecoin client and utilities used by XChainJS clients

378 lines 15.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Client = void 0; const tslib_1 = require("tslib"); const Litecoin = tslib_1.__importStar(require("bitcoinjs-lib")); // https://github.com/bitcoinjs/bitcoinjs-lib const Utils = tslib_1.__importStar(require("./utils")); const sochain = tslib_1.__importStar(require("./sochain-api")); const xchain_crypto_1 = require("@thorwallet/xchain-crypto"); const xchain_util_1 = require("@thorwallet/xchain-util"); const react_native_simple_crypto_1 = tslib_1.__importDefault(require("react-native-simple-crypto")); const get_address_1 = require("./get-address"); /** * Custom Litecoin client */ class Client { /** * Constructor * Client is initialised with network type * Pass strict null as nodeAuth to disable auth for node json rpc * * @param {LitecoinClientParams} params */ constructor({ network = 'testnet', sochainUrl = 'https://sochain.com/api/v2', nodeUrl, nodeAuth = { username: 'thorchain', password: 'password', }, rootDerivationPaths = { mainnet: `m/84'/2'/0'/0/`, testnet: `m/84'/1'/0'/0/`, }, }) { this.phrase = ''; this.sochainUrl = ''; this.nodeUrl = ''; /** * Set/Update the sochain url. * * @param {string} url The new sochain url. * @returns {void} */ this.setSochainUrl = (url) => { this.sochainUrl = url; }; /** * Set/update a new phrase. * * @param {string} phrase A new phrase. * @returns {Address} The address from the given phrase * * @throws {"Invalid phrase"} * Thrown if the given phase is invalid. */ this.setPhrase = (phrase, walletIndex = 0) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (xchain_crypto_1.validatePhrase(phrase)) { this.phrase = phrase; return get_address_1.getAddress({ network: this.getNetwork(), phrase, index: walletIndex }); } else { throw new Error('Invalid phrase'); } }); /** * Purge client. * * @returns {void} */ this.purgeClient = () => { this.phrase = ''; }; /** * Set/update the current network. * * @param {Network} network `mainnet` or `testnet`. * @returns {void} * * @throws {"Network must be provided"} * Thrown if network has not been set before. */ this.setNetwork = (net) => { if (!net) { throw new Error('Network must be provided'); } else { this.net = net; } }; /** * Get the current network. * * @returns {Network} The current network. (`mainnet` or `testnet`) */ this.getNetwork = () => { return this.net; }; /** * Get the explorer url. * * @returns {string} The explorer url based on the network. */ this.getExplorerUrl = () => { return Utils.isTestnet(this.net) ? 'https://tltc.bitaps.com' : 'https://ltc.bitaps.com'; }; /** * Get the explorer url for the given address. * * @param {Address} address * @returns {string} The explorer url for the given address based on the network. */ this.getExplorerAddressUrl = (address) => { return `${this.getExplorerUrl()}/${address}`; }; /** * Get the explorer url for the given transaction id. * * @param {string} txID The transaction id * @returns {string} The explorer url for the given transaction id based on the network. */ this.getExplorerTxUrl = (txID) => { return `${this.getExplorerUrl()}/${txID}`; }; /** * @private * Get private key. * * Private function to get keyPair from the this.phrase * * @param {string} phrase The phrase to be used for generating privkey * @returns {ECPairInterface} The privkey generated from the given phrase * * @throws {"Could not get private key from phrase"} Throws an error if failed creating LTC keys from the given phrase * */ this.getLtcKeys = (phrase, index = 0) => tslib_1.__awaiter(this, void 0, void 0, function* () { const ltcNetwork = Utils.ltcNetwork(this.net); const seed = yield xchain_crypto_1.getSeed(phrase); const master = yield (yield xchain_crypto_1.bip32.fromSeed(seed, ltcNetwork)).derivePath(this.getFullDerivationPath(index)); if (!master.privateKey) { throw new Error('Could not get private key from phrase'); } return Litecoin.ECPair.fromPrivateKey(master.privateKey, { network: ltcNetwork }); }); /** * Validate the given address. * * @param {Address} address * @returns {boolean} `true` or `false` */ this.validateAddress = (address) => Utils.validateAddress(address, this.net); /** * Get transaction history of a given address with pagination options. * By default it will return the transaction history of the current wallet. * * @param {TxHistoryParams} params The options to get transaction history. (optional) * @returns {TxsPage} The transaction history. */ this.getTransactions = (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; // Sochain API doesn't have pagination parameter const offset = (_a = params === null || params === void 0 ? void 0 : params.offset) !== null && _a !== void 0 ? _a : 0; const limit = (params === null || params === void 0 ? void 0 : params.limit) || 10; try { const response = yield sochain.getAddress({ sochainUrl: this.sochainUrl, network: this.net, address: `${params === null || params === void 0 ? void 0 : params.address}`, }); const total = response.txs.length; const transactions = []; const txs = response.txs.filter((_, index) => offset <= index && index < offset + limit); for (const txItem of txs) { const rawTx = yield sochain.getTx({ sochainUrl: this.sochainUrl, network: this.net, hash: txItem.txid, }); const tx = { asset: xchain_util_1.AssetLTC, from: rawTx.inputs.map((i) => ({ from: i.address, amount: xchain_util_1.assetToBase(xchain_util_1.assetAmount(i.value, Utils.LTC_DECIMAL)), })), to: rawTx.outputs // ignore tx with type 'nulldata' .filter((i) => i.type !== 'nulldata') .map((i) => ({ to: i.address, amount: xchain_util_1.assetToBase(xchain_util_1.assetAmount(i.value, Utils.LTC_DECIMAL)) })), date: new Date(rawTx.time * 1000), type: 'transfer', hash: rawTx.txid, binanceFee: null, confirmations: rawTx.confirmations, ethCumulativeGasUsed: null, ethGas: null, ethGasPrice: null, ethGasUsed: null, ethTokenName: null, ethTokenSymbol: null, memo: null, }; transactions.push(tx); } const result = { total, txs: transactions, }; return result; } catch (error) { return Promise.reject(error); } }); /** * Get the transaction details of a given transaction id. * * @param {string} txId The transaction id. * @returns {Tx} The transaction details of the given transaction id. */ this.getTransactionData = (txId) => tslib_1.__awaiter(this, void 0, void 0, function* () { try { const rawTx = yield sochain.getTx({ sochainUrl: this.sochainUrl, network: this.net, hash: txId, }); return { asset: xchain_util_1.AssetLTC, from: rawTx.inputs.map((i) => ({ from: i.address, amount: xchain_util_1.assetToBase(xchain_util_1.assetAmount(i.value, Utils.LTC_DECIMAL)), })), to: rawTx.outputs.map((i) => ({ to: i.address, amount: xchain_util_1.assetToBase(xchain_util_1.assetAmount(i.value, Utils.LTC_DECIMAL)) })), date: new Date(rawTx.time * 1000), type: 'transfer', hash: rawTx.txid, confirmations: rawTx.confirmations, binanceFee: null, ethCumulativeGasUsed: null, ethGas: null, ethGasPrice: null, ethGasUsed: null, ethTokenName: null, ethTokenSymbol: null, memo: null, }; } catch (error) { return Promise.reject(error); } }); /** * Get the rates and fees. * * @param {string} memo The memo to be used for fee calculation (optional) * @returns {FeesWithRates} The fees and rates */ this.getFeesWithRates = (memo) => tslib_1.__awaiter(this, void 0, void 0, function* () { const nextBlockFeeRate = yield sochain.getSuggestedTxFee(); const rates = { fastest: nextBlockFeeRate * 5, fast: nextBlockFeeRate * 1, average: nextBlockFeeRate * 0.5, }; const fees = { type: 'byte', fast: Utils.calcFee(rates.fast, memo), average: Utils.calcFee(rates.average, memo), fastest: Utils.calcFee(rates.fastest, memo), }; return { fees, rates }; }); /** * Get the current fees. * * @returns {Fees} The fees without memo */ this.getFees = () => tslib_1.__awaiter(this, void 0, void 0, function* () { try { const { fees } = yield this.getFeesWithRates(); return fees; } catch (error) { return Promise.reject(error); } }); /** * Get the fees for transactions with memo. * If you want to get `Fees` and `FeeRates` at once, use `getFeesAndRates` method * * @param {string} memo * @returns {Fees} The fees with memo */ this.getFeesWithMemo = (memo) => tslib_1.__awaiter(this, void 0, void 0, function* () { try { const { fees } = yield this.getFeesWithRates(memo); return fees; } catch (error) { return Promise.reject(error); } }); /** * Get the fee rates for transactions without a memo. * If you want to get `Fees` and `FeeRates` at once, use `getFeesAndRates` method * * @returns {FeeRates} The fee rate */ this.getFeeRates = () => tslib_1.__awaiter(this, void 0, void 0, function* () { try { const { rates } = yield this.getFeesWithRates(); return rates; } catch (error) { return Promise.reject(error); } }); /** * Sign an arbitrary string message. * * * @returns {Signature} The current address. * * @throws {"Phrase must be provided"} Thrown if phrase has not been set before. */ this.signMessage = (msg) => tslib_1.__awaiter(this, void 0, void 0, function* () { const msgHash = yield react_native_simple_crypto_1.default.SHA.sha256(msg); const msgBuffer = Buffer.from(msgHash, 'hex'); const keys = yield this.getLtcKeys(this.phrase); const signature = keys.sign(msgBuffer).toString('hex'); const pubKey = keys.publicKey.toString('hex'); return { signature, pubKey }; }); /** * Transfer LTC. * * @param {TxParams&FeeRate} params The transfer options. * @returns {TxHash} The transaction hash. */ this.transfer = (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { try { const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0; const feeRate = params.feeRate || (yield this.getFeeRates()).fast; const { psbt } = yield Utils.buildTx(Object.assign(Object.assign({}, params), { feeRate, sender: yield get_address_1.getAddress({ network: this.getNetwork(), phrase: this.phrase, index: fromAddressIndex }), sochainUrl: this.sochainUrl, network: this.net })); const ltcKeys = this.getLtcKeys(this.phrase, fromAddressIndex); psbt.signAllInputs(yield ltcKeys); // Sign all inputs psbt.finalizeAllInputs(); // Finalise inputs const txHex = psbt.extractTransaction().toHex(); // TX extracted and formatted to hex return yield Utils.broadcastTx({ network: this.net, txHex, nodeUrl: this.nodeUrl, auth: this.nodeAuth, }); } catch (e) { return Promise.reject(e); } }); this.net = network; this.rootDerivationPaths = rootDerivationPaths; this.nodeUrl = !!nodeUrl ? nodeUrl : network === 'mainnet' ? 'https://ltc.thorchain.info' : 'https://testnet.ltc.thorchain.info'; this.nodeAuth = // Leave possibility to send requests without auth info for user // by strictly passing nodeAuth as null value nodeAuth === null ? undefined : nodeAuth; this.setSochainUrl(sochainUrl); } /** * Get getFullDerivationPath * * @param {number} index the HD wallet index * @returns {string} The bitcoin derivation path based on the network. */ getFullDerivationPath(index) { return this.rootDerivationPaths[this.net] + `${index}`; } } exports.Client = Client; //# sourceMappingURL=client.js.map