-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# What ❔ Add APIs to get token info. ## Why ❔ To be able to get token info including token usd price.
- Loading branch information
Showing
16 changed files
with
690 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { ApiProperty } from "@nestjs/swagger"; | ||
import { ResponseBaseDto } from "../common/responseBase.dto"; | ||
|
||
export class EthPriceDto { | ||
@ApiProperty({ | ||
type: String, | ||
description: "ETH price in USD", | ||
example: "1823.567", | ||
}) | ||
public readonly ethusd: string; | ||
|
||
@ApiProperty({ | ||
type: String, | ||
description: "ETH price timestamp", | ||
example: "1624961308", | ||
}) | ||
public readonly ethusd_timestamp: string; | ||
} | ||
|
||
export class EthPriceResponseDto extends ResponseBaseDto { | ||
@ApiProperty({ | ||
description: "ETH price", | ||
type: EthPriceDto, | ||
}) | ||
public readonly result: EthPriceDto; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { ApiProperty } from "@nestjs/swagger"; | ||
import { ResponseBaseDto } from "../common/responseBase.dto"; | ||
|
||
export class TokenInfoDto { | ||
@ApiProperty({ | ||
type: String, | ||
description: "Token contract address", | ||
example: "0x000000000000000000000000000000000000800A", | ||
}) | ||
public readonly contractAddress: string; | ||
|
||
@ApiProperty({ | ||
type: String, | ||
description: "Token name", | ||
example: "Ether", | ||
}) | ||
public readonly tokenName: string; | ||
|
||
@ApiProperty({ | ||
type: String, | ||
description: "Token symbol", | ||
example: "ETH", | ||
}) | ||
public readonly symbol: string; | ||
|
||
@ApiProperty({ | ||
type: String, | ||
description: "Token decimals", | ||
example: "18", | ||
}) | ||
public readonly tokenDecimal: string; | ||
|
||
@ApiProperty({ | ||
type: String, | ||
description: "Token price in USD", | ||
example: "1823.567", | ||
}) | ||
public readonly tokenPriceUSD: string; | ||
|
||
@ApiProperty({ | ||
type: String, | ||
description: "Token liquidity in USD", | ||
example: "220000000000", | ||
}) | ||
public readonly liquidity: string; | ||
|
||
@ApiProperty({ | ||
type: String, | ||
description: "Token L1 address", | ||
example: "0x0000000000000000000000000000000000000000", | ||
}) | ||
public readonly l1Address: string; | ||
|
||
@ApiProperty({ | ||
type: String, | ||
description: "Token icon URL", | ||
example: "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1698873266", | ||
}) | ||
public readonly iconURL: string; | ||
} | ||
|
||
export class TokenInfoResponseDto extends ResponseBaseDto { | ||
@ApiProperty({ | ||
description: "Token info", | ||
type: TokenInfoDto, | ||
isArray: true, | ||
}) | ||
public readonly result: TokenInfoDto[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { Test } from "@nestjs/testing"; | ||
import { mock } from "jest-mock-extended"; | ||
import { Logger } from "@nestjs/common"; | ||
import { TokenService } from "../../token/token.service"; | ||
import { Token, ETH_TOKEN } from "../../token/token.entity"; | ||
import { StatsController } from "./stats.controller"; | ||
|
||
describe("StatsController", () => { | ||
let controller: StatsController; | ||
let tokenServiceMock: TokenService; | ||
|
||
beforeEach(async () => { | ||
tokenServiceMock = mock<TokenService>({ | ||
findOne: jest.fn().mockResolvedValue(null), | ||
}); | ||
|
||
const module = await Test.createTestingModule({ | ||
controllers: [StatsController], | ||
providers: [ | ||
{ | ||
provide: TokenService, | ||
useValue: tokenServiceMock, | ||
}, | ||
], | ||
}).compile(); | ||
module.useLogger(mock<Logger>()); | ||
|
||
controller = module.get<StatsController>(StatsController); | ||
}); | ||
|
||
describe("ethPrice", () => { | ||
it("returns ok response and ETH price when ETH token is found", async () => { | ||
jest.spyOn(tokenServiceMock, "findOne").mockResolvedValueOnce({ | ||
usdPrice: ETH_TOKEN.usdPrice, | ||
offChainDataUpdatedAt: new Date("2023-03-03"), | ||
} as Token); | ||
|
||
const response = await controller.ethPrice(); | ||
expect(response).toEqual({ | ||
status: "1", | ||
message: "OK", | ||
result: { | ||
ethusd: ETH_TOKEN.usdPrice.toString(), | ||
ethusd_timestamp: Math.floor(new Date("2023-03-03").getTime() / 1000).toString(), | ||
}, | ||
}); | ||
}); | ||
|
||
it("returns ok response and ETH price with default values when ETH token doesn't have price details", async () => { | ||
jest.spyOn(tokenServiceMock, "findOne").mockResolvedValueOnce({} as Token); | ||
|
||
const response = await controller.ethPrice(); | ||
expect(response).toEqual({ | ||
status: "1", | ||
message: "OK", | ||
result: { | ||
ethusd: "", | ||
ethusd_timestamp: "", | ||
}, | ||
}); | ||
}); | ||
|
||
it("returns not ok response and no ETH price info when ETH token is not found", async () => { | ||
const response = await controller.ethPrice(); | ||
expect(response).toEqual({ | ||
status: "0", | ||
message: "No data found", | ||
result: null, | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { Controller, Get, UseFilters } from "@nestjs/common"; | ||
import { ApiTags, ApiExcludeController } from "@nestjs/swagger"; | ||
import { ResponseStatus, ResponseMessage } from "../dtos/common/responseBase.dto"; | ||
import { ApiExceptionFilter } from "../exceptionFilter"; | ||
import { EthPriceResponseDto } from "../dtos/stats/ethPrice.dto"; | ||
import { TokenService } from "../../token/token.service"; | ||
import { ETH_TOKEN } from "../../token/token.entity"; | ||
import { dateToTimestamp } from "../../common/utils"; | ||
|
||
const entityName = "stats"; | ||
|
||
@ApiExcludeController() | ||
@ApiTags(entityName) | ||
@Controller(`api/${entityName}`) | ||
@UseFilters(ApiExceptionFilter) | ||
export class StatsController { | ||
constructor(private readonly tokenService: TokenService) {} | ||
|
||
@Get("/ethprice") | ||
public async ethPrice(): Promise<EthPriceResponseDto> { | ||
const token = await this.tokenService.findOne(ETH_TOKEN.l2Address, { usdPrice: true, offChainDataUpdatedAt: true }); | ||
return { | ||
status: token ? ResponseStatus.OK : ResponseStatus.NOTOK, | ||
message: token ? ResponseMessage.OK : ResponseMessage.NO_DATA_FOUND, | ||
result: token | ||
? { | ||
ethusd: token.usdPrice?.toString() || "", | ||
ethusd_timestamp: token.offChainDataUpdatedAt | ||
? dateToTimestamp(token.offChainDataUpdatedAt).toString() | ||
: "", | ||
} | ||
: null, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Module } from "@nestjs/common"; | ||
import { StatsController } from "./stats.controller"; | ||
import { TokenModule } from "../../token/token.module"; | ||
|
||
@Module({ | ||
imports: [TokenModule], | ||
controllers: [StatsController], | ||
}) | ||
export class ApiStatsModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { Test } from "@nestjs/testing"; | ||
import { mock } from "jest-mock-extended"; | ||
import { Logger } from "@nestjs/common"; | ||
import { TokenService } from "../../token/token.service"; | ||
import { Token, ETH_TOKEN } from "../../token/token.entity"; | ||
import { TokenController } from "./token.controller"; | ||
|
||
describe("TokenController", () => { | ||
let controller: TokenController; | ||
let tokenServiceMock: TokenService; | ||
|
||
const contractAddress = "address"; | ||
|
||
beforeEach(async () => { | ||
tokenServiceMock = mock<TokenService>({ | ||
findOne: jest.fn().mockResolvedValue(null), | ||
}); | ||
|
||
const module = await Test.createTestingModule({ | ||
controllers: [TokenController], | ||
providers: [ | ||
{ | ||
provide: TokenService, | ||
useValue: tokenServiceMock, | ||
}, | ||
], | ||
}).compile(); | ||
module.useLogger(mock<Logger>()); | ||
|
||
controller = module.get<TokenController>(TokenController); | ||
}); | ||
|
||
describe("tokenInfo", () => { | ||
it("returns ok response and token info when token is found", async () => { | ||
jest.spyOn(tokenServiceMock, "findOne").mockResolvedValueOnce(ETH_TOKEN); | ||
|
||
const response = await controller.tokenInfo(contractAddress); | ||
expect(response).toEqual({ | ||
status: "1", | ||
message: "OK", | ||
result: [ | ||
{ | ||
contractAddress: ETH_TOKEN.l2Address, | ||
iconURL: ETH_TOKEN.iconURL, | ||
l1Address: ETH_TOKEN.l1Address, | ||
liquidity: ETH_TOKEN.liquidity.toString(), | ||
symbol: ETH_TOKEN.symbol, | ||
tokenDecimal: ETH_TOKEN.decimals.toString(), | ||
tokenName: ETH_TOKEN.name, | ||
tokenPriceUSD: ETH_TOKEN.usdPrice.toString(), | ||
}, | ||
], | ||
}); | ||
}); | ||
|
||
it("returns ok response and token info with default values when token doesn't have all details", async () => { | ||
jest.spyOn(tokenServiceMock, "findOne").mockResolvedValueOnce({ | ||
l2Address: "0x000000000000000000000000000000000000800A", | ||
symbol: "", | ||
decimals: 6, | ||
} as Token); | ||
|
||
const response = await controller.tokenInfo(contractAddress); | ||
expect(response).toEqual({ | ||
status: "1", | ||
message: "OK", | ||
result: [ | ||
{ | ||
contractAddress: "0x000000000000000000000000000000000000800A", | ||
iconURL: "", | ||
l1Address: "", | ||
liquidity: "", | ||
symbol: "", | ||
tokenDecimal: "6", | ||
tokenName: "", | ||
tokenPriceUSD: "", | ||
}, | ||
], | ||
}); | ||
}); | ||
|
||
it("returns not ok response and no token info when token is not found", async () => { | ||
const response = await controller.tokenInfo(contractAddress); | ||
expect(response).toEqual({ | ||
status: "0", | ||
message: "No data found", | ||
result: [], | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.