Skip to content

Commit

Permalink
chore(nodes): move methods to indexers and add TS types
Browse files Browse the repository at this point in the history
  • Loading branch information
bludnic committed Aug 1, 2024
1 parent 8a13693 commit f9614c0
Show file tree
Hide file tree
Showing 41 changed files with 525 additions and 231 deletions.
4 changes: 2 additions & 2 deletions src/hooks/queries/transaction/useBtcTransactionQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MaybeRef, unref } from 'vue'
import { useStore } from 'vuex'
import { useQuery } from '@tanstack/vue-query'
import { Cryptos } from '@/lib/constants'
import { btc } from '@/lib/nodes'
import { btcIndexer } from '@/lib/nodes'
import { BtcTransaction } from '@/lib/nodes/types/transaction'
import { PendingTransaction, PendingTxStore } from '@/lib/pending-transactions'
import { refetchIntervalFactory, refetchOnMountFn, retryDelayFactory, retryFactory } from './utils'
Expand All @@ -20,7 +20,7 @@ export function useBtcTransactionQuery(

return useQuery<BtcTransaction | PendingTransaction>({
queryKey: ['transaction', Cryptos.BTC, transactionId],
queryFn: () => btc.getTransaction(unref(transactionId), store.state.btc.address),
queryFn: () => btcIndexer.getTransaction(unref(transactionId), store.state.btc.address),
initialData: () => {
const pendingTransaction = PendingTxStore.get(Cryptos.BTC)
if (pendingTransaction?.id === unref(transactionId)) {
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/queries/transaction/useDogeTransactionQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MaybeRef, unref } from 'vue'
import { useStore } from 'vuex'
import { useQuery } from '@tanstack/vue-query'
import { Cryptos } from '@/lib/constants'
import { doge } from '@/lib/nodes'
import { dogeIndexer } from '@/lib/nodes'
import { DogeTransaction } from '@/lib/nodes/types/transaction'
import { PendingTransaction, PendingTxStore } from '@/lib/pending-transactions'
import { refetchIntervalFactory, refetchOnMountFn, retryDelayFactory, retryFactory } from './utils'
Expand All @@ -20,7 +20,7 @@ export function useDogeTransactionQuery(

return useQuery<DogeTransaction | PendingTransaction>({
queryKey: ['transaction', Cryptos.DOGE, transactionId],
queryFn: () => doge.getTransaction(unref(transactionId), store.state.doge.address),
queryFn: () => dogeIndexer.getTransaction(unref(transactionId), store.state.doge.address),
initialData: () => {
const pendingTransaction = PendingTxStore.get(Cryptos.DOGE)
if (pendingTransaction?.id === unref(transactionId)) {
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/queries/useBlockHeight.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { computed, MaybeRef, unref } from 'vue'
import { useQuery } from '@tanstack/vue-query'
import { getTxFetchInfo } from '@/lib/transactionsFetching'
import { adm, btc, dash, doge, eth, kly } from '@/lib/nodes'
import { adm, btcIndexer, dash, dogeIndexer, eth, kly } from '@/lib/nodes'

type Chain = 'ADM' | 'BTC' | 'DOGE' | 'DASH' | 'ETH' | 'KLY'

Expand All @@ -10,9 +10,9 @@ const fetchBlockHeight = async (chain: Chain): Promise<number> => {
case 'ADM':
return adm.getHeight()
case 'BTC':
return btc.getHeight()
return btcIndexer.getHeight()
case 'DOGE':
return doge.getHeight()
return dogeIndexer.getHeight()
case 'DASH':
return dash.getHeight()
case 'ETH':
Expand Down
10 changes: 7 additions & 3 deletions src/lib/bitcoin/bitcoin-api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import BtcBaseApi from './btc-base-api'
import { Cryptos } from '../constants'
import { btc } from '@/lib/nodes/btc'
import { btcIndexer } from '../../lib/nodes'

export default class BitcoinApi extends BtcBaseApi {
constructor(passphrase) {
Expand Down Expand Up @@ -28,7 +28,9 @@ export default class BitcoinApi extends BtcBaseApi {

/** @override */
sendTransaction(txHex) {
return btc.useClient((client) => client.post('/tx', txHex)).then((response) => response.data)
return btcIndexer
.useClient((client) => client.post('/tx', txHex))
.then((response) => response.data)
}

/** @override */
Expand Down Expand Up @@ -84,6 +86,8 @@ export default class BitcoinApi extends BtcBaseApi {

/** Executes a GET request to the API */
_get(url, params) {
return btc.useClient((client) => client.get(url, { params })).then((response) => response.data)
return btcIndexer
.useClient((client) => client.get(url, { params }))
.then((response) => response.data)
}
}
8 changes: 5 additions & 3 deletions src/lib/bitcoin/doge-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { isPositiveNumber } from '../numericHelpers'
import { ECPairFactory } from 'ecpair'
import * as tinysecp from 'tiny-secp256k1'
import { convertToSmallestUnit } from './bitcoin-utils'
import { doge } from '@/lib/nodes/doge'
import { dogeIndexer } from '../../lib/nodes'

const ECPairAPI = ECPairFactory(tinysecp)

Expand Down Expand Up @@ -150,12 +150,14 @@ export default class DogeApi extends BtcBaseApi {

/** Executes a GET request to the DOGE API */
_get(url, params) {
return doge.useClient((client) => client.get(url, { params })).then((response) => response.data)
return dogeIndexer
.useClient((client) => client.get(url, { params }))
.then((response) => response.data)
}

/** Executes a POST request to the DOGE API */
_post(url, data) {
return doge
return dogeIndexer
.useClient((client) => client.post(url, qs.stringify(data), POST_CONFIG))
.then((response) => response.data)
}
Expand Down
23 changes: 22 additions & 1 deletion src/lib/nodes/btc-indexer/BtcIndexer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createBtcLikeClient } from '../utils/createBtcLikeClient'
import type { AxiosInstance } from 'axios'
import { AxiosInstance, AxiosRequestConfig } from 'axios'
import { Node } from '@/lib/nodes/abstract.node'
import { NODE_LABELS } from '@/lib/nodes/constants'
import { formatBtcVersion } from '@/lib/nodes/utils/nodeVersionFormatters'
Expand Down Expand Up @@ -36,6 +36,7 @@ export class BtcIndexer extends Node<AxiosInstance> {
}
}

// @todo fetch version from the indexer, not node
protected async fetchNodeVersion(): Promise<void> {
const { data } = await this.client.post<FetchBtcNodeInfoResult>('/bitcoind', {
jsonrpc: '1.0',
Expand All @@ -48,4 +49,24 @@ export class BtcIndexer extends Node<AxiosInstance> {
this.version = formatBtcVersion(version)
}
}

/**
* Performs a request to the Bitcoin indexer.
*/
async request<Response = any, Params = any>(
method: 'GET' | 'POST',
path: string,
params?: Params,
requestConfig?: AxiosRequestConfig
): Promise<Response> {
return this.client
.request({
...requestConfig,
url: path,
method,
params: method === 'GET' ? params : undefined,
data: method === 'POST' ? params : undefined
})
.then((res) => res.data)
}
}
80 changes: 80 additions & 0 deletions src/lib/nodes/btc-indexer/BtcIndexerClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import type { AxiosRequestConfig } from 'axios'
import { Client } from '../abstract.client'
import { BtcIndexer } from './BtcIndexer'
import { MULTIPLIER, normalizeTransaction } from './utils'
import { Transaction } from './types/api/common/transaction'
import { UTXO } from './types/api/common/unspent'
import { GetAddressParams } from './types/api/get-address/get-address-params'
import { GetAddressResult } from './types/api/get-address/get-address-result'
import { GetUnspentsParams } from './types/api/get-unspents/get-unspents-params'

/**
* Provides methods for calling the ADAMANT API.
Expand All @@ -16,4 +23,77 @@ export class BtcIndexerClient extends Client<BtcIndexer> {

void this.watchNodeStatusChange()
}

/**
* Performs a request to the BTC indexer.
*/
async request<Response = any, Params = any>(
method: 'GET' | 'POST',
path: string,
params?: Params,
requestConfig?: AxiosRequestConfig
): Promise<Response> {
return this.getNode().request<Response, Params>(method, path, params, requestConfig)
}

/**
* Return transaction details normalized.
*
* @param transactionId Transaction ID
* @param address Owner BTC address
*/
async getTransaction(transactionId: string, address: string) {
const transaction = await this.request<Transaction>('GET', `/tx/${transactionId}`)

const height = await this.getHeight().catch((err) => {
console.warn('BtcClient: Failed to get current height:', err)
return undefined
})

return normalizeTransaction(transaction, address, height)
}

/**
* Query transactions history
*
* @param address BTC address
* @param toTx Until transaction ID. For pagination.
*/
async getTransactions(address: string, toTx?: string) {
const endpoint = toTx ? `/address/${address}/txs/chain/${toTx}` : `/address/${address}/txs`

const transactions = await this.request<Transaction[]>('GET', endpoint)

return transactions.map((transaction) => normalizeTransaction(transaction, address))
}

/**
* Get unspent transaction outputs (UTXOs) for the specified address.
* @param address BTC address
*/
async getUnspents(address: string) {
return this.request<UTXO[], GetUnspentsParams>('GET', `/address/${address}/utxo`)
}

async getFeeRate() {
return this.request<Record<string, number>>('GET', '/fee-estimates')
}

async getHeight() {
const height = await this.request<string>('GET', '/blocks/tip/height')

return Number(height)
}

async getAddress(address: string) {
return this.request<GetAddressResult, GetAddressParams>('GET', `/address/${address}`)
}

async getBalance(address: string) {
const { chain_stats } = await this.getAddress(address)

const balance = (chain_stats.funded_txo_sum - chain_stats.spent_txo_sum) / MULTIPLIER

return balance
}
}
File renamed without changes.
File renamed without changes.
79 changes: 7 additions & 72 deletions src/lib/nodes/btc/BtcClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import type { AxiosRequestConfig } from 'axios'
import { BtcNode } from './BtcNode'
import { Client } from '../abstract.client'
import { normalizeTransaction, MULTIPLIER } from './utils'
import { Transaction } from './types/api/common/transaction'
import { UTXO } from './types/api/common/unspent'
import { GetUnspentsParams } from './types/api/get-unspents/get-unspents-params'
import { GetAddressParams } from './types/api/get-address/get-address-params'
import { GetAddressResult } from './types/api/get-address/get-address-result'
import { RpcRequest } from './types/api/common'

/**
* Provides methods for calling the ADAMANT API.
Expand All @@ -24,73 +19,13 @@ export class BtcClient extends Client<BtcNode> {
void this.watchNodeStatusChange()
}

async request<Response = any, Params = any>(
method: 'GET' | 'POST',
path: string,
params?: Params,
requestConfig?: AxiosRequestConfig
): Promise<Response> {
return this.getNode().request(method, path, params, requestConfig)
}

/**
* Return transaction details normalized.
*
* @param transactionId Transaction ID
* @param address Owner BTC address
*/
async getTransaction(transactionId: string, address: string) {
const transaction = await this.request<Transaction>('GET', `/tx/${transactionId}`)

const height = await this.getHeight().catch((err) => {
console.warn('BtcClient: Failed to get current height:', err)
return undefined
})

return normalizeTransaction(transaction, address, height)
}

/**
* Query transactions history
*
* @param address BTC address
* @param toTx Until transaction ID. For pagination.
* Performs an RPC request to the Dash node.
*/
async getTransactions(address: string, toTx?: string) {
const endpoint = toTx ? `/address/${address}/txs/chain/${toTx}` : `/address/${address}/txs`

const transactions = await this.request<Transaction[]>('GET', endpoint)

return transactions.map((transaction) => normalizeTransaction(transaction, address))
}

/**
* Get unspent transaction outputs (UTXOs) for the specified address.
* @param address BTC address
*/
async getUnspents(address: string) {
return this.request<UTXO[], GetUnspentsParams>('GET', `/address/${address}/utxo`)
}

async getFeeRate() {
return this.request<Record<string, number>>('GET', '/fee-estimates')
}

async getHeight() {
const height = await this.request<string>('GET', '/blocks/tip/height')

return Number(height)
}

async getAddress(address: string) {
return this.request<GetAddressResult, GetAddressParams>('GET', `/address/${address}`)
}

async getBalance(address: string) {
const { chain_stats } = await this.getAddress(address)

const balance = (chain_stats.funded_txo_sum - chain_stats.spent_txo_sum) / MULTIPLIER

return balance
async invoke<Result = any, Request extends RpcRequest = RpcRequest>(
params: Request,
requestConfig?: AxiosRequestConfig
) {
return this.getNode().invoke<Result, Request>(params, requestConfig)
}
}
Loading

0 comments on commit f9614c0

Please sign in to comment.