Skip to content

Commit

Permalink
feat: add Noves API (#2862)
Browse files Browse the repository at this point in the history
* feat: creates Noves API class

* feat: adds noves pagination (#2863)

---------

Co-authored-by: Nicole O'Brien <[email protected]>
  • Loading branch information
jeeanribeiro and nicole-obrien authored Oct 7, 2024
1 parent db9a3e2 commit 82278ad
Show file tree
Hide file tree
Showing 38 changed files with 603 additions and 52 deletions.
1 change: 1 addition & 0 deletions packages/desktop/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ const rendererPlugins = [
'process.env.APP_PROTOCOL': JSON.stringify(appProtocol),
'process.env.WALLETCONNECT_PROJECT_ID': JSON.stringify(process.env.WALLETCONNECT_PROJECT_ID),
'process.env.TRANSAK_API_KEY': JSON.stringify(process.env.TRANSAK_API_KEY),
'process.env.NOVES_API_KEY': JSON.stringify(process.env.NOVES_API_KEY),
}),
// The ethereumjs libraries require the NormalModuleReplacementPlugin & the ProvidePlugin
new NormalModuleReplacementPlugin(/node:/, (resource) => {
Expand Down
35 changes: 18 additions & 17 deletions packages/shared/src/lib/auxiliary/blockscout/api/blockscout.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,36 +43,37 @@ export class BlockscoutApi extends BaseApi implements IBlockscoutApi {
if (exitFunction && exitFunction(items)) {
return Promise.resolve(items)
}
return this.get<IPaginationResponse<T>>(path, { ...queryParameters, ...nextPageParameters }).then(
(response) => {
if (!response?.items) {
return Promise.resolve(items)
}
return this.makePaginatedGetRequest(
path,
queryParameters,
items.concat(response.items),
response.next_page_params,
exitFunction
)
return this.get<IPaginationResponse<T>>({
path,
queryParameters: { ...queryParameters, ...nextPageParameters },
}).then((response) => {
if (!response?.items) {
return Promise.resolve(items)
}
)
return this.makePaginatedGetRequest(
path,
queryParameters,
items.concat(response.items),
response.next_page_params,
exitFunction
)
})
}

async getBackendVersion(): Promise<string | undefined> {
const response = await this.get<{ backend_version }>('config/backend-version')
const response = await this.get<{ backend_version }>({ path: 'config/backend-version' })
return response?.['backend_version']
}

async getStats(): Promise<IBlockscoutStats | undefined> {
const response = await this.get<IBlockscoutStats>('stats')
const response = await this.get<IBlockscoutStats>({ path: 'stats' })
if (response) {
return this.get('stats')
return this.get({ path: 'stats' })
}
}

async getAssetMetadata(assetAddress: string): Promise<IBlockscoutTokenInfo | undefined> {
const response = await this.get<IBlockscoutTokenInfoDto>(`tokens/${assetAddress}`)
const response = await this.get<IBlockscoutTokenInfoDto>({ path: `tokens/${assetAddress}` })
if (response) {
return {
...response,
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/lib/auxiliary/country/api/country.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class IpApi extends BaseApi {
}
| undefined
> {
const ipData = await this.get<IIpApiResponse>('json')
const ipData = await this.get<IIpApiResponse>({ path: 'json' })
if (ipData === undefined) {
return undefined
}
Expand All @@ -56,7 +56,7 @@ export class IpApi extends BaseApi {
}

async getCountryCode(): Promise<string | undefined> {
const ipData = await this.get<IIpApiResponse>('json')
const ipData = await this.get<IIpApiResponse>({ path: 'json' })
return ipData?.country_code
}
}
1 change: 1 addition & 0 deletions packages/shared/src/lib/auxiliary/noves/apis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './noves.api'
27 changes: 27 additions & 0 deletions packages/shared/src/lib/auxiliary/noves/apis/noves-base.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { BaseApi, IRequestParams } from '@core/utils'
import { SupportedChain } from '../interfaces'

export class NovesBaseApi extends BaseApi {
protected get<T>(params: Omit<IRequestParams, 'body'>): Promise<T | undefined> {
return this.makeRequest<T>({
...params,
headers: {
apiKey: process.env.NOVES_API_KEY ?? 'demokey',
},
})
}

protected post<T>(params: IRequestParams): Promise<T | undefined> {
return this.makeRequest<T>({
...params,
headers: {
apiKey: process.env.NOVES_API_KEY ?? 'demokey',
},
})
}

async getSupportedEvmChains(): Promise<SupportedChain[]> {
const response = await this.get<SupportedChain[]>({ path: 'evm/chains' })
return response ?? []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { NovesForesightDescribeResponse, NovesTransaction } from '../interfaces'
import { NovesBaseApi } from './noves-base.api'

export class NovesForesightApi extends NovesBaseApi {
constructor() {
super('https://foresight.noves.fi')
}

async previewTransaction(
chain: string,
transaction: NovesTransaction,
viewAsAccountAddress?: string,
blockNumber?: number
): Promise<unknown | undefined> {
const response = await this.post<unknown>({
path: `evm/${chain}/preview`,
body: JSON.stringify(transaction),
queryParameters: {
...(viewAsAccountAddress ? { viewAsAccountAddress } : {}),
...(blockNumber ? { blockNumber } : {}),
},
})
return response
}

async describeTransaction(
chain: string,
transaction: NovesTransaction
): Promise<NovesForesightDescribeResponse | undefined> {
const response = await this.post<NovesForesightDescribeResponse>({
path: `evm/${chain}/describe`,
body: JSON.stringify(transaction),
})
return response
}
}
28 changes: 28 additions & 0 deletions packages/shared/src/lib/auxiliary/noves/apis/noves-nodeplus.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { EthRpcMethod } from '../enums'
import { SupportedChain } from '../interfaces'
import { EthRpcParams, EthRpcResponses, NodeType } from '../types'
import { NovesBaseApi } from './noves-base.api'

export class NovesNodePlusApi extends NovesBaseApi {
constructor() {
super('https://rpc.noves.fi')
}

async rpcPost<T extends EthRpcMethod>(
method: T,
chain: SupportedChain,
nodeType: NodeType,
params: EthRpcParams[T]
): Promise<EthRpcResponses[T] | undefined> {
const response = await this.post<EthRpcResponses[T]>({
path: `${chain.name}_${nodeType}`,
body: JSON.stringify({
method,
params,
id: 1,
jsonrpc: '2.0',
}),
})
return response
}
}
15 changes: 15 additions & 0 deletions packages/shared/src/lib/auxiliary/noves/apis/noves-pricing.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NovesTokenPriceResponse } from '../interfaces'
import { NovesBaseApi } from './noves-base.api'

export class NovesPricingApi extends NovesBaseApi {
constructor() {
super('https://pricing.noves.fi')
}

async getTokenPrice(tokenAddress: string, chain: string): Promise<NovesTokenPriceResponse | undefined> {
const response = await this.get<NovesTokenPriceResponse>({
path: `${chain}/price/${tokenAddress}`,
})
return response
}
}
104 changes: 104 additions & 0 deletions packages/shared/src/lib/auxiliary/noves/apis/noves-translate.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { QueryParameters } from '@core/utils'
import {
NovesHistoryItem,
NovesHistoryOptions,
NovesHistoryResponse,
NovesPagination,
NovesTxDescriptionResponse,
NovesTxResponse,
NovesTxsOptions,
NovesTxsResponse,
} from '../interfaces'
import { NovesBaseApi } from './noves-base.api'
import { NovesTokenBalancesResponse } from '../types'

export class NovesTranslateApi extends NovesBaseApi {
constructor() {
super('https://translate.noves.fi')
}

async getTransaction(
txHash: string,
chain: string,
viewAsAccountAddress?: string
): Promise<NovesTxResponse | undefined> {
const response = await this.get<NovesTxResponse>({
path: `evm/${chain}/tx/${txHash}`,
queryParameters: viewAsAccountAddress ? { viewAsAccountAddress } : {},
})
return response
}

async describeTransaction(txHash: string, chain: string): Promise<NovesTxDescriptionResponse | undefined> {
const response = await this.get<NovesTxDescriptionResponse>({ path: `evm/${chain}/describeTx/${txHash}` })
return response
}

async getTransactionsFromAddress(
accountAddress: string,
chain: string,
options?: NovesTxsOptions
): Promise<NovesTxsResponse | undefined> {
const response = await this.get<NovesTxsResponse>({
path: `evm/${chain}/txs/${accountAddress}`,
queryParameters: options as QueryParameters,
})
return response
}

async getHistoryFromAddress(
accountAddress: string,
chain: string,
options?: NovesHistoryOptions
): Promise<NovesHistoryItem[]> {
const response = await this.get<NovesHistoryResponse>({
path: `evm/${chain}/history/${accountAddress}`,
queryParameters: options as QueryParameters,
})

if (response) {
const responses = await this.recursiveRequest([response], response)

const items = responses.reduce((acc, response) => {
return [...acc, ...response.items]
}, [] as NovesHistoryItem[])

return items
}

return []
}

async recursiveRequest<T extends NovesPagination>(
previousResponses: T[],
pagination: NovesPagination
): Promise<T[]> {
if (pagination?.hasNextPage) {
const response = await this.get<T>({
path: pagination.nextPageUrl,
})

if (response) {
return this.recursiveRequest([...previousResponses, response], response)
}

return previousResponses
}

return previousResponses
}

async getTokenBalancesFromAddress(
accountAddress: string,
chain: string,
tokensHashes: string[],
blockNumber?: number
): Promise<NovesTokenBalancesResponse | undefined> {
const response = await this.post<NovesTokenBalancesResponse>({
path: `/evm/${chain}/tokens/balancesOf/${accountAddress}`,
body: JSON.stringify(tokensHashes),
queryParameters: blockNumber ? { blockNumber } : {},
})
return response
}
}
12 changes: 12 additions & 0 deletions packages/shared/src/lib/auxiliary/noves/apis/noves.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { INovesApi } from '../interfaces'
import { NovesForesightApi } from './noves-foresight.api'
import { NovesNodePlusApi } from './noves-nodeplus.api'
import { NovesPricingApi } from './noves-pricing.api'
import { NovesTranslateApi } from './noves-translate.api'

export class NovesApi implements INovesApi {
translate = new NovesTranslateApi()
foresight = new NovesForesightApi()
pricing = new NovesPricingApi()
nodePlus = new NovesNodePlusApi()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export enum EthRpcMethod {
BlockNumber = 'eth_blockNumber',
GetBlockByNumber = 'eth_getBlockByNumber',
GetBlockByHash = 'eth_getBlockByHash',
GetTransactionByHash = 'eth_getTransactionByHash',
GetTransactionReceipt = 'eth_getTransactionReceipt',
Call = 'eth_call',
GetBalance = 'eth_getBalance',
GetCode = 'eth_getCode',
GetLogs = 'eth_getLogs',
GetStorageAt = 'eth_getStorageAt',
EstimateGas = 'eth_estimateGas',
}
1 change: 1 addition & 0 deletions packages/shared/src/lib/auxiliary/noves/enums/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './eth-rpc-method.enum'
3 changes: 3 additions & 0 deletions packages/shared/src/lib/auxiliary/noves/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './apis'
export * from './interfaces'
export * from './types'
16 changes: 16 additions & 0 deletions packages/shared/src/lib/auxiliary/noves/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export * from './noves-api-supported-chain.interface'
export * from './noves-api.interface'
export * from './noves-foresight-api.interface'
export * from './noves-foresight-describe-response.interface'
export * from './noves-history-response.interface'
export * from './noves-nodeplus-api.interface'
export * from './noves-pagination.interface'
export * from './noves-pricing-api.interface'
export * from './noves-token-balance.interface'
export * from './noves-token-price-response.interface'
export * from './noves-token.interface'
export * from './noves-transaction.interface'
export * from './noves-translate-api.interface'
export * from './noves-tx-description-response.interface'
export * from './noves-tx-response.interface'
export * from './noves-txs-response.interface'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface SupportedChain {
name?: string
ecosystem?: string
evmChainId?: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { INovesTranslateApi } from './noves-translate-api.interface'
import { INovesForesightApi } from './noves-foresight-api.interface'
import { INovesPricingApi } from './noves-pricing-api.interface'
import { INovesNodeplusApi } from './noves-nodeplus-api.interface'

export interface INovesApi {
translate: INovesTranslateApi
foresight: INovesForesightApi
pricing: INovesPricingApi
nodePlus: INovesNodeplusApi
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NovesForesightDescribeResponse } from './noves-foresight-describe-response.interface'
import { NovesTransaction } from './noves-transaction.interface'

export interface INovesForesightApi {
previewTransaction(
chain: string,
transaction: NovesTransaction,
viewAsAccountAddress?: string,
blockNumber?: number
): Promise<unknown | undefined>
describeTransaction(
chain: string,
transaction: NovesTransaction
): Promise<NovesForesightDescribeResponse | undefined>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface NovesForesightDescribeResponse {
description: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { NovesPagination } from './noves-pagination.interface'

export interface NovesHistoryResponse extends NovesPagination {
items: NovesHistoryItem[]
}

export interface NovesHistoryItem {
transactionHash: string
blockNumber: string
timestamp: number
}
Loading

0 comments on commit 82278ad

Please sign in to comment.