From 329d3e13571df9f9da35ef929e33868daf305776 Mon Sep 17 00:00:00 2001 From: Rafael Cardenas Date: Mon, 1 Jul 2024 11:12:02 -0600 Subject: [PATCH] chore: better docs --- .github/workflows/vercel.yml | 2 +- .gitignore | 1 + api/src/api/routes/addresses.ts | 4 +- api/src/api/routes/blocks.ts | 4 +- api/src/api/routes/etchings.ts | 18 +- api/src/api/routes/transactions.ts | 4 +- api/src/api/schemas.ts | 326 ++++++++++++++++++++++------- api/src/pg/types.ts | 2 +- 8 files changed, 265 insertions(+), 96 deletions(-) diff --git a/.github/workflows/vercel.yml b/.github/workflows/vercel.yml index dd73130..c643bc3 100644 --- a/.github/workflows/vercel.yml +++ b/.github/workflows/vercel.yml @@ -25,7 +25,7 @@ jobs: environment: name: ${{ github.ref_name == 'main' && 'Production' || 'Preview' }} - url: ${{ github.ref_name == 'main' && 'https://runes-api.vercel.app/' || 'https://runes-api-pbcblockstack-blockstack.vercel.app/' }} + url: ${{ github.ref_name == 'main' && 'https://runes-api.vercel.app/' || 'https://runehook-pbcblockstack-hirosystems.vercel.app/' }} steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 5834ec2..54ec2a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target Runehook.toml +.DS_Store diff --git a/api/src/api/routes/addresses.ts b/api/src/api/routes/addresses.ts index 29f6870..5dca58d 100644 --- a/api/src/api/routes/addresses.ts +++ b/api/src/api/routes/addresses.ts @@ -19,9 +19,9 @@ export const AddressRoutes: FastifyPluginCallback< { schema: { operationId: 'getAddressBalances', - summary: 'Get address balances', + summary: 'Address balances', description: 'Retrieves a paginated list of address balances', - tags: ['Runes'], + tags: ['Balances'], params: Type.Object({ address: AddressSchema, }), diff --git a/api/src/api/routes/blocks.ts b/api/src/api/routes/blocks.ts index a2d1751..6c49050 100644 --- a/api/src/api/routes/blocks.ts +++ b/api/src/api/routes/blocks.ts @@ -19,9 +19,9 @@ export const BlockRoutes: FastifyPluginCallback< { schema: { operationId: 'getBlockActivity', - summary: 'Get block activity', + summary: 'Block activity', description: 'Retrieves a paginated list of rune activity for a block', - tags: ['Runes'], + tags: ['Activities'], params: Type.Object({ block: BlockSchema, }), diff --git a/api/src/api/routes/etchings.ts b/api/src/api/routes/etchings.ts index 3a2a673..6e37afe 100644 --- a/api/src/api/routes/etchings.ts +++ b/api/src/api/routes/etchings.ts @@ -29,9 +29,9 @@ export const EtchingRoutes: FastifyPluginCallback< { schema: { operationId: 'getEtchings', - summary: 'Get rune etchings', + summary: 'Rune etchings', description: 'Retrieves a paginated list of rune etchings', - tags: ['Runes'], + tags: ['Etchings'], querystring: Type.Object({ offset: Optional(OffsetSchema), limit: Optional(LimitSchema), @@ -61,7 +61,7 @@ export const EtchingRoutes: FastifyPluginCallback< operationId: 'getEtching', summary: 'Rune etching', description: 'Retrieves information for a Rune etching', - tags: ['Runes'], + tags: ['Etchings'], params: Type.Object({ etching: RuneSchema, }), @@ -86,9 +86,9 @@ export const EtchingRoutes: FastifyPluginCallback< { schema: { operationId: 'getRuneActivity', - summary: 'Rune rune activity', + summary: 'Rune activity', description: 'Retrieves all activity for a Rune', - tags: ['Runes'], + tags: ['Activities'], params: Type.Object({ etching: RuneSchema, }), @@ -119,9 +119,9 @@ export const EtchingRoutes: FastifyPluginCallback< { schema: { operationId: 'getRuneAddressActivity', - summary: 'Rune rune activity for address', + summary: 'Rune activity for address', description: 'Retrieves all activity for a Rune address', - tags: ['Runes'], + tags: ['Activities'], params: Type.Object({ etching: RuneSchema, address: AddressSchema, @@ -160,7 +160,7 @@ export const EtchingRoutes: FastifyPluginCallback< operationId: 'getRuneHolders', summary: 'Rune holders', description: 'Retrieves a paginated list of holders for a Rune', - tags: ['Runes'], + tags: ['Balances'], params: Type.Object({ etching: RuneSchema, }), @@ -193,7 +193,7 @@ export const EtchingRoutes: FastifyPluginCallback< operationId: 'getRuneHolderBalance', summary: 'Rune holder balance', description: 'Retrieves holder balance for a specific Rune', - tags: ['Runes'], + tags: ['Balances'], params: Type.Object({ etching: RuneSchema, address: AddressSchema, diff --git a/api/src/api/routes/transactions.ts b/api/src/api/routes/transactions.ts index da78387..1baf14d 100644 --- a/api/src/api/routes/transactions.ts +++ b/api/src/api/routes/transactions.ts @@ -19,9 +19,9 @@ export const TransactionRoutes: FastifyPluginCallback< { schema: { operationId: 'getTransactionActivity', - summary: 'Get transaction activity', + summary: 'Transaction activity', description: 'Retrieves a paginated list of rune activity for a transaction', - tags: ['Runes'], + tags: ['Activities'], params: Type.Object({ tx_id: TransactionIdSchema, }), diff --git a/api/src/api/schemas.ts b/api/src/api/schemas.ts index eebcd81..ce257b1 100644 --- a/api/src/api/schemas.ts +++ b/api/src/api/schemas.ts @@ -7,7 +7,7 @@ export const OpenApiSchemaOptions: SwaggerOptions = { openapi: { info: { title: 'Runes API', - description: ``, + description: `REST API to get information about Runes`, version: SERVER_VERSION.tag, }, externalDocs: { @@ -22,17 +22,25 @@ export const OpenApiSchemaOptions: SwaggerOptions = { ], tags: [ { - name: 'Runes', - description: '', + name: 'Etchings', + description: 'Rune etchings', + }, + { + name: 'Activities', + description: 'Rune activities', + }, + { + name: 'Balances', + description: 'Rune balances', + }, + { + name: 'Status', + description: 'API status', }, ], }, }; -// ========================== -// Parameters -// ========================== - export const OffsetSchema = Type.Integer({ minimum: 0, title: 'Offset', @@ -48,12 +56,12 @@ export const LimitSchema = Type.Integer({ }); export type Limit = Static; -const RuneIdSchema = Type.RegEx(/^[0-9]+:[0-9]+$/); -const RuneNumberSchema = Type.RegEx(/^[0-9]+$/); +const RuneIdSchema = Type.RegEx(/^[0-9]+:[0-9]+$/, { title: 'Rune ID' }); +const RuneNumberSchema = Type.RegEx(/^[0-9]+$/, { title: 'Rune number' }); export const RuneNumberSchemaCType = TypeCompiler.Compile(RuneNumberSchema); -const RuneNameSchema = Type.RegEx(/^[A-Z]+$/); +const RuneNameSchema = Type.RegEx(/^[A-Z]+$/, { title: 'Rune name' }); export const RuneNameSchemaCType = TypeCompiler.Compile(RuneNameSchema); -const RuneSpacedNameSchema = Type.RegEx(/^[A-Z](•[A-Z]+)+$/); +const RuneSpacedNameSchema = Type.RegEx(/^[A-Z](•[A-Z]+)+$/, { title: 'Rune name with spacers' }); export const RuneSpacedNameSchemaCType = TypeCompiler.Compile(RuneSpacedNameSchema); export const RuneSchema = Type.Union([ @@ -78,27 +86,26 @@ export const TransactionIdSchema = Type.RegEx(/^[a-fA-F0-9]{64}$/, { }); export type TransactionId = Static; -export const TransactionOutputSchema = Type.RegEx(/^[a-fA-F0-9]{64}:[0-9]+$/, { - title: 'Transaction Output', - description: 'A transaction output', - examples: ['8f46f0d4ef685e650727e6faf7e30f23b851a7709714ec774f7909b3fb5e604c:0'], -}); -export type TransactionOutput = Static; +// const TransactionOutputSchema = Type.RegEx(/^[a-fA-F0-9]{64}:[0-9]+$/, { +// title: 'Transaction Output', +// description: 'A transaction output', +// examples: ['8f46f0d4ef685e650727e6faf7e30f23b851a7709714ec774f7909b3fb5e604c:0'], +// }); +// type TransactionOutput = Static; -export const BlockHeightSchema = Type.RegEx(/^[0-9]+$/, { +const BlockHeightSchema = Type.RegEx(/^[0-9]+$/, { title: 'Block Height', description: 'Bitcoin block height', examples: [777678], }); export const BlockHeightCType = TypeCompiler.Compile(BlockHeightSchema); -export type BlockHeight = Static; const BlockHashSchema = Type.RegEx(/^[0]{8}[a-fA-F0-9]{56}$/, { title: 'Block Hash', description: 'Bitcoin block hash', examples: ['0000000000000000000452773967cdd62297137cdaf79950c5e8bb0c62075133'], }); -export type BlockHash = Static; +type BlockHash = Static; export const BlockSchema = Type.Union([BlockHeightSchema, BlockHashSchema]); export type Block = Static; @@ -116,78 +123,230 @@ export const ApiStatusResponse = Type.Object( { title: 'Api Status Response' } ); -const LocationDetailResponseSchema = Type.Object({ - block_hash: Type.String({ - examples: ['00000000000000000000c9787573a1f1775a2b56b403a2d0c7957e9a5bc754bb'], - }), - block_height: Type.Integer({ examples: [840000] }), - tx_id: Type.String({ - examples: ['2bb85f4b004be6da54f766c17c1e855187327112c231ef2ff35ebad0ea67c69e'], - }), - tx_index: Type.Integer({ examples: [1] }), - vout: Optional(Type.Integer({ examples: [100] })), - output: Optional( - Type.String({ - examples: ['2bb85f4b004be6da54f766c17c1e855187327112c231ef2ff35ebad0ea67c69e:100'], - }) - ), - timestamp: Type.Integer({ examples: [1713571767] }), +const LocationDetailResponseSchema = Type.Object( + { + block_hash: Type.String({ + examples: ['00000000000000000000c9787573a1f1775a2b56b403a2d0c7957e9a5bc754bb'], + title: 'Block hash', + description: 'Bitcoin block hash', + }), + block_height: Type.Integer({ + examples: [840000], + title: 'Block height', + description: 'Bitcoin block height', + }), + tx_id: Type.String({ + examples: ['2bb85f4b004be6da54f766c17c1e855187327112c231ef2ff35ebad0ea67c69e'], + title: 'Transaction ID', + description: 'Bitcoin transaction ID', + }), + tx_index: Type.Integer({ + examples: [1], + title: 'Transaction Index', + description: 'Index of this transaction in its Bitcoin block', + }), + vout: Optional( + Type.Integer({ + examples: [100], + title: 'Output number', + description: 'Bitcoin transaction output number', + }) + ), + output: Optional( + Type.String({ + examples: ['2bb85f4b004be6da54f766c17c1e855187327112c231ef2ff35ebad0ea67c69e:100'], + title: 'Transaction output', + description: 'Bitcoin transaction output', + }) + ), + timestamp: Type.Integer({ + examples: [1713571767], + title: 'Timestamp', + description: 'Bitcoin transaction timestamp', + }), + }, + { + title: 'Transaction location', + description: 'Location of the transaction which confirmed this operation', + } +); + +const RuneIdResponseSchema = Type.String({ + title: 'ID', + description: 'Rune ID', + examples: ['840000:1'], +}); + +const RuneNameResponseSchema = Type.String({ + title: 'Name', + description: 'Rune name', + examples: ['ZZZZZFEHUZZZZZ'], +}); + +const RuneSpacedNameResponseSchema = Type.String({ + title: 'Spaced name', + description: 'Rune name with spacers', + examples: ['Z•Z•Z•Z•Z•FEHU•Z•Z•Z•Z•Z'], +}); + +const RuneNumberResponseSchema = Type.Integer({ + title: 'Number', + description: 'Rune number', + examples: [1], }); export const EtchingResponseSchema = Type.Object({ - id: Type.String({ examples: ['840000:1'] }), - name: Type.String({ examples: ['ZZZZZFEHUZZZZZ'] }), - spaced_name: Type.String({ examples: ['Z•Z•Z•Z•Z•FEHU•Z•Z•Z•Z•Z'] }), - number: Type.Integer({ examples: [1] }), - divisibility: Type.Integer({ examples: [2] }), - symbol: Type.String({ examples: ['ᚠ'] }), - turbo: Type.Boolean({ examples: [false] }), - mint_terms: Type.Object({ - amount: Nullable(Type.String({ examples: ['100'] })), - cap: Nullable(Type.String({ examples: ['1111111'] })), - height_start: Nullable(Type.Integer({ examples: [840000] })), - height_end: Nullable(Type.Integer({ examples: [1050000] })), - offset_start: Nullable(Type.Integer({ examples: [0] })), - offset_end: Nullable(Type.Integer({ examples: [200] })), - }), - supply: Type.Object({ - current: Type.String({ examples: ['11274916350'] }), - minted: Type.String({ examples: ['274916100'] }), - total_mints: Type.String({ examples: ['250'] }), - mint_percentage: Type.String({ examples: ['59.4567'] }), - mintable: Type.Boolean(), - burned: Type.String({ examples: ['5100'] }), - total_burns: Type.String({ examples: ['17'] }), - premine: Type.String({ examples: ['11000000000'] }), + id: RuneIdResponseSchema, + name: RuneNameResponseSchema, + spaced_name: RuneSpacedNameResponseSchema, + number: RuneNumberResponseSchema, + divisibility: Type.Integer({ + title: 'Divisibility', + description: 'Rune decimal places', + examples: [2], }), + symbol: Type.String({ title: 'Symbol', description: 'Rune symbol', examples: ['ᚠ'] }), + turbo: Type.Boolean({ title: 'Turbo', description: 'Rune upgradeability', examples: [false] }), + mint_terms: Type.Object( + { + amount: Nullable( + Type.String({ + examples: ['100'], + title: 'Mint amount', + description: 'Amount awarded per mint', + }) + ), + cap: Nullable( + Type.String({ + examples: ['1111111'], + title: 'Mint cap', + description: 'Maximum number of mints allowed', + }) + ), + height_start: Nullable( + Type.Integer({ + examples: [840000], + title: 'Mint block height start', + description: 'Block height at which the mint period opens', + }) + ), + height_end: Nullable( + Type.Integer({ + examples: [1050000], + title: 'Mint block height end', + description: 'Block height at which the mint period closes', + }) + ), + offset_start: Nullable( + Type.Integer({ + examples: [0], + title: 'Mint block height offset start', + description: 'Block height etching offset at which the mint period opens', + }) + ), + offset_end: Nullable( + Type.Integer({ + examples: [200], + title: 'Mint block height offset end', + description: 'Block height etching offset at which the mint period closes', + }) + ), + }, + { title: 'Mint terms', description: 'Rune mint terms' } + ), + supply: Type.Object( + { + current: Type.String({ + examples: ['11274916350'], + title: 'Current supply', + description: 'Circulating supply including mints, burns and premine', + }), + minted: Type.String({ + examples: ['274916100'], + title: 'Minted amount', + description: 'Total minted amount', + }), + total_mints: Type.String({ + examples: ['250'], + title: 'Total mints', + description: 'Number of mints for this rune', + }), + mint_percentage: Type.String({ + examples: ['59.4567'], + title: 'Mint percentage', + description: 'Percentage of mints that have been claimed', + }), + mintable: Type.Boolean({ + title: 'Mintable', + description: 'Whether or not this rune is mintable at this time', + }), + burned: Type.String({ + examples: ['5100'], + title: 'Burned amount', + description: 'Total burned amount', + }), + total_burns: Type.String({ + examples: ['17'], + title: 'Total burns', + description: 'Number of burns for this rune', + }), + premine: Type.String({ + examples: ['11000000000'], + title: 'Premine amount', + description: 'Amount premined for this rune', + }), + }, + { title: 'Supply information', description: 'Rune supply information' } + ), location: LocationDetailResponseSchema, }); export type EtchingResponse = Static; const RuneDetailResponseSchema = Type.Object({ - rune: Type.Object({ - id: Type.String({ examples: ['840000:1'] }), - name: Type.String({ examples: ['ZZZZZFEHUZZZZZ'] }), - spaced_name: Type.String({ examples: ['Z•Z•Z•Z•Z•FEHU•Z•Z•Z•Z•Z'] }), - }), + rune: Type.Object( + { + id: RuneIdResponseSchema, + name: RuneNameResponseSchema, + spaced_name: RuneSpacedNameResponseSchema, + }, + { title: 'Rune detail', description: 'Details of the rune affected by this activity' } + ), }); export const SimpleActivityResponseSchema = Type.Object({ - address: Optional(Type.String({ examples: ['bc1q7jd477wc5s88hsvenr0ddtatsw282hfjzg59wz'] })), + address: Optional( + Type.String({ + examples: ['bc1q7jd477wc5s88hsvenr0ddtatsw282hfjzg59wz'], + title: 'Address', + description: 'Bitcoin address which initiated this activity', + }) + ), receiver_address: Optional( - Type.String({ examples: ['bc1pgdrveee2v4ez95szaakw5gkd8eennv2dddf9rjdrlt6ch56lzrrsxgvazt'] }) + Type.String({ + examples: ['bc1pgdrveee2v4ez95szaakw5gkd8eennv2dddf9rjdrlt6ch56lzrrsxgvazt'], + title: 'Receiver address', + description: 'Bitcoin address which is receiving rune balance', + }) + ), + amount: Optional( + Type.String({ + examples: ['11000000000'], + title: 'Amount', + description: 'Rune amount relevat to this activity', + }) + ), + operation: Type.Union( + [ + Type.Literal('etching'), + Type.Literal('mint'), + Type.Literal('burn'), + Type.Literal('send'), + Type.Literal('receive'), + ], + { title: 'Operation', description: 'Type of operation described in this activity' } ), - amount: Optional(Type.String({ examples: ['11000000000'] })), - operation: Type.Union([ - Type.Literal('etching'), - Type.Literal('mint'), - Type.Literal('burn'), - Type.Literal('send'), - Type.Literal('receive'), - ]), location: LocationDetailResponseSchema, }); -export type SimpleActivityResponse = Static; export const ActivityResponseSchema = Type.Intersect([ RuneDetailResponseSchema, @@ -196,10 +355,19 @@ export const ActivityResponseSchema = Type.Intersect([ export type ActivityResponse = Static; export const SimpleBalanceResponseSchema = Type.Object({ - address: Optional(Type.String({ examples: ['bc1q7jd477wc5s88hsvenr0ddtatsw282hfjzg59wz'] })), - balance: Type.String({ examples: ['11000000000'] }), + address: Optional( + Type.String({ + examples: ['bc1q7jd477wc5s88hsvenr0ddtatsw282hfjzg59wz'], + title: 'Address', + description: 'Bitcoin address which holds this balance', + }) + ), + balance: Type.String({ + examples: ['11000000000'], + title: 'Balance', + description: 'Rune balance', + }), }); -export type SimpleBalanceResponse = Static; export const BalanceResponseSchema = Type.Intersect([ RuneDetailResponseSchema, diff --git a/api/src/pg/types.ts b/api/src/pg/types.ts index ccaffe8..b5d41d8 100644 --- a/api/src/pg/types.ts +++ b/api/src/pg/types.ts @@ -5,7 +5,7 @@ export type DbPaginatedResult = { export type DbCountedQueryResult = T & { total: number }; -export type DbRune = { +type DbRune = { id: string; number: number; name: string;