@tatumio/tatum
Version:
Tatum JS SDK
426 lines • 18 kB
JavaScript
;
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