Skip to content

Commit

Permalink
Merge branch 'master' into feature/ALL-2165-rename-com-to-io
Browse files Browse the repository at this point in the history
  • Loading branch information
Hathoriel authored Jul 19, 2023
2 parents 993e0ad + 41397f5 commit 436c511
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 103 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [1.5.11] - 2023.07.13
### Changed
- Fix rpc calls without api key & Added haqq archive/non-archive calls

## [1.5.10] - 2023.07.10
### Changed
- Selected Archive/Non-Archive node for Ethereum RPC calls based on method
Expand Down
2 changes: 2 additions & 0 deletions src/dto/Network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ export const LOAD_BALANCER_NETWORKS = [...UTXO_LOAD_BALANCER_NETWORKS, ...EVM_LO
export const EVM_ARCHIVE_NON_ARCHIVE_LOAD_BALANCER_NETWORKS = [
Network.ETHEREUM,
Network.ETHEREUM_SEPOLIA,
Network.HAQQ,
Network.HAQQ_TESTNET,
]

export const SOLANA_NETWORKS = [Network.SOLANA, Network.SOLANA_DEVNET]
Expand Down
242 changes: 148 additions & 94 deletions src/service/address/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import {
TokenDetails,
isDataApiEvmEnabledNetwork,
isDataApiUtxoEnabledNetwork,
isEvmBasedNetwork,
isEvmBasedNetwork, isTronNetwork
} from '../../dto'
import { CONFIG, Constant, ErrorUtils, ResponseDto, Utils } from '../../util'
import { EvmRpc, GenericRpc } from '../rpc'
import { EvmRpc, GenericRpc, TronRpc } from '../rpc'
import { Network, TatumConfig } from '../tatum'
import { AddressBalance, AddressTransaction, GetAddressTransactionsQuery } from './address.dto'
import { decodeUInt256 } from '../../util/decode'

@Service({
factory: (data: { id: string }) => {
Expand Down Expand Up @@ -40,22 +41,23 @@ export class Address {
}: AddressBalanceDetails): Promise<ResponseDto<AddressBalance[]>> {
const chain = this.config.network
return ErrorUtils.tryFail(async () => {
const [nativeBalances, tokenBalances] = await Promise.all([
this.getNativeBalance(addresses),
isDataApiEvmEnabledNetwork(chain) &&
this.connector
.get<{ result: AddressBalance[] }, ApiBalanceRequest>({
path: `data/balances`,
params: {
pageSize,
offset: page,
excludeMetadata: true,
chain,
addresses: addresses.join(','),
},
})
.then((r) => r.result),
])

const fullBalances = isTronNetwork(chain) ? await this.getFullBalance(addresses) : { nativeBalance: '0', tokenBalances: [] }
const nativeBalances = isTronNetwork(chain) ? [fullBalances.nativeBalance] : await this.getNativeBalance(addresses)
const tokenBalances = isTronNetwork(chain) ? fullBalances.tokenBalances : isDataApiEvmEnabledNetwork(chain) &&
await this.connector
.get<{ result: AddressBalance[] }, ApiBalanceRequest>({
path: `data/balances`,
params: {
pageSize,
offset: page,
excludeMetadata: true,
chain,
addresses: addresses.join(','),
},
})
.then((r) => r.result)

const result: AddressBalance[] = []
for (const [i, nativeBalance] of nativeBalances.entries()) {
result.push({
Expand All @@ -69,7 +71,8 @@ export class Address {
if (!tokenBalances) {
return result
}
return [...result, ...(await this.processTokenBalanceDetails(tokenBalances, chain))]
const serializedTokenBalances = isTronNetwork(chain) ? tokenBalances : await this.processTokenBalanceDetails(tokenBalances, chain)
return [...result, ...serializedTokenBalances]
})
}

Expand All @@ -86,40 +89,61 @@ export class Address {
page = 0,
}: GetAddressTransactionsQuery): Promise<ResponseDto<AddressTransaction[]>> {
const chain = this.config.network
let path
return ErrorUtils.tryFail(async () => {
if (isDataApiEvmEnabledNetwork(chain)) {
return this.connector
.get<{ result: AddressTransaction[] }>({
path: `data/transactions`,
params: {
chain,
addresses: address,
transactionTypes: transactionTypes?.join(),
transactionSubtype: transactionDirection,
blockFrom: fromBlock,
blockTo: toBlock,
pageSize,
offset: page,
},
})
.then((r) => r.result)
}
let path
if ([Network.BITCOIN, Network.BITCOIN_TESTNET].includes(chain)) {
path = `bitcoin/transaction/address/${address}`
} else if ([Network.LITECOIN, Network.LITECOIN_TESTNET].includes(chain)) {
path = `litecoin/transaction/address/${address}`
} else if ([Network.DOGECOIN, 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.`)
switch (true) {
case isDataApiEvmEnabledNetwork(chain):
return this.connector
.get<{ result: AddressTransaction[] }>({
path: `data/transactions`,
params: {
chain,
addresses: address,
transactionTypes: transactionTypes?.join(),
transactionSubtype: transactionDirection,
blockFrom: fromBlock,
blockTo: toBlock,
pageSize,
offset: page,
},
})
.then((r) => r.result)
case [Network.BITCOIN, Network.BITCOIN_TESTNET].includes(chain):
path = `bitcoin/transaction/address/${address}`
break
case [Network.LITECOIN, Network.LITECOIN_TESTNET].includes(chain):
path = `litecoin/transaction/address/${address}`
break
case [Network.DOGECOIN, Network.DOGECOIN_TESTNET].includes(chain):
path = `dogecoin/transaction/address/${address}`
break
default:
throw new Error(`Not supported for ${chain} network.`)
}
return this.processUtxoBasedTxs(path, pageSize, page, transactionDirection, chain, address)
})
}

private async processTRC20TokenBalanceDetails(tokenBalances: {[key: string]: string}) {
const balances = Object.entries(tokenBalances[0])
const serializedTokenBalance: Array<unknown> = []
for (let i = 0; i < balances.length; i++) {
const asset = await Utils.getRpc<TronRpc>(this.id, this.config).triggerConstantContract(
balances[i][0], balances[i][0], 'symbol()', '', { visible: true }
).then(r => decodeUInt256(r.constant_result[0]))
const decimals = await Utils.getRpc<TronRpc>(this.id, this.config).triggerConstantContract(
balances[i][0], balances[i][0], 'decimals()', '', { visible: true }
).then(r => decodeUInt256(r.constant_result[0]))
const balance = balances[i][1]
serializedTokenBalance.push({
asset,
decimals,
balance
})
}
return serializedTokenBalance
}

private async processTokenBalanceDetails(tokenBalances: AddressBalance[], chain: Network) {
const result: AddressBalance[] = []
// Processing token details
Expand Down Expand Up @@ -173,8 +197,8 @@ export class Address {
blockNumber: number
time: number
hash: string
inputs: Array<{ coin: { address: string; value: number | string } }>
outputs: Array<{ address: string; value: string | number }>
inputs: Array<{ coin: { address: string, value: number | string } }>
outputs: Array<{ address: string, value: string | number }>
}>
>({
path,
Expand Down Expand Up @@ -232,61 +256,91 @@ export class Address {
})
}

private async getNativeBalance(addresses: string[]): Promise<string[]> {
private async getFullBalance(addresses: string[]): Promise<{nativeBalance: string, tokenBalances: []}> {
const network = this.config.network
if (isEvmBasedNetwork(network)) {
const rpc = Utils.getRpc<EvmRpc>(this.id, network)
const result = await Promise.all(
addresses.map((a, i) => rpc.rawRpcCall(Utils.prepareRpcCall('eth_getBalance', [a, 'pending'], i))),
)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return result.map((e) => new BigNumber(e.result).dividedBy(10 ** Constant.DECIMALS[network]).toString())
switch (true) {
case [Network.TRON, Network.TRON_SHASTA].includes(network):
if (addresses.length !== 1) {
throw new Error(`UTXO based networks like ${network} support only one address per call.`)
}
return this.connector
.get<{
balance: number,
createTime: number
trc10: [{
key: string,
value: number,
}]
trc20: {[key: string]: string}
freeNetLimit: number,
bandwidth: number,
}>({
path: `tron/account/${addresses[0]}`,
})
.then(async (r) =>
{
return Object.create({
nativeBalance: r.balance.toString(),
tokenBalances: await this.processTRC20TokenBalanceDetails(r.trc20),
})
})
}
if ([Network.SOLANA, Network.SOLANA_DEVNET].includes(network)) {
const rpc = Utils.getRpc<GenericRpc>(this.id, network)
return rpc
.rawBatchRpcCall(
addresses.map((a, i) => Utils.prepareRpcCall('getBalance', [a, { commitment: 'processed' }], i)),
)
.then((r) =>
r.map((e) => new BigNumber(e.result.value).dividedBy(10 ** Constant.DECIMALS[network]).toString()),
)
} else if ([Network.XRP, 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 = Utils.getRpc<GenericRpc>(this.id, network)
return rpc
.rawRpcCall(
throw new Error(`Unsupported network ${network} for now.`)
}

private async getNativeBalance(addresses: string[]): Promise<string[]> {
const network = this.config.network
switch (true) {
case isEvmBasedNetwork(network):
return Promise.all(
addresses.map((a, i) => Utils.getRpc<EvmRpc>(this.id, this.config).rawRpcCall(Utils.prepareRpcCall('eth_getBalance', [a, 'pending'], i))),
).then(r => r.map((e) =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
new BigNumber(e.result).dividedBy(10 ** Constant.DECIMALS[network]).toString()))
case [Network.SOLANA, Network.SOLANA_DEVNET].includes(network):
return Utils.getRpc<GenericRpc>(this.id, this.config)
.rawBatchRpcCall(
addresses.map((a, i) => Utils.prepareRpcCall('getBalance', [a, { commitment: 'processed' }], i)),
)
.then((r) =>
r.map((e) => new BigNumber(e.result.value).dividedBy(10 ** Constant.DECIMALS[network]).toString()),
)
case [Network.XRP, Network.XRP_TESTNET].includes(network):
if (addresses.length !== 1) {
throw new Error(`UTXO based networks like ${network} support only one address per call.`)
}
return Utils.getRpc<GenericRpc>(this.id, this.config).rawRpcCall(
Utils.prepareRpcCall('account_info', [
{
account: addresses[0],
ledger_index: 'current',
},
]),
)
.then((r) => [
new BigNumber(r.result.account_data?.Balance || 0)
.dividedBy(10 ** Constant.DECIMALS[network])
.toString(),
])
} else if (isDataApiUtxoEnabledNetwork(network)) {
if (addresses.length !== 1) {
throw new Error(`UTXO based networks like ${network} support only one address per call.`)
}
return this.connector
.get<Array<{ value: number }>>({
path: 'data/utxos',
params: {
chain: network,
address: addresses[0],
totalValue: 200000000000,
},
})
.then((r) => [r.reduce((acc, val) => acc + val.value, 0).toString()])
.then((r) => [
new BigNumber(r.result.account_data?.Balance || 0)
.dividedBy(10 ** Constant.DECIMALS[network])
.toString(),
])
case isDataApiUtxoEnabledNetwork(network):
if (addresses.length !== 1) {
throw new Error(`UTXO based networks like ${network} support only one address per call.`)
}
return this.connector
.get<Array<{ value: number }>>({
path: 'data/utxos',
params: {
chain: network,
address: addresses[0],
totalValue: 200000000000,
},
})
.then((r) => [r.reduce((acc, val) => acc + val.value, 0).toString()])
case [Network.TRON, Network.TRON_SHASTA].includes(network):
throw new Error(`Use 'getFullBalance' method for network ${network}.`)
}
// TODO: implement for other networks - TRON, XLM etc etc
// TODO: implement for other networks - XLM etc etc
throw new Error(`Unsupported network ${network} for now.`)
}
}
2 changes: 2 additions & 0 deletions src/service/rpc/evm/EvmArchiveLoadBalancerRpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export class EvmArchiveLoadBalancerRpc extends AbstractEvmRpc implements EvmBase
}

private isArchiveMethod(rpc: JsonRpcCall): boolean {


const isArchiveMethod = ARCHIVE_METHODS.includes(rpc.method)
if (isArchiveMethod) {
return true
Expand Down
10 changes: 5 additions & 5 deletions src/service/tatum/tatum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export abstract class BaseUtxoClass extends BaseTatumSdk {

constructor(id: string) {
super(id)
this.rpc = Utils.getRpc<UtxoBasedRpcSuite>(id, Container.of(id).get(CONFIG).network)
this.rpc = Utils.getRpc<UtxoBasedRpcSuite>(id, Container.of(id).get(CONFIG))
this.fee = Container.of(id).get(FeeUtxo)
}
}
Expand All @@ -46,7 +46,7 @@ export abstract class BaseEvmClass extends BaseTatumSdk {

constructor(id: string) {
super(id)
this.rpc = Utils.getRpc<EvmBasedRpcSuite>(id, Container.of(id).get(CONFIG).network)
this.rpc = Utils.getRpc<EvmBasedRpcSuite>(id, Container.of(id).get(CONFIG))
}
}

Expand Down Expand Up @@ -91,21 +91,21 @@ export class Xrp extends BaseTatumSdk {
rpc: XrpRpcSuite
constructor(id: string) {
super(id)
this.rpc = Utils.getRpc<XrpRpcSuite>(id, Container.of(id).get(CONFIG).network)
this.rpc = Utils.getRpc<XrpRpcSuite>(id, Container.of(id).get(CONFIG))
}
}
export class Solana extends BaseTatumSdk {
rpc: SolanaRpcSuite
constructor(id: string) {
super(id)
this.rpc = Utils.getRpc<SolanaRpcSuite>(id, Container.of(id).get(CONFIG).network)
this.rpc = Utils.getRpc<SolanaRpcSuite>(id, Container.of(id).get(CONFIG))
}
}
export class Tron extends BaseTatumSdk {
rpc: TronRpcSuite
constructor(id: string) {
super(id)
this.rpc = Utils.getRpc<TronRpcSuite>(id, Container.of(id).get(CONFIG).network)
this.rpc = Utils.getRpc<TronRpcSuite>(id, Container.of(id).get(CONFIG))
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/service/walletProvider/metaMask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class MetaMask<T extends EvmRpc> {

constructor(private readonly id: string) {
this.config = Container.of(this.id).get(CONFIG)
this.rpc = Utils.getRpc<T>(this.id, this.config.network)
this.rpc = Utils.getRpc<T>(this.id, this.config)
this.connector = Container.of(this.id).get(TatumConnector)
}

Expand Down
Loading

0 comments on commit 436c511

Please sign in to comment.