UNPKG

@tatumio/tatum

Version:

Tatum JS SDK

426 lines 18 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Address = exports.AddressTron = exports.AddressTezos = void 0; const bignumber_js_1 = require("bignumber.js"); const typedi_1 = require("typedi"); const connector_1 = require("../../connector"); const dto_1 = require("../../dto"); const util_1 = require("../../util"); const decode_1 = require("../../util/decode"); const tatum_1 = require("../tatum"); let AddressTezos = class AddressTezos { constructor(id) { this.id = id; this.config = typedi_1.Container.of(this.id).get(util_1.CONFIG); this.connector = typedi_1.Container.of(this.id).get(connector_1.TatumConnector); } /** * Get balance of all tokens for a given Tezos address. */ async getBalance({ address, tokenTypes, page, pageSize, }) { const chain = this.config.network; return util_1.ErrorUtils.tryFail(async () => { const data = await this.connector.get({ path: `data/balances`, params: { pageSize, offset: page, chain, addresses: address, tokenTypes: tokenTypes?.join(','), }, }); return data.result.map(({ address, symbol, balance, type, tokenAddress, tokenId }) => { const tokenBalance = { address, asset: symbol, balance, type, tokenAddress, tokenId, }; if (type === 'native') { tokenBalance.asset = util_1.Constant.CURRENCY_NAMES[chain]; delete tokenBalance.tokenAddress; delete tokenBalance.tokenId; } return tokenBalance; }); }); } /** * Get all transactions, that are related to the given address. It could be both incoming and outgoing transactions. */ async getTransactions({ address, transactionDirection, fromBlock, toBlock, pageSize = 10, page = 0, cursor = undefined, }) { const chain = this.config.network; return util_1.ErrorUtils.tryFail(async () => { const data = await this.connector.get({ path: `data/transactions`, params: { chain, addresses: address, transactionSubtype: transactionDirection, blockFrom: fromBlock, blockTo: toBlock, pageSize, offset: page, cursor, }, }); return { result: data.result, prevPage: data.prevPage, nextPage: data.nextPage, }; }); } }; exports.AddressTezos = AddressTezos; exports.AddressTezos = AddressTezos = __decorate([ (0, typedi_1.Service)({ factory: (data) => new AddressTezos(data.id), transient: true, }), __metadata("design:paramtypes", [String]) ], AddressTezos); let AddressTron = class AddressTron { constructor(id) { this.id = id; this.config = typedi_1.Container.of(this.id).get(util_1.CONFIG); this.connector = typedi_1.Container.of(this.id).get(connector_1.TatumConnector); } /** * Get balance of all tokens for a given address. */ async getBalance({ address }) { const chain = this.config.network; return util_1.ErrorUtils.tryFail(async () => { const fullBalances = await this.getFullBalance(address); const nativeBalances = [fullBalances.nativeBalance]; const tokenBalances = fullBalances.tokenBalances; const result = formatNativeBalances(nativeBalances, [address], chain); if (!tokenBalances) { return result; } return [...result, ...tokenBalances]; }); } async getFullBalance(address) { const data = await this.connector.get({ path: `tron/account/${address}`, }); let tokenBalances = []; if (data.trc20.length > 0) { tokenBalances = await this.processTRC20TokenBalanceDetails(address, data.trc20); } return { nativeBalance: data.balance.toString(), tokenBalances, }; } async processTRC20TokenBalanceDetails(address, tokenBalances) { const serializedTokenBalance = []; for (const token of tokenBalances) { const tokenAddress = Object.keys(token)[0]; const asset = await util_1.Utils.getRpc(this.id, this.config) .triggerConstantContract(tokenAddress, tokenAddress, 'symbol()', '', { visible: true, }) .then((r) => (0, decode_1.decodeHexString)(r.constant_result[0])); const decimals = await util_1.Utils.getRpc(this.id, this.config) .triggerConstantContract(tokenAddress, tokenAddress, 'decimals()', '', { visible: true, }) .then((r) => (0, decode_1.decodeUInt256)(r.constant_result[0])); const balance = Object.values(token)[0]; serializedTokenBalance.push({ asset, decimals, balance, type: 'fungible', address, tokenAddress, }); } return serializedTokenBalance; } }; exports.AddressTron = AddressTron; exports.AddressTron = AddressTron = __decorate([ (0, typedi_1.Service)({ factory: (data) => { return new AddressTron(data.id); }, transient: true, }), __metadata("design:paramtypes", [String]) ], AddressTron); let Address = class Address { constructor(id) { this.id = id; this.config = typedi_1.Container.of(this.id).get(util_1.CONFIG); this.connector = typedi_1.Container.of(this.id).get(connector_1.TatumConnector); } /** * Get balance of all tokens for a given address. * You can get balance of multiple addresses in one call. */ async getBalance({ page = 0, pageSize = 10, addresses, tokenTypes, }) { const chain = this.config.network; return util_1.ErrorUtils.tryFail(async () => { const result = []; if (!tokenTypes || tokenTypes.includes('native')) { const nativeBalances = await this.getNativeBalance(addresses); result.push(...formatNativeBalances(nativeBalances, addresses, chain)); if (tokenTypes) { tokenTypes = tokenTypes.filter((tokenType) => tokenType !== 'native'); } } if (tokenTypes?.length === 0) { return result; } const tokenBalances = (0, dto_1.isDataApiEvmEnabledNetwork)(chain) && (await this.connector .get({ path: `data/balances`, params: { pageSize, offset: page, excludeMetadata: true, chain, addresses: addresses.join(','), tokenTypes: tokenTypes?.join(','), }, }) .then((r) => r.result)); if (tokenBalances) { const serializedTokenBalances = await this.processTokenBalanceDetails(tokenBalances, chain); result.push(...serializedTokenBalances); } return result; }); } /** * Get all transactions, that are related to the given address. It could be both incoming and outgoing transactions. */ async getTransactions({ address, transactionDirection, transactionTypes, fromBlock, toBlock, pageSize = 10, page = 0, tokenAddress, }) { const chain = this.config.network; return util_1.ErrorUtils.tryFail(async () => { if ((0, dto_1.isDataApiEnabledNetwork)(chain)) { return this.connector .get({ path: `data/transactions`, params: { chain, addresses: address, transactionTypes: transactionTypes?.join(), transactionSubtype: transactionDirection, blockFrom: fromBlock, blockTo: toBlock, pageSize, offset: page, tokenAddress, }, }) .then((r) => r.result); } let path; if ([tatum_1.Network.BITCOIN, tatum_1.Network.BITCOIN_TESTNET].includes(chain)) { path = `bitcoin/transaction/address/${address}`; } else if ([tatum_1.Network.LITECOIN, tatum_1.Network.LITECOIN_TESTNET].includes(chain)) { path = `litecoin/transaction/address/${address}`; } else if ([tatum_1.Network.DOGECOIN, tatum_1.Network.DOGECOIN_TESTNET].includes(chain)) { path = `dogecoin/transaction/address/${address}`; } if (!path) { // TODO: implement for other networks - TRON, XRP, CARDANO, SOL, XLM etc etc throw new Error(`Not supported for ${chain} network.`); } return this.processUtxoBasedTxs(path, pageSize, page, transactionDirection, chain, address, fromBlock, toBlock); }); } async processTokenBalanceDetails(tokenBalances, chain) { const result = []; // Processing token details const details = await Promise.all(tokenBalances.map((details) => this.connector.get({ path: 'data/tokens', params: { chain, tokenAddress: details.tokenAddress, }, }))); for (let i = 0; i < tokenBalances.length; i++) { const tokenBalance = tokenBalances[i]; const item = { address: tokenBalance.address, tokenAddress: tokenBalance.tokenAddress, balance: tokenBalance.balance, type: tokenBalance.type, }; if (tokenBalance.lastUpdatedBlockNumber) { item.lastUpdatedBlockNumber = tokenBalance.lastUpdatedBlockNumber; } if (details[i].symbol) { item.asset = details[i].symbol; } if (details[i].decimals) { item.decimals = details[i].decimals; } if (tokenBalance.tokenId) { item.tokenId = tokenBalance.tokenId; } result.push(item); } return result; } processUtxoBasedTxs(path, pageSize, page, transactionDirection, chain, address, blockFrom, blockTo) { return this.connector .get({ path, basePath: util_1.Constant.TATUM_API_URL.V3, params: { pageSize, offset: page * pageSize, txType: transactionDirection, blockFrom, blockTo, }, }) .then((r) => { const result = []; for (const tx of r) { const item = { chain, blockNumber: tx.blockNumber, timestamp: tx.time, transactionType: transactionDirection || 'incoming', hash: tx.hash, address, amount: '0', }; tx.inputs .filter((i) => i.coin.address === address) .forEach((i) => { item.amount = new bignumber_js_1.BigNumber(item.amount) .minus(new bignumber_js_1.BigNumber(i.coin.value).dividedBy(typeof i.coin.value === 'number' ? 10 ** util_1.Constant.DECIMALS[chain] : 1)) .toString(); }); tx.outputs .filter((i) => i.address === address) .forEach((i) => { item.amount = new bignumber_js_1.BigNumber(item.amount) .plus(new bignumber_js_1.BigNumber(i.value).dividedBy(typeof i.value === 'number' ? 10 ** util_1.Constant.DECIMALS[chain] : 1)) .toString(); }); if (new bignumber_js_1.BigNumber(item.amount).isGreaterThan(0)) { item.transactionType = 'incoming'; result.push(item); } else { item.transactionType = 'outgoing'; item.amount = new bignumber_js_1.BigNumber(item.amount).multipliedBy(-1).toString(); result.push(item); } } return result; }); } async getNativeBalance(addresses) { const network = this.config.network; if ((0, dto_1.isEvmBasedNetwork)(network)) { const rpc = util_1.Utils.getRpc(this.id, this.config); const result = await Promise.all(addresses.map((a, i) => rpc.rawRpcCall(util_1.Utils.prepareRpcCall('eth_getBalance', [a, 'pending'], i)))); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return result.map((e) => new bignumber_js_1.BigNumber(e.result).dividedBy(10 ** util_1.Constant.DECIMALS[network]).toString()); } if ([tatum_1.Network.SOLANA, tatum_1.Network.SOLANA_DEVNET].includes(network)) { const rpc = util_1.Utils.getRpc(this.id, this.config); return rpc .rawBatchRpcCall(addresses.map((a, i) => util_1.Utils.prepareRpcCall('getBalance', [a, { commitment: 'processed' }], i))) .then((r) => { if (Array.isArray(r)) { return r.map((e) => new bignumber_js_1.BigNumber(e.result.value).dividedBy(10 ** util_1.Constant.DECIMALS[network]).toString()); } return [new bignumber_js_1.BigNumber(r.result.value).dividedBy(10 ** util_1.Constant.DECIMALS[network]).toString()]; }); } else if ([tatum_1.Network.XRP, tatum_1.Network.XRP_TESTNET].includes(network)) { if (addresses.length !== 1) { throw new Error(`UTXO based networks like ${network} support only one address per call.`); } const rpc = util_1.Utils.getRpc(this.id, this.config); return rpc .rawRpcCall(util_1.Utils.prepareRpcCall('account_info', [ { account: addresses[0], ledger_index: 'current', }, ])) .then((r) => [ new bignumber_js_1.BigNumber(r.result.account_data?.Balance || 0) .dividedBy(10 ** util_1.Constant.DECIMALS[network]) .toString(), ]); } else if ((0, dto_1.isDataApiUtxoEnabledNetwork)(network)) { if (addresses.length !== 1) { throw new Error(`UTXO based networks like ${network} support only one address per call.`); } return this.connector .get({ path: 'data/utxos', params: { chain: network, address: addresses[0], totalValue: 200000000000, }, }) .then((r) => [r.reduce((acc, val) => acc + val.value, 0).toString()]); } else if (network === tatum_1.Network.HORIZEN_EON) { const rpc = util_1.Utils.getRpc(this.id, this.config); const result = await Promise.all(addresses.map((a) => rpc.getBalance(a))); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return result.map((e) => new bignumber_js_1.BigNumber(e.result).dividedBy(10 ** util_1.Constant.DECIMALS[network]).toString()); } // TODO: implement for other networks - TRON, XLM etc etc throw new Error(`Unsupported network ${network} for now.`); } }; exports.Address = Address; exports.Address = Address = __decorate([ (0, typedi_1.Service)({ factory: (data) => { return new Address(data.id); }, transient: true, }), __metadata("design:paramtypes", [String]) ], Address); function formatNativeBalances(nativeBalances, addresses, chain) { const result = []; for (const [i, nativeBalance] of nativeBalances.entries()) { result.push({ address: addresses[i], asset: util_1.Constant.CURRENCY_NAMES[chain], decimals: util_1.Constant.DECIMALS[chain], balance: nativeBalance, type: 'native', }); } return result; } //# sourceMappingURL=address.js.map