From 444c0dd5430e637d287468eedb96cfcf972b077e Mon Sep 17 00:00:00 2001 From: Bart Tadych Date: Tue, 23 Jan 2024 12:59:10 +0100 Subject: [PATCH] feat: add getTokenMetadata to solana api. (#1171) * feat: add getTokenMetadata to solana api. * update swagger. --- .changeset/shaggy-sheep-wonder.md | 7 ++ .changeset/tidy-bears-tan.md | 7 ++ .../common/solUtils/generator.config.json | 2 +- .../src/generated/client/abstractClient.ts | 15 ++++ .../operations/GetTokenMetadataOperation.ts | 49 ++++++++++++ .../src/generated/operations/index.ts | 1 + .../src/generated/operations/operations.ts | 2 + .../types/SolGetTokenMetadataNetworkEnum.ts | 16 ++++ .../src/generated/types/SolMetaplexToken.ts | 75 +++++++++++++++++++ .../src/generated/types/SolSPLTokenPrice.ts | 24 +++--- .../src/generated/types/SolTokenMetadata.ts | 70 +++++++++++++++++ .../solUtils/src/generated/types/index.ts | 3 + .../common/solUtils/src/operations/openapi.ts | 50 ++++++++++++- .../integration/test/getTokenPrice.test.ts | 2 +- packages/solApi/src/generated/ClientSolApi.ts | 5 +- 15 files changed, 309 insertions(+), 19 deletions(-) create mode 100644 .changeset/shaggy-sheep-wonder.md create mode 100644 .changeset/tidy-bears-tan.md create mode 100644 packages/common/solUtils/src/generated/operations/GetTokenMetadataOperation.ts create mode 100644 packages/common/solUtils/src/generated/types/SolGetTokenMetadataNetworkEnum.ts create mode 100644 packages/common/solUtils/src/generated/types/SolMetaplexToken.ts create mode 100644 packages/common/solUtils/src/generated/types/SolTokenMetadata.ts diff --git a/.changeset/shaggy-sheep-wonder.md b/.changeset/shaggy-sheep-wonder.md new file mode 100644 index 0000000000..009360f165 --- /dev/null +++ b/.changeset/shaggy-sheep-wonder.md @@ -0,0 +1,7 @@ +--- +'@moralisweb3/common-sol-utils': patch +'@moralisweb3/sol-api': patch +'moralis': patch +--- + +This version adds a new `getTokenMetadata` endpoint to the Solana API. diff --git a/.changeset/tidy-bears-tan.md b/.changeset/tidy-bears-tan.md new file mode 100644 index 0000000000..23aabfebfb --- /dev/null +++ b/.changeset/tidy-bears-tan.md @@ -0,0 +1,7 @@ +--- +'@moralisweb3/common-sol-utils': minor +'@moralisweb3/sol-api': minor +'moralis': minor +--- + +`nativePrice`, `exchangeName` and `exchangeAddress` properties in the `SolSPLTokenPrice` class are now optional. diff --git a/packages/common/solUtils/generator.config.json b/packages/common/solUtils/generator.config.json index 5081e33e72..cf4e35e0d0 100644 --- a/packages/common/solUtils/generator.config.json +++ b/packages/common/solUtils/generator.config.json @@ -34,7 +34,7 @@ "operations": { "groupRef": "#/x-tag-sdk", "isEnabledRef": "#/x-tag-sdk", - "filterOperationIds": ["getTokenPrice"] + "filterOperationIds": ["getTokenPrice", "getTokenMetadata"] } } } diff --git a/packages/common/solUtils/src/generated/client/abstractClient.ts b/packages/common/solUtils/src/generated/client/abstractClient.ts index 442caf0cc4..4003513d5f 100644 --- a/packages/common/solUtils/src/generated/client/abstractClient.ts +++ b/packages/common/solUtils/src/generated/client/abstractClient.ts @@ -1,5 +1,7 @@ import { GetTokenPriceOperation, GetTokenPriceOperationRequest, GetTokenPriceOperationRequestJSON } from '../operations/GetTokenPriceOperation'; import { SolSPLTokenPrice, SolSPLTokenPriceJSON } from '../types/SolSPLTokenPrice'; +import { GetTokenMetadataOperation, GetTokenMetadataOperationRequest, GetTokenMetadataOperationRequestJSON } from '../operations/GetTokenMetadataOperation'; +import { SolTokenMetadata, SolTokenMetadataJSON } from '../types/SolTokenMetadata'; export interface OperationV3 { operationId: string; @@ -36,5 +38,18 @@ export abstract class AbstractClient { SolSPLTokenPrice, SolSPLTokenPriceJSON >(GetTokenPriceOperation), + /** + * @description Get the global token metadata for a given network and contract (mint, standard, name, symbol, metaplex). + * @param request Request with parameters. + * @param {Object} request.network The network to query + * @param {Object} request.address The address of the contract + * @returns {Object} Response for the request. + */ + getTokenMetadata: this.createEndpoint< + GetTokenMetadataOperationRequest, + GetTokenMetadataOperationRequestJSON, + SolTokenMetadata, + SolTokenMetadataJSON + >(GetTokenMetadataOperation), }; } diff --git a/packages/common/solUtils/src/generated/operations/GetTokenMetadataOperation.ts b/packages/common/solUtils/src/generated/operations/GetTokenMetadataOperation.ts new file mode 100644 index 0000000000..4999303d76 --- /dev/null +++ b/packages/common/solUtils/src/generated/operations/GetTokenMetadataOperation.ts @@ -0,0 +1,49 @@ +import { SolNetwork, SolNetworkInput, SolNetworkJSON, SolAddress, SolAddressInput, SolAddressJSON } from '../../dataTypes'; +import { SolTokenMetadata, SolTokenMetadataJSON } from '../types/SolTokenMetadata'; + +// request parameters: +// - network ($ref: #/paths/~1token~1{network}~1{address}~1metadata/get/parameters/0/schema) +// - address ($ref: #/paths/~1token~1{network}~1{address}~1metadata/get/parameters/1/schema) + +export interface GetTokenMetadataOperationRequest { + /** + * @description The network to query + */ + readonly network: SolNetworkInput | SolNetwork; + /** + * @description The address of the contract + */ + readonly address: SolAddressInput | SolAddress; +} + +export interface GetTokenMetadataOperationRequestJSON { + readonly network: SolNetworkJSON; + readonly address: SolAddressJSON; +} + +export type GetTokenMetadataOperationResponse = SolTokenMetadata; +export type GetTokenMetadataOperationResponseJSON = SolTokenMetadataJSON; + +export const GetTokenMetadataOperation = { + operationId: "getTokenMetadata", + groupName: "token", + httpMethod: "get", + routePattern: "/token/{network}/{address}/metadata", + parameterNames: ["network","address"], + hasResponse: true, + hasBody: false, + + parseResponse(json: SolTokenMetadataJSON): SolTokenMetadata { + return SolTokenMetadata.fromJSON(json); + }, + + serializeRequest(request: GetTokenMetadataOperationRequest): GetTokenMetadataOperationRequestJSON { + const network = SolNetwork.create(request.network); + const address = SolAddress.create(request.address); + return { + network: network.toJSON(), + address: address.toJSON(), + }; + }, + +} diff --git a/packages/common/solUtils/src/generated/operations/index.ts b/packages/common/solUtils/src/generated/operations/index.ts index 209d4ce747..1801516a47 100644 --- a/packages/common/solUtils/src/generated/operations/index.ts +++ b/packages/common/solUtils/src/generated/operations/index.ts @@ -1,2 +1,3 @@ export * from './GetTokenPriceOperation'; +export * from './GetTokenMetadataOperation'; export * from './operations'; diff --git a/packages/common/solUtils/src/generated/operations/operations.ts b/packages/common/solUtils/src/generated/operations/operations.ts index bee536c2e3..fd0baf920f 100644 --- a/packages/common/solUtils/src/generated/operations/operations.ts +++ b/packages/common/solUtils/src/generated/operations/operations.ts @@ -1,5 +1,7 @@ import { GetTokenPriceOperation } from './GetTokenPriceOperation'; +import { GetTokenMetadataOperation } from './GetTokenMetadataOperation'; export const operations = [ GetTokenPriceOperation, + GetTokenMetadataOperation, ]; diff --git a/packages/common/solUtils/src/generated/types/SolGetTokenMetadataNetworkEnum.ts b/packages/common/solUtils/src/generated/types/SolGetTokenMetadataNetworkEnum.ts new file mode 100644 index 0000000000..2fd8406f12 --- /dev/null +++ b/packages/common/solUtils/src/generated/types/SolGetTokenMetadataNetworkEnum.ts @@ -0,0 +1,16 @@ +// $ref: #/paths/~1token~1{network}~1{address}~1metadata/get/parameters/0/schema +// typeName: getTokenMetadata_network_Enum + +export type SolGetTokenMetadataNetworkEnumJSON = "mainnet" | "devnet"; +export type SolGetTokenMetadataNetworkEnumInput = "mainnet" | "devnet"; +export type SolGetTokenMetadataNetworkEnumValue = "mainnet" | "devnet"; + +export abstract class SolGetTokenMetadataNetworkEnum { + public static create(input: SolGetTokenMetadataNetworkEnumInput | SolGetTokenMetadataNetworkEnumValue): SolGetTokenMetadataNetworkEnumValue { + return input; + } + + public static fromJSON(json: SolGetTokenMetadataNetworkEnumJSON): SolGetTokenMetadataNetworkEnumValue { + return json; + } +} diff --git a/packages/common/solUtils/src/generated/types/SolMetaplexToken.ts b/packages/common/solUtils/src/generated/types/SolMetaplexToken.ts new file mode 100644 index 0000000000..d353673817 --- /dev/null +++ b/packages/common/solUtils/src/generated/types/SolMetaplexToken.ts @@ -0,0 +1,75 @@ +// $ref: #/components/schemas/MetaplexToken +// type: MetaplexToken +// properties: +// - metadataUri ($ref: #/components/schemas/MetaplexToken/properties/metadataUri) +// - masterEdition ($ref: #/components/schemas/MetaplexToken/properties/masterEdition) +// - isMutable ($ref: #/components/schemas/MetaplexToken/properties/isMutable) +// - primarySaleHappened ($ref: #/components/schemas/MetaplexToken/properties/primarySaleHappened) +// - sellerFeeBasisPoints ($ref: #/components/schemas/MetaplexToken/properties/sellerFeeBasisPoints) +// - updateAuthority ($ref: #/components/schemas/MetaplexToken/properties/updateAuthority) + +export interface SolMetaplexTokenJSON { + readonly metadataUri: string; + readonly masterEdition: boolean; + readonly isMutable: boolean; + readonly primarySaleHappened: number; + readonly sellerFeeBasisPoints: number; + readonly updateAuthority: string; +} + +export interface SolMetaplexTokenInput { + readonly metadataUri: string; + readonly masterEdition: boolean; + readonly isMutable: boolean; + readonly primarySaleHappened: number; + readonly sellerFeeBasisPoints: number; + readonly updateAuthority: string; +} + +export class SolMetaplexToken { + public static create(input: SolMetaplexTokenInput | SolMetaplexToken): SolMetaplexToken { + if (input instanceof SolMetaplexToken) { + return input; + } + return new SolMetaplexToken(input); + } + + public static fromJSON(json: SolMetaplexTokenJSON): SolMetaplexToken { + const input: SolMetaplexTokenInput = { + metadataUri: json.metadataUri, + masterEdition: json.masterEdition, + isMutable: json.isMutable, + primarySaleHappened: json.primarySaleHappened, + sellerFeeBasisPoints: json.sellerFeeBasisPoints, + updateAuthority: json.updateAuthority, + }; + return SolMetaplexToken.create(input); + } + + public readonly metadataUri: string; + public readonly masterEdition: boolean; + public readonly isMutable: boolean; + public readonly primarySaleHappened: number; + public readonly sellerFeeBasisPoints: number; + public readonly updateAuthority: string; + + private constructor(input: SolMetaplexTokenInput) { + this.metadataUri = input.metadataUri; + this.masterEdition = input.masterEdition; + this.isMutable = input.isMutable; + this.primarySaleHappened = input.primarySaleHappened; + this.sellerFeeBasisPoints = input.sellerFeeBasisPoints; + this.updateAuthority = input.updateAuthority; + } + + public toJSON(): SolMetaplexTokenJSON { + return { + metadataUri: this.metadataUri, + masterEdition: this.masterEdition, + isMutable: this.isMutable, + primarySaleHappened: this.primarySaleHappened, + sellerFeeBasisPoints: this.sellerFeeBasisPoints, + updateAuthority: this.updateAuthority, + } + } +} diff --git a/packages/common/solUtils/src/generated/types/SolSPLTokenPrice.ts b/packages/common/solUtils/src/generated/types/SolSPLTokenPrice.ts index cb8bc50370..1e37de31b4 100644 --- a/packages/common/solUtils/src/generated/types/SolSPLTokenPrice.ts +++ b/packages/common/solUtils/src/generated/types/SolSPLTokenPrice.ts @@ -11,16 +11,16 @@ import { SolAddress, SolAddressInput, SolAddressJSON } from '../../dataTypes'; export interface SolSPLTokenPriceJSON { readonly nativePrice?: SolSPLNativePriceJSON; - readonly usdPrice: number; - readonly exchangeAddress: SolAddressJSON; - readonly exchangeName: string; + readonly usdPrice?: number; + readonly exchangeAddress?: SolAddressJSON; + readonly exchangeName?: string; } export interface SolSPLTokenPriceInput { readonly nativePrice?: SolSPLNativePriceInput | SolSPLNativePrice; - readonly usdPrice: number; - readonly exchangeAddress: SolAddressInput | SolAddress; - readonly exchangeName: string; + readonly usdPrice?: number; + readonly exchangeAddress?: SolAddressInput | SolAddress; + readonly exchangeName?: string; } export class SolSPLTokenPrice { @@ -35,21 +35,21 @@ export class SolSPLTokenPrice { const input: SolSPLTokenPriceInput = { nativePrice: json.nativePrice ? SolSPLNativePrice.fromJSON(json.nativePrice) : undefined, usdPrice: json.usdPrice, - exchangeAddress: SolAddress.fromJSON(json.exchangeAddress), + exchangeAddress: json.exchangeAddress ? SolAddress.fromJSON(json.exchangeAddress) : undefined, exchangeName: json.exchangeName, }; return SolSPLTokenPrice.create(input); } public readonly nativePrice?: SolSPLNativePrice; - public readonly usdPrice: number; - public readonly exchangeAddress: SolAddress; - public readonly exchangeName: string; + public readonly usdPrice?: number; + public readonly exchangeAddress?: SolAddress; + public readonly exchangeName?: string; private constructor(input: SolSPLTokenPriceInput) { this.nativePrice = input.nativePrice ? SolSPLNativePrice.create(input.nativePrice) : undefined; this.usdPrice = input.usdPrice; - this.exchangeAddress = SolAddress.create(input.exchangeAddress); + this.exchangeAddress = input.exchangeAddress ? SolAddress.create(input.exchangeAddress) : undefined; this.exchangeName = input.exchangeName; } @@ -57,7 +57,7 @@ export class SolSPLTokenPrice { return { nativePrice: this.nativePrice ? this.nativePrice.toJSON() : undefined, usdPrice: this.usdPrice, - exchangeAddress: this.exchangeAddress.toJSON(), + exchangeAddress: this.exchangeAddress ? this.exchangeAddress.toJSON() : undefined, exchangeName: this.exchangeName, } } diff --git a/packages/common/solUtils/src/generated/types/SolTokenMetadata.ts b/packages/common/solUtils/src/generated/types/SolTokenMetadata.ts new file mode 100644 index 0000000000..cd5fafd4cf --- /dev/null +++ b/packages/common/solUtils/src/generated/types/SolTokenMetadata.ts @@ -0,0 +1,70 @@ +import { SolMetaplexToken, SolMetaplexTokenInput, SolMetaplexTokenJSON } from '../types/SolMetaplexToken'; + +// $ref: #/components/schemas/TokenMetadata +// type: TokenMetadata +// properties: +// - mint ($ref: #/components/schemas/TokenMetadata/properties/mint) +// - standard ($ref: #/components/schemas/TokenMetadata/properties/standard) +// - name ($ref: #/components/schemas/TokenMetadata/properties/name) +// - symbol ($ref: #/components/schemas/TokenMetadata/properties/symbol) +// - metaplex ($ref: #/components/schemas/MetaplexToken) + +export interface SolTokenMetadataJSON { + readonly mint: string; + readonly standard: string; + readonly name: string; + readonly symbol: string; + readonly metaplex: SolMetaplexTokenJSON; +} + +export interface SolTokenMetadataInput { + readonly mint: string; + readonly standard: string; + readonly name: string; + readonly symbol: string; + readonly metaplex: SolMetaplexTokenInput | SolMetaplexToken; +} + +export class SolTokenMetadata { + public static create(input: SolTokenMetadataInput | SolTokenMetadata): SolTokenMetadata { + if (input instanceof SolTokenMetadata) { + return input; + } + return new SolTokenMetadata(input); + } + + public static fromJSON(json: SolTokenMetadataJSON): SolTokenMetadata { + const input: SolTokenMetadataInput = { + mint: json.mint, + standard: json.standard, + name: json.name, + symbol: json.symbol, + metaplex: SolMetaplexToken.fromJSON(json.metaplex), + }; + return SolTokenMetadata.create(input); + } + + public readonly mint: string; + public readonly standard: string; + public readonly name: string; + public readonly symbol: string; + public readonly metaplex: SolMetaplexToken; + + private constructor(input: SolTokenMetadataInput) { + this.mint = input.mint; + this.standard = input.standard; + this.name = input.name; + this.symbol = input.symbol; + this.metaplex = SolMetaplexToken.create(input.metaplex); + } + + public toJSON(): SolTokenMetadataJSON { + return { + mint: this.mint, + standard: this.standard, + name: this.name, + symbol: this.symbol, + metaplex: this.metaplex.toJSON(), + } + } +} diff --git a/packages/common/solUtils/src/generated/types/index.ts b/packages/common/solUtils/src/generated/types/index.ts index 305b2602f2..170f3ff451 100644 --- a/packages/common/solUtils/src/generated/types/index.ts +++ b/packages/common/solUtils/src/generated/types/index.ts @@ -1,3 +1,6 @@ export * from './SolGetTokenPriceNetworkEnum'; +export * from './SolGetTokenMetadataNetworkEnum'; export * from './SolSPLTokenPrice'; +export * from './SolTokenMetadata'; export * from './SolSPLNativePrice'; +export * from './SolMetaplexToken'; diff --git a/packages/common/solUtils/src/operations/openapi.ts b/packages/common/solUtils/src/operations/openapi.ts index c97915a572..2d313da00f 100644 --- a/packages/common/solUtils/src/operations/openapi.ts +++ b/packages/common/solUtils/src/operations/openapi.ts @@ -28,6 +28,10 @@ export interface paths { /** Gets the token price (usd and native) for a given contract address and network. */ get: operations["getTokenPrice"]; }; + "/token/{network}/{address}/metadata": { + /** Get the global token metadata for a given network and contract (mint, standard, name, symbol, metaplex). */ + get: operations["getTokenMetadata"]; + }; } export interface components { @@ -60,7 +64,7 @@ export interface components { metadataUri: string; masterEdition: boolean; isMutable: boolean; - primarySaleHappened: boolean; + primarySaleHappened: number; sellerFeeBasisPoints: number; updateAuthority: string; }; @@ -79,9 +83,24 @@ export interface components { }; SPLTokenPrice: { nativePrice?: components["schemas"]["SPLNativePrice"]; - usdPrice: number; - exchangeAddress: string; - exchangeName: string; + usdPrice?: number; + exchangeAddress?: string; + exchangeName?: string; + }; + MetaplexToken: { + metadataUri: string; + masterEdition: boolean; + isMutable: boolean; + primarySaleHappened: number; + sellerFeeBasisPoints: number; + updateAuthority: string; + }; + TokenMetadata: { + mint: string; + standard: string; + name: string; + symbol: string; + metaplex: components["schemas"]["MetaplexToken"]; }; }; } @@ -225,6 +244,29 @@ export interface operations { }; }; }; + /** Get the global token metadata for a given network and contract (mint, standard, name, symbol, metaplex). */ + getTokenMetadata: { + parameters: { + path: { + /** The network to query */ + network: "mainnet" | "devnet"; + /** The address of the contract */ + address: string; + }; + }; + responses: { + 200: { + content: { + "application/json": components["schemas"]["TokenMetadata"]; + }; + }; + 400: { + content: { + "application/json": string; + }; + }; + }; + }; } export interface external {} diff --git a/packages/solApi/integration/test/getTokenPrice.test.ts b/packages/solApi/integration/test/getTokenPrice.test.ts index 0b887dd7b5..b9a8ff2f4f 100644 --- a/packages/solApi/integration/test/getTokenPrice.test.ts +++ b/packages/solApi/integration/test/getTokenPrice.test.ts @@ -18,7 +18,7 @@ describe('Moralis SolApi', () => { address: '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R', }); - expect(result.exchangeAddress.address).toBe('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'); + expect(result.exchangeAddress?.address).toBe('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'); expect(result.exchangeName).toBe('Raydium'); expect(result.usdPrice).toBe(0.9965); expect(result.nativePrice?.decimals).toBe(9); diff --git a/packages/solApi/src/generated/ClientSolApi.ts b/packages/solApi/src/generated/ClientSolApi.ts index 1f45464726..f7bc72098c 100644 --- a/packages/solApi/src/generated/ClientSolApi.ts +++ b/packages/solApi/src/generated/ClientSolApi.ts @@ -1,6 +1,6 @@ // CAUTION: This file is automatically generated. Do not edit it manually! -import { getBalanceOperation, GetBalanceRequest, GetBalanceResponseAdapter, getNFTsOperation, GetNFTsRequest, GetNFTsResponseAdapter, getPortfolioOperation, GetPortfolioRequest, GetPortfolioResponseAdapter, getSPLOperation, GetSPLRequest, GetSPLResponseAdapter, getNFTMetadataOperation, GetNFTMetadataRequest, GetNFTMetadataResponseAdapter, GetTokenPriceOperationResponseJSON, GetTokenPriceOperation, GetTokenPriceOperationRequest, GetTokenPriceOperationResponse } from '@moralisweb3/common-sol-utils'; +import { getBalanceOperation, GetBalanceRequest, GetBalanceResponseAdapter, getNFTsOperation, GetNFTsRequest, GetNFTsResponseAdapter, getPortfolioOperation, GetPortfolioRequest, GetPortfolioResponseAdapter, getSPLOperation, GetSPLRequest, GetSPLResponseAdapter, getNFTMetadataOperation, GetNFTMetadataRequest, GetNFTMetadataResponseAdapter, GetTokenPriceOperationResponseJSON, GetTokenPriceOperation, GetTokenPriceOperationRequest, GetTokenPriceOperationResponse, GetTokenMetadataOperationResponseJSON, GetTokenMetadataOperation, GetTokenMetadataOperationRequest, GetTokenMetadataOperationResponse } from '@moralisweb3/common-sol-utils'; import { OperationResolver, OperationV3Resolver } from '@moralisweb3/api-utils'; import { ApiModule, ResponseAdapter } from '@moralisweb3/common-core'; export abstract class ClientSolApi extends ApiModule { @@ -32,6 +32,9 @@ export abstract class ClientSolApi extends ApiModule { getTokenPrice: (request: GetTokenPriceOperationRequest): Promise> => { return new OperationV3Resolver(GetTokenPriceOperation, this.baseUrl, this.core).fetch(request, null); }, + getTokenMetadata: (request: GetTokenMetadataOperationRequest): Promise> => { + return new OperationV3Resolver(GetTokenMetadataOperation, this.baseUrl, this.core).fetch(request, null); + }, };