@cheqd/sdk
Version:
A TypeScript SDK built with CosmJS to interact with the cheqd network ledger
597 lines • 28.6 kB
JavaScript
"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