diff --git a/src/api/init.ts b/src/api/init.ts index f008d859..a6dd7e48 100644 --- a/src/api/init.ts +++ b/src/api/init.ts @@ -8,7 +8,8 @@ import FastifyCors from '@fastify/cors'; import { StatusRoutes } from './routes/status'; import FastifyMetrics, { IFastifyMetrics } from 'fastify-metrics'; import { Server } from 'http'; -import { isProdEnv, PINO_LOGGER_CONFIG } from '@hirosystems/api-toolkit'; +import { isProdEnv } from './util/helpers'; +import { PINO_LOGGER_CONFIG } from '@hirosystems/api-toolkit'; export const Api: FastifyPluginAsync, Server, TypeBoxTypeProvider> = async ( fastify, diff --git a/src/api/routes/ft.ts b/src/api/routes/ft.ts index c436e907..2c54e089 100644 --- a/src/api/routes/ft.ts +++ b/src/api/routes/ft.ts @@ -19,7 +19,6 @@ import { import { handleChainTipCache, handleTokenCache } from '../util/cache'; import { generateTokenErrorResponse, TokenErrorResponseSchema } from '../util/errors'; import { parseMetadataLocaleBundle } from '../util/helpers'; -import BigNumber from 'bignumber.js'; const IndexRoutes: FastifyPluginCallback, Server, TypeBoxTypeProvider> = ( fastify, @@ -122,16 +121,11 @@ const ShowRoutes: FastifyPluginCallback, Server, TypeBoxTyp locale: request.query.locale, }); const contract = metadataBundle?.smartContract; - const decimals = metadataBundle?.token?.decimals ?? undefined; - const total_supply = metadataBundle?.token?.total_supply ?? undefined; await reply.send({ name: metadataBundle?.token?.name ?? undefined, symbol: metadataBundle?.token?.symbol ?? undefined, - decimals, - total_supply: - decimals != undefined && total_supply != undefined - ? BigNumber(total_supply).shiftedBy(-decimals).toString() - : undefined, + decimals: metadataBundle?.token?.decimals ?? undefined, + total_supply: metadataBundle?.token?.total_supply ?? undefined, token_uri: metadataBundle?.token?.uri ?? undefined, description: metadataBundle?.metadataLocale?.metadata?.description ?? undefined, tx_id: contract.tx_id, diff --git a/src/api/schemas.ts b/src/api/schemas.ts index f9cabc7c..ce5444a6 100644 --- a/src/api/schemas.ts +++ b/src/api/schemas.ts @@ -156,16 +156,19 @@ const TokenDescription = Type.String({ examples: [ 'Heavy hitters, all-stars and legends of the game join forces to create a collection of unique varsity jackets', ], + description: 'Description', }); const TokenImage = Type.String({ format: 'uri', examples: ['ipfs://ipfs/QmZMqhh2ztwuZ3Y8PyEp2z5auyH3TCm3nnr5ZfjjgDjd5q/12199.png'], + description: 'Original image URL', }); const TokenCachedImage = Type.String({ format: 'uri', examples: ['https://ipfs.io/ipfs/QmZMqhh2ztwuZ3Y8PyEp2z5auyH3TCm3nnr5ZfjjgDjd5q/12199.png'], + description: 'Cached image URL', }); export const Metadata = Type.Object( @@ -187,6 +190,7 @@ export type MetadataType = Static; export const TokenUri = Type.String({ format: 'uri', examples: ['ipfs://ipfs/Qmf9yDYuPTrp8NRUFf8xxDd5Ud24Dx9uYMwMn8o8G2cWPW/12200.json'], + description: "URI for this token's metadata JSON", }); export const TokenNotFoundResponse = Type.Object( @@ -245,10 +249,21 @@ export const PaginatedResponse = (type: T, title: string) => export const FtMetadataResponse = Type.Object( { - name: Type.Optional(Type.String({ examples: ['Wrapped USD'] })), - symbol: Type.Optional(Type.String({ examples: ['xUSD'] })), - decimals: Type.Optional(Type.Integer({ examples: [8] })), - total_supply: Type.Optional(Type.String({ examples: ['9999980000000'] })), + name: Type.Optional(Type.String({ examples: ['Wrapped USD'], description: 'Token name' })), + symbol: Type.Optional(Type.String({ examples: ['xUSD'], description: 'Token symbol' })), + decimals: Type.Optional( + Type.Integer({ + examples: [8], + description: "Number of decimal places clients should use to format this token's amounts", + }) + ), + total_supply: Type.Optional( + Type.String({ + examples: ['9999980000000'], + description: + 'Current circulating supply as reported by its contract. Clients should format this amount with the correct number of `decimals` before displaying to users', + }) + ), token_uri: Type.Optional(TokenUri), description: Type.Optional(TokenDescription), image_uri: Type.Optional(TokenCachedImage), @@ -256,10 +271,15 @@ export const FtMetadataResponse = Type.Object( image_canonical_uri: Type.Optional(TokenImage), tx_id: Type.String({ examples: ['0x5642ca7d68976b6b2a2055689d9a57de26d67f0dd8b016d1b0d94cb634454cdd'], + description: 'ID for the transaction that deployed this token', + }), + sender_address: Type.String({ + examples: ['SPZA22A4D15RKH5G8XDGQ7BPC20Q5JNMH0VQKSR6'], + description: 'Deployer address', }), - sender_address: Type.String({ examples: ['SPZA22A4D15RKH5G8XDGQ7BPC20Q5JNMH0VQKSR6'] }), asset_identifier: Type.String({ examples: ['SPZA22A4D15RKH5G8XDGQ7BPC20Q5JNMH0VQKSR6.token-ststx-earn-v1::stSTXearn'], + description: 'Clarity asset identifier', }), metadata: Type.Optional(Metadata), }, diff --git a/src/api/util/helpers.ts b/src/api/util/helpers.ts index ebfd99d0..a6d3cf9f 100644 --- a/src/api/util/helpers.ts +++ b/src/api/util/helpers.ts @@ -1,6 +1,14 @@ import { DbMetadataLocaleBundle } from '../../pg/types'; import { MetadataPropertiesType, MetadataType, MetadataValueType } from '../schemas'; +export const isDevEnv = process.env.NODE_ENV === 'development'; +export const isTestEnv = process.env.NODE_ENV === 'test'; +export const isProdEnv = + process.env.NODE_ENV === 'production' || + process.env.NODE_ENV === 'prod' || + !process.env.NODE_ENV || + (!isTestEnv && !isDevEnv); + export function parseMetadataLocaleBundle( locale?: DbMetadataLocaleBundle ): MetadataType | undefined { diff --git a/src/index.ts b/src/index.ts index 4f22b6c8..66d36b99 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,12 +4,8 @@ import { buildApiServer, buildPromServer } from './api/init'; import { TokenProcessorMetrics } from './token-processor/token-processor-metrics'; import { ENV } from './env'; import { buildAdminRpcServer } from './admin-rpc/init'; -import { - buildProfilerServer, - isProdEnv, - logger, - registerShutdownConfig, -} from '@hirosystems/api-toolkit'; +import { isProdEnv } from './api/util/helpers'; +import { buildProfilerServer, logger, registerShutdownConfig } from '@hirosystems/api-toolkit'; import { closeChainhookServer, startChainhookServer } from './chainhook/server'; /** diff --git a/tests/api/ft.test.ts b/tests/api/ft.test.ts index dafe8d6f..04edcef7 100644 --- a/tests/api/ft.test.ts +++ b/tests/api/ft.test.ts @@ -167,7 +167,7 @@ describe('FT routes', () => { name: 'hello-world', symbol: 'HELLO', token_uri: 'http://test.com/uri.json', - total_supply: '0.000001', + total_supply: '1', sender_address: 'SP2SYHR84SDJJDK8M09HFS4KBFXPPCX9H7RZ9YVTS', asset_identifier: 'SP2SYHR84SDJJDK8M09HFS4KBFXPPCX9H7RZ9YVTS.hello-world::ft-token', tx_id: '0x123456', @@ -240,7 +240,7 @@ describe('FT routes', () => { name: 'hello-world', symbol: 'HELLO', token_uri: 'http://test.com/uri.json', - total_supply: '0.000001', + total_supply: '1', decimals: 6, sender_address: 'SP2SYHR84SDJJDK8M09HFS4KBFXPPCX9H7RZ9YVTS', asset_identifier: 'SP2SYHR84SDJJDK8M09HFS4KBFXPPCX9H7RZ9YVTS.hello-world::ft-token',