Skip to content

Commit

Permalink
The creation of functionality to obtain token prices
Browse files Browse the repository at this point in the history
  • Loading branch information
Marielbamar21 committed Feb 22, 2024
1 parent d9e8cdc commit 2172a8b
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 1 deletion.
36 changes: 36 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@nestjs/core": "^10.2.8",
"@nestjs/graphql": "^12.0.10",
"@nestjs/platform-fastify": "^10.2.8",
"@nestjs/schedule": "^4.0.1",
"axios": "^1.6.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
Expand Down Expand Up @@ -74,6 +75,6 @@
"diag": true,
"bail": false,
"comments": true,
"timeout": 40000
"timeout": 40000
}
}
4 changes: 4 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'
import { Module } from '@nestjs/common'
import { GraphQLModule } from '@nestjs/graphql'
import { ScheduleModule } from '@nestjs/schedule'
import { LoggerModule } from 'nestjs-pino'
import { join } from 'path'
import { AppResolver } from './app.resolver'
import { EnvModule } from './env/env.module'
import { EnvService } from './env/env.service'
import { StealthexModule } from './stealthex/stealthex.module'
import { TokensModule } from './tokens/tokens.module'

@Module({
imports: [
Expand All @@ -30,7 +32,9 @@ import { StealthexModule } from './stealthex/stealthex.module'
}
},
}),
ScheduleModule.forRoot(),
StealthexModule,
TokensModule,
],
providers: [AppResolver],
})
Expand Down
4 changes: 4 additions & 0 deletions src/env/env.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export class EnvService {
public readonly STEALTH_EX_BASE_URL: string
public readonly STEALTH_EX_API_KEY: string

public readonly COINMARKETCAP_API_KEY: string

constructor(private readonly config: ConfigService) {
// public env variables
this.NODE_ENV = this.config.get<Environment>('NODE_ENV', Environment.Development)
Expand All @@ -36,6 +38,8 @@ export class EnvService {

this.STEALTH_EX_BASE_URL = this.config.get<string>('STEALTH_EX_BASE_URL', '')
this.STEALTH_EX_API_KEY = this.config.get<string>('STEALTH_EX_API_KEY', '')

this.COINMARKETCAP_API_KEY = this.config.get<string>('COINMARKETCAP_API_KEY', '')
}

public isProduction(): boolean {
Expand Down
11 changes: 11 additions & 0 deletions src/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Query {
getActiveSwaps(swapsIds: [String!]! = []): [ActiveSwaps!]!
getEstimatedAmount(amount: String! = "", from: String! = "", to: String! = ""): EstimatedAmount!
getPairTokensFromNativeCurrency(nativeCurrencies: [String!]! = []): GetPairTokens!
getTokenPrice(tokens: [String!]! = []): TokensPrice!
getTokens: Tokens!
getTokensToSwap: Tokens!
status: String!
Expand All @@ -53,6 +54,16 @@ type Token {
symbol: String!
}

type TokenInfo {
name: String!
symbol: String!
usd: Float!
}

type Tokens {
tokens: [Token!]!
}

type TokensPrice {
tokens: [TokenInfo!]!
}
29 changes: 29 additions & 0 deletions src/tokens/dtos/array-tokens-symbol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const TokensSymbols = [
'DOT',
'ASTR',
'USDT',
'ACA',
'aSEED',
'LDOT',
'GLMR',
'IBTC',
'INTR',
'PHA',
'BNC',
'vDOT',
'EQD',
'HDX',
'KSM',
'tDOT',
'lcDOT',
'EQ',
'MOVR',
'SDN',
'ETH',
'USDC',
'WBTC',
'BUSD',
'DAI',
'MATIC',
'BNB',
]
25 changes: 25 additions & 0 deletions src/tokens/dtos/get-tokens.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ObjectType, Field, ArgsType } from '@nestjs/graphql'

@ObjectType()
export class TokensPrice {
@Field(() => [TokenInfo])
tokens = []
}

@ObjectType()
export class TokenInfo {
@Field(() => String)
name = ''

@Field(() => String)
symbol = ''

@Field(() => Number)
usd = 0
}

@ArgsType()
export class GetTokensArgs {
@Field(() => [String])
tokens: string[] = []
}
27 changes: 27 additions & 0 deletions src/tokens/tokens.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export interface CoinMarketTokenPrice {
[key: tokenSymbol]: TokenPrice[]
}

export interface TokenPrice {
name: string
symbol: string
quote: {
USD: {
price: number | null
percent_change_1h: number
percent_change_24h: number
percent_change_7d: number
}
}
}
type tokenSymbol = string

export interface Tokens {
tokens: Token[]
}

export interface Token {
name: string
symbol: string
usd: number | null
}
13 changes: 13 additions & 0 deletions src/tokens/tokens.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { HttpModule } from '@nestjs/axios'
import { Module } from '@nestjs/common'
import { EnvModule } from 'src/env/env.module'
//import { EnvService } from '../env/env.service'
import { TokensResolver } from './tokens.resolver'
import { TokensService } from './tokens.service'

@Module({
imports: [HttpModule, EnvModule],
providers: [TokensService, TokensResolver],
exports: [TokensService],
})
export class TokensModule {}
16 changes: 16 additions & 0 deletions src/tokens/tokens.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Resolver, Query, Args } from '@nestjs/graphql'
import { TokensPrice, GetTokensArgs } from './dtos/get-tokens.dto'
import { TokensService } from './tokens.service'

@Resolver()
export class TokensResolver {
constructor(private readonly tokensService: TokensService) {}

@Query(() => TokensPrice)
getTokenPrice(@Args() args: GetTokensArgs) {
const tokens = this.tokensService.getTokensPrice(args.tokens)
return {
tokens,
}
}
}
68 changes: 68 additions & 0 deletions src/tokens/tokens.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { HttpService } from '@nestjs/axios'
import { Injectable } from '@nestjs/common'
import { Interval } from '@nestjs/schedule'
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino'
import { catchError, firstValueFrom, map } from 'rxjs'
import { TokensSymbols } from './dtos/array-tokens-symbol'
import { Token, CoinMarketTokenPrice, TokenPrice } from './tokens.interface'
import { EnvService } from '../env/env.service'

@Injectable()
export class TokensService {
private tokens: Token[] = []
constructor(
@InjectPinoLogger(TokensService.name)
private readonly logger: PinoLogger,
private readonly httpService: HttpService,
private readonly envService: EnvService,
) {}

getTokensPrice(symbol: string[]): Token[] {
return this.tokens.filter((element) => symbol.includes(element.symbol))
}

async fetchTokensPrices(symbols: string[]): Promise<CoinMarketTokenPrice> {
const response = await this.httpService
.get('https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest', {
headers: {
'X-CMC_PRO_API_KEY': this.envService.COINMARKETCAP_API_KEY,
},
params: {
symbol: symbols.join(','),
},
})
.pipe(
map((res) => res.data.data),
catchError((error: any) => {
this.logger.error({ error }, 'Error fetching tokens prices')
throw new Error(error?.response?.data.message || error.message)
}),
)

const tokens = await firstValueFrom(response)
return tokens
}
@Interval(5000)
async updateTokenPrices() {
try {
const tokenPrices = await this.fetchTokensPrices(TokensSymbols)
const newPrices: Token[] = []
const symbols: string[] = Object.keys(tokenPrices)
if (symbols.length === 0) return
symbols.forEach((element) => {
const token = tokenPrices[element] as TokenPrice[]
const { name, symbol, quote } = token[0]
const price = quote.USD.price || 0

newPrices.push({
name: name,
symbol: symbol,
usd: price,
})
})
this.tokens = newPrices
} catch (error) {
this.logger.error(JSON.stringify(error, null, 2), 'Error updating token prices')
}
}
}

0 comments on commit 2172a8b

Please sign in to comment.