diff --git a/src/dto/Currency.ts b/src/dto/Currency.ts index 1cc21419e..c437d1c0d 100644 --- a/src/dto/Currency.ts +++ b/src/dto/Currency.ts @@ -52,6 +52,7 @@ export enum Currency { ATOM = 'ATOM', IOTA = 'IOTA', CSPR = 'CSPR', + TON = 'TON' } export function networkToCurrency(network: Network): Currency { diff --git a/src/dto/Network.ts b/src/dto/Network.ts index 6b2499d4d..0724f4d6e 100644 --- a/src/dto/Network.ts +++ b/src/dto/Network.ts @@ -56,6 +56,7 @@ export enum Network { ZILLIQA = 'zilliqa-mainnet', BITCOIN_ELECTRS = 'bitcoin-mainnet-electrs', CASPER = 'casper-mainnet', + TON = 'ton-mainnet', // Testnets @@ -110,6 +111,7 @@ export enum Network { IOTA_TESTNET = 'iota-testnet', BITCOIN_ELECTRS_TESTNET = 'bitcoin-testnet-electrs', ROSTRUM_TESTNET = 'bch-testnet-rostrum', + TON_TESTNET = 'ton-testnet' } export const EVM_BASED_NETWORKS = [ @@ -267,6 +269,7 @@ export const IOTA_LOAD_BALANCER_NETWORKS = [Network.IOTA] export const BITCOIN_ELECTRS_NETWORKS = [Network.BITCOIN_ELECTRS, Network.BITCOIN_ELECTRS_TESTNET] export const IOTA_NETWORKS = [Network.IOTA, Network.IOTA_TESTNET] export const CASPER_NETWORKS = [Network.CASPER] +export const TON_NETWORKS = [Network.TON, Network.TON_TESTNET] export const LOAD_BALANCER_NETWORKS = [ ...UTXO_LOAD_BALANCER_NETWORKS, @@ -388,6 +391,8 @@ export const isElectrsNetwork = (network: Network) => BITCOIN_ELECTRS_NETWORKS.i export const isCasperNetwork = (network: Network) => CASPER_NETWORKS.includes(network) +export const isTonNetwork = (network: Network) => TON_NETWORKS.includes(network) + export const isSameGetBlockNetwork = (network: Network) => isUtxoBasedNetwork(network) || isEvmBasedNetwork(network) || @@ -960,5 +965,13 @@ export const NETWORK_METADATA: Record = { [Network.CASPER]: { currency: Currency.CSPR, testnet: false, + }, + [Network.TON]: { + currency: Currency.TON, + testnet: false, + }, + [Network.TON_TESTNET]: { + currency: Currency.TON, + testnet: true } } diff --git a/src/dto/rpc/TonRpcSuite.ts b/src/dto/rpc/TonRpcSuite.ts new file mode 100644 index 000000000..44a215681 --- /dev/null +++ b/src/dto/rpc/TonRpcSuite.ts @@ -0,0 +1,8 @@ +export interface GetStatus { + rest_online: boolean + indexing_latency: number +} + +export interface TonRpcSuite { + getStatus(): Promise +} diff --git a/src/service/rpc/other/AbstractTonRpc.ts b/src/service/rpc/other/AbstractTonRpc.ts new file mode 100644 index 000000000..ef5c9393e --- /dev/null +++ b/src/service/rpc/other/AbstractTonRpc.ts @@ -0,0 +1,19 @@ +import { PostI } from '../../../dto/PostI' +import { GetI } from '../../../dto/GetI' +import { QueryParams } from '../../../dto' +import { Utils } from '../../../util' +import { GetStatus, TonRpcSuite } from '../../../dto/rpc/TonRpcSuite' + +export abstract class AbstractTonRpc implements TonRpcSuite { + protected abstract post(post: PostI): Promise + + protected abstract get(get: GetI): Promise + + private async sendGet({ path, queryParams }: { path: string; queryParams?: QueryParams }): Promise { + return this.get({ path: Utils.addQueryParams({ basePath: path, queryParams: queryParams }) }) + } + + getStatus(): Promise { + return this.sendGet({ path: '/v2/status' }) + } +} diff --git a/src/service/rpc/other/TonRpc.ts b/src/service/rpc/other/TonRpc.ts new file mode 100644 index 000000000..3e3709a93 --- /dev/null +++ b/src/service/rpc/other/TonRpc.ts @@ -0,0 +1,40 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { GetI } from 'src/dto/GetI' +import { Container, Service } from 'typedi' +import { TatumConnector } from '../../../connector/tatum.connector' +import { PostI } from '../../../dto/PostI' +import { CONFIG, Utils } from '../../../util' +import { TatumConfig } from '../../tatum' +import { AbstractTonRpc } from './AbstractTonRpc' +import { TonRpcSuite } from '../../../dto/rpc/TonRpcSuite' + +@Service({ + factory: (data: { id: string }) => { + return new TonRpc(data.id) + }, + transient: true, +}) +export class TonRpc extends AbstractTonRpc implements TonRpcSuite { + protected readonly connector: TatumConnector + protected readonly config: TatumConfig + + constructor(id: string) { + super() + this.connector = Container.of(id).get(TatumConnector) + this.config = Container.of(id).get(CONFIG) + } + + public destroy() { + // Do nothing + } + + protected post(post: PostI): Promise { + const basePath = Utils.getV3RpcUrl(this.config) + return this.connector.post({ ...post, basePath }) + } + + protected get(get: GetI): Promise { + const basePath = Utils.getV3RpcUrl(this.config) + return this.connector.get({ ...get, basePath }) + } +} diff --git a/src/service/tatum/tatum.other.ts b/src/service/tatum/tatum.other.ts index 74fc2e3a6..4ad1e1420 100644 --- a/src/service/tatum/tatum.other.ts +++ b/src/service/tatum/tatum.other.ts @@ -19,6 +19,7 @@ import { Token } from '../token' import { TatumSdkChain } from './tatum' import { CosmosRpcSuite } from '../../dto/rpc/CosmosRpcSuite' import { CasperRpcSuite } from '../../dto/rpc/CasperRpcSuite' +import { TonRpcSuite } from '../../dto/rpc/TonRpcSuite' export abstract class BaseOther extends TatumSdkChain { ipfs: Ipfs @@ -151,6 +152,15 @@ export class Casper extends BaseOther { } } +export class Ton extends BaseOther { + rpc: TonRpcSuite + + constructor(id: string) { + super(id) + this.rpc = Utils.getRpc(id, Container.of(id).get(CONFIG)) + } +} + export class AlgorandAlgod extends BaseOther { rpc: AlgorandAlgodRpcSuite diff --git a/src/util/util.shared.ts b/src/util/util.shared.ts index b67467b78..cf4f00b00 100644 --- a/src/util/util.shared.ts +++ b/src/util/util.shared.ts @@ -7,7 +7,9 @@ import { isAlgorandAlgodNetwork, isAlgorandIndexerNetwork, isBnbLoadBalancerNetwork, - isCardanoNetwork, isCasperNetwork, isCosmosNetwork, + isCardanoNetwork, + isCasperNetwork, + isCosmosNetwork, isDogecoinLoadBalancedNetwork, isEosLoadBalancerNetwork, isEosNetwork, @@ -15,7 +17,8 @@ import { isEvmArchiveNonArchiveLoadBalancerNetwork, isEvmBasedNetwork, isEvmLoadBalancerNetwork, - isIotaLoadBalancerNetwork, isIotaNetwork, + isIotaLoadBalancerNetwork, + isIotaNetwork, isKadenaLoadBalancerNetwork, isNativeEvmLoadBalancerNetwork, isRostrumLoadBalancerNetwork, @@ -24,6 +27,7 @@ import { isStellarLoadBalancerNetwork, isStellarNetwork, isTezosNetwork, + isTonNetwork, isTronLoadBalancerNetwork, isTronNetwork, isUtxoBasedNetwork, @@ -50,11 +54,14 @@ import { Base, BinanceSmartChain, Bitcoin, - BitcoinCash, BitcoinElectrs, + BitcoinCash, + BitcoinElectrs, Bnb, - CardanoRosetta, Casper, + CardanoRosetta, + Casper, Celo, - Chiliz, CosmosRosetta, + Chiliz, + CosmosRosetta, Cronos, Dogecoin, Eos, @@ -83,7 +90,7 @@ import { Solana, Stellar, TatumConfig, - Tezos, + Tezos, Ton, Tron, UtxoRpc, Vechain, @@ -119,11 +126,16 @@ import { CONFIG, LOGGER } from './di.tokens' import { IotaRpc } from '../service/rpc/other/IotaRpc' import { CosmosLoadBalancerRpc } from '../service/rpc/other/CosmosLoadBalancerRpc' import { CasperLoadBalancerRpc } from '../service/rpc/other/CasperLoadBalancerRpc' +import { TonRpc } from '../service/rpc/other/TonRpc' export const Utils = { getRpc: (id: string, config: TatumConfig): T => { const { network } = config + if(isTonNetwork(network)) { + return Container.of(id).get(TonRpc) as T + } + if(isCasperNetwork(network)) { return Container.of(id).get(CasperLoadBalancerRpc) as T } @@ -341,7 +353,8 @@ export const Utils = { isAlgorandIndexerNetwork(network) || isStellarLoadBalancerNetwork(network) || isKadenaLoadBalancerNetwork(network) || - isIotaLoadBalancerNetwork(network) + isIotaLoadBalancerNetwork(network) || + isTonNetwork(network) ) { return null } @@ -349,6 +362,10 @@ export const Utils = { throw new Error(`Network ${network} is not supported.`) }, getStatusUrl(network: Network, url: string): string { + if (isTonNetwork(network)) { + return `${url}v2/liteserver/get_masterchain_info` + } + if (isIotaLoadBalancerNetwork(network)) { return `${url}api/core/v2/info` } @@ -410,13 +427,18 @@ export const Utils = { isAlgorandIndexerNetwork(network) || isStellarLoadBalancerNetwork(network) || isKadenaLoadBalancerNetwork(network) || - isIotaLoadBalancerNetwork(network) + isIotaLoadBalancerNetwork(network) || + isTonNetwork(network) ) { return 'GET' } return 'POST' }, parseStatusPayload: (network: Network, response: JsonRpcResponse | any) => { + if (isTonNetwork(network)) { + return new BigNumber((response.last.seqno as number) || -1).toNumber() + } + if (isCasperNetwork(network)) { return new BigNumber((response.result.last_added_block_info.height as number) || -1).toNumber() } @@ -472,6 +494,10 @@ export const Utils = { throw new Error(`Network ${network} is not supported.`) }, isResponseOk: (network: Network, response: JsonRpcResponse | any) => { + if (isTonNetwork(network)) { + return response.last.seqno !== undefined + } + if (isCasperNetwork(network)) { return response.result.last_added_block_info.height !== undefined } @@ -886,6 +912,9 @@ export const Utils = { return new BitcoinElectrs(id) as T case Network.CASPER: return new Casper(id) as T + case Network.TON: + case Network.TON_TESTNET: + return new Ton(id) as T default: return new FullSdk(id) as T }