UNPKG

@cheqd/sdk

Version:

A TypeScript SDK built with CosmJS to interact with the cheqd network ledger

597 lines 28.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResourceModule = exports.setupResourceExtension = exports.typeUrlMsgCreateResourceResponse = exports.typeUrlMsgCreateResource = exports.protobufLiterals = exports.defaultResourceExtensionKey = void 0; exports.isMsgCreateResourceEncodeObject = isMsgCreateResourceEncodeObject; const _1 = require("./_"); const signer_1 = require("../signer"); const proto_signing_cjs_1 = require("@cosmjs/proto-signing-cjs"); const types_1 = require("../types"); const index_1 = require("@cheqd/ts-proto-cjs/cheqd/resource/v2/index"); const stargate_cjs_1 = require("@cosmjs/stargate-cjs"); const file_type_cjs_1 = require("file-type-cjs"); const uint8arrays_cjs_1 = require("uint8arrays-cjs"); const utils_cjs_1 = require("@cosmjs/utils-cjs"); const utils_1 = require("../utils"); const oracle_1 = require("./oracle"); const coin_1 = require("cosmjs-types/cosmos/base/v1beta1/coin"); /** Default extension key for resource-related query operations */ exports.defaultResourceExtensionKey = 'resource'; /** * Protobuf message type literals for resource operations. * Used for consistent message type identification across the module. */ exports.protobufLiterals = { /** Create resource message type */ MsgCreateResource: 'MsgCreateResource', /** Create resource response message type */ MsgCreateResourceResponse: 'MsgCreateResourceResponse', }; /** Type URL for MsgCreateResource messages */ exports.typeUrlMsgCreateResource = `/${index_1.protobufPackage}.${exports.protobufLiterals.MsgCreateResource}`; /** Type URL for MsgCreateResourceResponse messages */ exports.typeUrlMsgCreateResourceResponse = `/${index_1.protobufPackage}.${exports.protobufLiterals.MsgCreateResourceResponse}`; /** * Type guard function to check if an object is a MsgCreateResourceEncodeObject. * * @param obj - EncodeObject to check * @returns True if the object is a MsgCreateResourceEncodeObject */ function isMsgCreateResourceEncodeObject(obj) { return obj.typeUrl === exports.typeUrlMsgCreateResource; } /** * Sets up the resource extension for the querier client. * Creates and configures the resource-specific query methods. * * @param base - Base QueryClient to extend * @returns Configured resource extension with query methods */ const setupResourceExtension = (base) => { const rpc = (0, stargate_cjs_1.createProtobufRpcClient)(base); const queryService = new index_1.QueryClientImpl(rpc); return { [exports.defaultResourceExtensionKey]: { resource: async (collectionId, resourceId) => { const { resource } = await queryService.Resource({ collectionId, id: resourceId }); (0, utils_cjs_1.assert)(resource); return resource; }, resourceMetadata: async (collectionId, resourceId) => { const { resource } = await queryService.ResourceMetadata({ collectionId, id: resourceId }); (0, utils_cjs_1.assert)(resource); return resource; }, collectionResources: async (collectionId, paginationKey) => { const response = await queryService.CollectionResources({ collectionId, pagination: (0, stargate_cjs_1.createPagination)(paginationKey), }); return response; }, latestResourceVersion: async (collectionId, name, type) => { const { resource } = await queryService.LatestResourceVersion({ collectionId, name, resourceType: type, }); (0, utils_cjs_1.assert)(resource); return resource; }, latestResourceVersionMetadata: async (collectionId, name, type) => { const { resource } = await queryService.LatestResourceVersionMetadata({ collectionId, name, resourceType: type, }); (0, utils_cjs_1.assert)(resource); return resource; }, params: async () => { const response = await queryService.Params({}); (0, utils_cjs_1.assert)(response.params); return response; }, }, }; }; exports.setupResourceExtension = setupResourceExtension; /** * Resource Module class providing comprehensive linked resource functionality. * Handles creation, querying, and metadata management of resources linked to DID documents. */ class ResourceModule extends _1.AbstractCheqdSDKModule { // @ts-expect-error underlying type `GeneratedType` is intentionally wider static registryTypes = [ [exports.typeUrlMsgCreateResource, index_1.MsgCreateResource], [exports.typeUrlMsgCreateResourceResponse, index_1.MsgCreateResourceResponse], ]; /** Base denomination for Cheqd network transactions */ static baseMinimalDenom = 'ncheq'; /** Base denomination in USD for Cheqd network transactions */ static baseUsdDenom = 'usd'; /** Default slippage tolerance in base points (BPS) */ static defaultSlippageBps = 500n; /** * Standard fee amounts for different resource types. * Fees vary based on resource content type and processing requirements. */ static fees = { /** Default fee for creating image resources */ DefaultCreateResourceImageFee: { amount: '10000000000', denom: ResourceModule.baseMinimalDenom }, /** Default fee for creating JSON resources */ DefaultCreateResourceJsonFee: { amount: '10000000000', denom: ResourceModule.baseMinimalDenom }, /** Default fee for creating other types of resources */ DefaultCreateResourceDefaultFee: { amount: '10000000000', denom: ResourceModule.baseMinimalDenom }, /** Default fee for creating image resources in USD */ DefaultCreateResourceImageFeeUsd: { amount: '100000000000000000', denom: ResourceModule.baseUsdDenom }, /** Default fee for creating JSON resources in USD */ DefaultCreateResourceJsonFeeUsd: { amount: '400000000000000000', denom: ResourceModule.baseUsdDenom }, /** Default fee for creating other types of resources in USD */ DefaultCreateResourceDefaultFeeUsd: { amount: '200000000000000000', denom: ResourceModule.baseUsdDenom, }, }; /** Standard gas limits for DLR operations. * These represent the default gas limits for various DLR-related transactions. */ static gasLimits = { /** Gas limit for creating linked resources */ CreateLinkedResourceImageGasLimit: '1000000', /** Gas limit for creating linked JSON resources */ CreateLinkedResourceJsonGasLimit: '1000000', /** Gas limit for creating other types of linked resources */ CreateLinkedResourceDefaultGasLimit: '1000000', }; /** Querier extension setup function for resource operations */ static querierExtensionSetup = exports.setupResourceExtension; /** Querier instance with resource extension capabilities */ querier; oracleFeesAvailability; /** * Constructs a new resource module instance. * * @param signer - Signing client for blockchain transactions * @param querier - Querier client with resource extension for data retrieval */ constructor(signer, querier) { super(signer, querier); this.querier = querier; this.methods = { createLinkedResourceTx: this.createLinkedResourceTx.bind(this), queryLinkedResource: this.queryLinkedResource.bind(this), queryLinkedResourceMetadata: this.queryLinkedResourceMetadata.bind(this), queryLinkedResources: this.queryLinkedResources.bind(this), queryLatestLinkedResourceVersion: this.queryLatestLinkedResourceVersion.bind(this), queryLatestLinkedResourceVersionMetadata: this.queryLatestLinkedResourceVersionMetadata.bind(this), queryResourceParams: this.queryResourceParams.bind(this), generateCreateResourceImageFees: this.generateCreateResourceImageFees.bind(this), generateCreateResourceJsonFees: this.generateCreateResourceJsonFees.bind(this), generateCreateResourceDefaultFees: this.generateCreateResourceDefaultFees.bind(this), getPriceRangeFromResourceParams: this.getPriceRangeFromResourceParams.bind(this), }; } /** * Gets the registry types for resource message encoding/decoding. * * @returns Iterable of [typeUrl, GeneratedType] pairs for the registry */ getRegistryTypes() { return ResourceModule.registryTypes; } /** * Gets the querier extension setup for resource operations. * * @returns Query extension setup function for resource functionality */ getQuerierExtensionSetup() { return ResourceModule.querierExtensionSetup; } /** * Signs a resource payload with provided signature inputs. * Creates a complete signed resource message ready for blockchain submission. * * @param payload - Resource payload to sign * @param signInputs - Signing inputs or pre-computed signatures * @returns Promise resolving to the signed resource message */ static async signPayload(payload, signInputs) { const signBytes = index_1.MsgCreateResourcePayload.encode(payload).finish(); let signatures; if (types_1.ISignInputs.isSignInput(signInputs)) { signatures = await signer_1.CheqdSigningStargateClient.signIdentityTx(signBytes, signInputs); } else { signatures = signInputs; } return { payload, signatures, }; } /** * Creates a linked resource transaction on the blockchain. * Handles automatic fee calculation based on resource content type and size. * * @param signInputs - Signing inputs or pre-computed signatures for the transaction * @param resourcePayload - Resource payload containing data and metadata * @param address - Address of the account submitting the transaction * @param fee - Transaction fee configuration or 'auto' for automatic calculation * @param memo - Optional transaction memo * @param feeOptions - Optional fee options for the transaction * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the transaction response * @throws Error if linked resource data is empty when automatic fee calculation is requested */ async createLinkedResourceTx(signInputs, resourcePayload, address, fee, memo, feeOptions, context) { if (!this._signer) { this._signer = context.sdk.signer; } const payload = index_1.MsgCreateResourcePayload.fromPartial(resourcePayload); const msg = await ResourceModule.signPayload(payload, signInputs); const encObj = { typeUrl: exports.typeUrlMsgCreateResource, value: msg, }; if (address === '') { address = (await context.sdk.options.wallet.getAccounts())[0].address; } if (!fee) { if (payload.data.length === 0) { throw new Error('Linked resource data is empty'); } fee = await (async function (that) { const mimeType = await ResourceModule.readMimeType(payload.data); if (mimeType.startsWith('image/')) { return await that.generateCreateResourceImageFees(address, undefined, feeOptions, context); } if (mimeType.startsWith('application/json')) { return await that.generateCreateResourceJsonFees(address, undefined, feeOptions, context); } return await that.generateCreateResourceDefaultFees(address, undefined, feeOptions, context); })(this); } return this._signer.signAndBroadcast(address, [encObj], fee, memo); } /** * Queries a specific linked resource by collection and resource ID. * Retrieves the complete resource data along with its metadata. * * @param collectionId - ID of the collection containing the resource * @param resourceId - ID of the resource to query * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the resource with metadata */ async queryLinkedResource(collectionId, resourceId, context) { if (!this.querier) { this.querier = context.sdk.querier; } return await this.querier[exports.defaultResourceExtensionKey].resource(collectionId, resourceId); } /** * Queries metadata for a specific linked resource. * Retrieves only the metadata without the resource content data. * * @param collectionId - ID of the collection containing the resource * @param resourceId - ID of the resource to query metadata for * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the resource metadata */ async queryLinkedResourceMetadata(collectionId, resourceId, context) { if (!this.querier) { this.querier = context.sdk.querier; } return await this.querier[exports.defaultResourceExtensionKey].resourceMetadata(collectionId, resourceId); } /** * Queries all resources in a collection with pagination support. * Retrieves a list of all resources belonging to a specific collection. * * @param collectionId - ID of the collection to query resources for * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the collection resources response with pagination */ async queryLinkedResources(collectionId, context) { if (!this.querier) { this.querier = context.sdk.querier; } return await this.querier[exports.defaultResourceExtensionKey].collectionResources(collectionId); } /** * Queries the latest version of a linked resource by collection ID, name, and type. * Retrieves the most recent version of the specified resource. * * @param collectionId - ID of the collection containing the resource * @param name - Name of the resource * @param type - Type of the resource * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the latest resource version with metadata */ async queryLatestLinkedResourceVersion(collectionId, name, type, context) { if (!this.querier) { this.querier = context.sdk.querier; } return await this.querier[exports.defaultResourceExtensionKey].latestResourceVersion(collectionId, name, type); } /** * Queries the latest metadata of a linked resource by collection ID, name, and type. * Retrieves only the metadata of the most recent version of the specified resource. * * @param collectionId - ID of the collection containing the resource * @param name - Name of the resource * @param type - Type of the resource * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the latest resource metadata */ async queryLatestLinkedResourceVersionMetadata(collectionId, name, type, context) { if (!this.querier) { this.querier = context.sdk.querier; } return await this.querier[exports.defaultResourceExtensionKey].latestResourceVersionMetadata(collectionId, name, type); } async shouldUseOracleFees(context) { if (!this.oracleFeesAvailability) { this.oracleFeesAvailability = (async () => { if (!this.querier && context?.sdk?.querier) { this.querier = context.sdk.querier; } const oracle = this.querier?.[oracle_1.defaultOracleExtensionKey]; if (!oracle?.queryParams) { return false; } try { await oracle.queryParams(); return true; } catch { return false; } })(); } return this.oracleFeesAvailability; } /** * Queries the Resource module parameters from the blockchain. * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the Resource module parameters */ async queryResourceParams(context) { if (!this.querier) { this.querier = context.sdk.querier; } return await this.querier[exports.defaultResourceExtensionKey].params(); } /** * Generates oracle-powered fees for creating image DID-Linked Resources. * * @param feePayer - Address of the account that will pay the transaction fees * @param granter - Optional address of the account granting fee payment permissions * @param feeOptions - Options for fetching oracle fees * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the fee configuration for DLR creation */ async generateCreateResourceImageFees(feePayer, granter, feeOptions, context) { if (!this.querier) { this.querier = context.sdk.querier; } if (!(await this.shouldUseOracleFees(context))) { return ResourceModule.generateCreateResourceImageFees(feePayer, granter); } // fetch fee parameters from the Resource module const feeParams = await this.queryResourceParams(context); // get the price range for the image resource creation const priceRange = await this.getPriceRangeFromResourceParams(feeParams, 'image', feeOptions); // calculate the oracle fee amount based on the price range and options return { amount: [await this.calculateOracleFeeAmount(priceRange, feeOptions, context)], gas: ResourceModule.gasLimits.CreateLinkedResourceImageGasLimit, payer: feePayer, granter, }; } /** * Generates oracle-powered fees for creating JSON DID-Linked Resources. * * @param feePayer - Address of the account that will pay the transaction fees * @param granter - Optional address of the account granting fee payment permissions * @param feeOptions - Options for fetching oracle fees * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the fee configuration for DLR creation */ async generateCreateResourceJsonFees(feePayer, granter, feeOptions, context) { if (!this.querier) { this.querier = context.sdk.querier; } if (!(await this.shouldUseOracleFees(context))) { return ResourceModule.generateCreateResourceJsonFees(feePayer, granter); } // fetch fee parameters from the Resource module const feeParams = await this.queryResourceParams(context); // get the price range for the JSON resource creation const priceRange = await this.getPriceRangeFromResourceParams(feeParams, 'json', feeOptions); // calculate the oracle fee amount based on the price range and options return { amount: [await this.calculateOracleFeeAmount(priceRange, feeOptions, context)], gas: ResourceModule.gasLimits.CreateLinkedResourceJsonGasLimit, payer: feePayer, granter, }; } /** * Generates oracle-powered fees for creating default DID-Linked Resources. * * @param feePayer - Address of the account that will pay the transaction fees * @param granter - Optional address of the account granting fee payment permissions * @param feeOptions - Options for fetching oracle fees * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the fee configuration for DLR creation */ async generateCreateResourceDefaultFees(feePayer, granter, feeOptions, context) { if (!this.querier) { this.querier = context.sdk.querier; } if (!(await this.shouldUseOracleFees(context))) { return ResourceModule.generateCreateResourceDefaultFees(feePayer, granter); } // fetch fee parameters from the Resource module const feeParams = await this.queryResourceParams(context); // get the price range for the default resource creation const priceRange = await this.getPriceRangeFromResourceParams(feeParams, 'default', feeOptions); // calculate the oracle fee amount based on the price range and options return { amount: [await this.calculateOracleFeeAmount(priceRange, feeOptions, context)], gas: ResourceModule.gasLimits.CreateLinkedResourceDefaultGasLimit, payer: feePayer, granter, }; } /** * Gets the fee range for a specific DID operation from the module parameters. * @param feeParams - DID module fee parameters * @param operation - DID operation type ('create', 'update', 'deactivate') * @param feeOptions - Options for fee retrieval * @returns Promise resolving to the fee range for the specified operation */ async getPriceRangeFromResourceParams(feeParams, operation, feeOptions) { const operationFees = (() => { switch (operation) { case 'image': return feeParams.params?.image; case 'json': return feeParams.params?.json; case 'default': return feeParams.params?.default; default: throw new Error('Unsupported operation for fee retrieval'); } })(); const operationFee = feeOptions?.feeDenom ? operationFees?.find((fee) => fee.denom === feeOptions.feeDenom) : (operationFees?.find((fee) => fee.denom === ResourceModule.baseUsdDenom) ?? operationFees?.find((fee) => fee.denom === ResourceModule.baseMinimalDenom)); if (!operationFee) { throw new Error(`Fee parameters not found for operation: ${operation}`); } return operationFee; } /** * Calculates the oracle fee amount based on the provided fee range and options. * @param feeRange - Fee range for the DID operation * @param feeOptions - Options for fee calculation * @param context - Optional SDK context for accessing clients * @returns Promise resolving to the calculated fee amount as a Coin */ async calculateOracleFeeAmount(feeRange, feeOptions, context) { if (!this.querier) { this.querier = context.sdk.querier; } if (feeRange.denom !== feeOptions?.feeDenom && feeOptions?.feeDenom !== undefined) { throw new Error(`Fee denomination mismatch: expected ${feeRange.denom}, got ${feeOptions.feeDenom}`); } const wantedFeeAmount = feeRange.denom === ResourceModule.baseUsdDenom ? (feeOptions?.wantedAmountUsd ?? ResourceModule.isFixedRange(feeRange)) ? feeRange.minAmount : feeRange.minAmount : feeRange.minAmount; // override fee options, if unassigned - case: moving average type feeOptions = { ...feeOptions, movingAverageType: feeOptions?.movingAverageType || oracle_1.MovingAverages.WMA, }; // override fee options, if unassigned - case: WMA strategy feeOptions = { ...feeOptions, wmaStrategy: feeOptions?.wmaStrategy || feeOptions?.movingAverageType === oracle_1.MovingAverages.WMA ? oracle_1.WMAStrategies.BALANCED : undefined, }; const convertedFeeAmount = feeRange.denom === ResourceModule.baseUsdDenom ? (0, proto_signing_cjs_1.parseCoins)((await this.querier[oracle_1.defaultOracleExtensionKey].convertUSDtoCHEQ(wantedFeeAmount, feeOptions?.movingAverageType, feeOptions?.wmaStrategy, feeOptions?.wmaWeights?.map((w) => BigInt(w)))).amount)[0] : coin_1.Coin.fromPartial({ amount: wantedFeeAmount, denom: feeRange.denom }); return feeOptions?.slippageBps ? ResourceModule.applySlippageToCoin(convertedFeeAmount, feeOptions.slippageBps) : convertedFeeAmount; } /** * Applies slippage to a given coin amount based on the specified basis points. * @param coin - Coin amount to apply slippage to * @param slippageBps - Slippage in basis points (bps) * @returns Coin with adjusted amount after applying slippage */ static applySlippageToCoin(coin, slippageBps) { const base = BigInt(coin.amount); const delta = (base * BigInt(slippageBps)) / BigInt(10_000); const adjustedAmount = base + delta; return coin_1.Coin.fromPartial({ amount: adjustedAmount.toString(), denom: coin.denom }); } /** * Checks if a fee range represents a fixed fee (minAmount equals maxAmount). * @param feeRange - Fee range to check * @returns True if the fee range is fixed, false otherwise */ static isFixedRange(feeRange) { return feeRange.minAmount === feeRange.maxAmount; } /** * Reads and determines the MIME type of resource content. * Analyzes the content to identify the appropriate MIME type for fee calculation and validation. * * @param content - Resource content as byte array * @returns Promise resolving to the detected MIME type string */ static async readMimeType(content) { if ((0, utils_1.isJSON)((0, uint8arrays_cjs_1.toString)(content, 'utf-8'))) return 'application/json'; return (await (0, file_type_cjs_1.fromBuffer)(content))?.mime ?? 'application/octet-stream'; } /** * Generates fee configuration for image resource creation transactions. * Uses higher gas limits appropriate for image processing and storage. * * @param feePayer - Address of the account that will pay the transaction fees * @param granter - Optional address of the account granting fee payment permissions * @returns Promise resolving to the fee configuration for image resources */ static async generateCreateResourceImageFees(feePayer, granter) { return { amount: [ResourceModule.fees.DefaultCreateResourceImageFee], gas: ResourceModule.gasLimits.CreateLinkedResourceImageGasLimit, payer: feePayer, granter: granter, }; } /** * Generates fee configuration for JSON resource creation transactions. * Uses gas limits optimized for JSON data processing and validation. * * @param feePayer - Address of the account that will pay the transaction fees * @param granter - Optional address of the account granting fee payment permissions * @returns Promise resolving to the fee configuration for JSON resources */ static async generateCreateResourceJsonFees(feePayer, granter) { return { amount: [ResourceModule.fees.DefaultCreateResourceJsonFee], gas: ResourceModule.gasLimits.CreateLinkedResourceJsonGasLimit, payer: feePayer, granter: granter, }; } /** * Generates fee configuration for default resource creation transactions. * Uses standard gas limits for generic resource types and binary data. * * @param feePayer - Address of the account that will pay the transaction fees * @param granter - Optional address of the account granting fee payment permissions * @returns Promise resolving to the fee configuration for default resources */ static async generateCreateResourceDefaultFees(feePayer, granter) { return { amount: [ResourceModule.fees.DefaultCreateResourceDefaultFee], gas: ResourceModule.gasLimits.CreateLinkedResourceDefaultGasLimit, payer: feePayer, granter: granter, }; } } exports.ResourceModule = ResourceModule; //# sourceMappingURL=resource.js.map