@thorwallet/xchain-litecoin
Version:
Custom Litecoin client and utilities used by XChainJS clients
378 lines • 15.5 kB
JavaScript
;
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