Implement Custom Provider
Mesh offers options like Blockfrost or Koios (see Providers). These providers access the Cardano blockchain to retrieve data, such as smart contract UTXOs, or submit signed transactions.
Customize a provider to utilize GraphQL, cardano-cli, or websocket with Mesh SDK. Any query method works if the output conforms to the interface.
This guide demonstrates how to create a custom provider and integrate it with Mesh to work with the transaction builder.
How it works
JavaScript interfaces define the application structure and syntax for classes. Classes based on an interface must abide by its structure.
Providers implement one or more interfaces. For example, the KoiosProvider Class implements IFetcher and ISubmitter. It must strictly conform to their structures.
KoiosProvider implements IFetcher and ISubmitter using the implement keyword:
export class KoiosProvider implements IFetcher, ISubmitter {}Visit the GitHub repo at packages/module/src/common/contracts for the latest interfaces.
Create a custom provider class by creating functions with the same name, input parameters, and return type as the defined methods for each interface. This ensures compatibility with Mesh functions.
IFetcher has 6 functions (see packages/module/src/common/contracts/fetcher.ts):
import type { AccountInfo, AssetMetadata, Protocol, UTxO } from '@mesh/common/types';
export interface IFetcher {
fetchAccountInfo(address: string): Promise<AccountInfo>;
fetchAddressUTxOs(address: string, asset?: string): Promise<UTxO[]>;
fetchAssetAddresses(asset: string): Promise<{ address: string; quantity: string }[]>;
fetchAssetMetadata(asset: string): Promise<AssetMetadata>;
fetchHandleAddress(handle: string): Promise<string>;
fetchProtocolParameters(epoch: number): Promise<Protocol>;
}KoiosProvider must implement these functions as defined in IFetcher.
Implement your own provider
Review existing providers to get started. Visit packages/module/src/providers.
Use this codebase as a starting point:
import { IFetcher, ISubmitter } from "@mesh/common/contracts";
import { parseHttpError } from "@mesh/common/utils";
import type {
AccountInfo,
AssetMetadata,
Protocol,
UTxO,
} from "@mesh/common/types";
export class NAMEProvider implements IFetcher, ISubmitter {
constructor(network: "") {
// init variables and other Javascript libraries needed
}
async fetchAccountInfo(address: string): Promise<AccountInfo> {
try {
// return <AccountInfo>{
// ...
// };
} catch (error) {
throw parseHttpError(error);
}
}
async fetchAddressUTxOs(address: string, asset?: string): Promise<UTxO[]> {
try {
// return <UTxO[]>[
// ...
// ];
} catch (error) {
throw parseHttpError(error);
}
}
async fetchAssetAddresses(
asset: string
): Promise<{ address: string; quantity: string }[]> {
try {
// return AssetAddresses;
} catch (error) {
throw parseHttpError(error);
}
}
async fetchAssetMetadata(asset: string): Promise<AssetMetadata> {
try {
// return <AssetMetadata>[
// ...
// ];
} catch (error) {
throw parseHttpError(error);
}
}
async fetchHandleAddress(handle: string): Promise<string> {
try {
// return handleAddress;
} catch (error) {
throw parseHttpError(error);
}
}
async fetchProtocolParameters(epoch = Number.NaN): Promise<Protocol> {
try {
// return <Protocol>{
// ...
// };
} catch (error) {
throw parseHttpError(error);
}
}
async submitTx(tx: string): Promise<string> {
try {
// if (status === 200)
// return txHash;
} catch (error) {
throw parseHttpError(error);
}
}
}This code may change if the interface updates. Your provider may require different interfaces depending on its purpose.
Implement constructor and functions
Define the constructor.
A constructor creates and initializes a class. It runs when creating an object using the new keyword.
Providers usually require basic information, such as the network or API key.
KoiosProvider requires network and version (optional):
private readonly _axiosInstance: AxiosInstance;
constructor(network: 'api' | 'preview' | 'preprod' | 'guild', version = 0) {
this._axiosInstance = axios.create({
baseURL: `https://${network}.koios.rest/api/v${version}`,
});
}This constructor initializes the Axios instance with user-provided parameters.
Define each function required by the interface. Understand the following:
- Query method for the blockchain provider.
- Interface input parameters.
- Blockchain provider input parameters.
- Expected interface outputs.
- Blockchain provider return values.
Map data correctly from the blockchain provider to the interface's required data type.
For example, KoiosProvider implements fetchProtocolParameters() to map Koios responses to the Protocol data type:
async fetchProtocolParameters(epoch: number): Promise<Protocol> {
try {
const { data, status } = await this._axiosInstance.get(
`epoch_params?_epoch_no=${epoch}`,
);
if (status === 200)
return <Protocol>{
coinsPerUTxOSize: data[0].coins_per_utxo_size,
collateralPercent: data[0].collateral_percent,
decentralisation: data[0].decentralisation,
epoch: data[0].epoch_no,
keyDeposit: data[0].key_deposit,
maxBlockExMem: data[0].max_block_ex_mem.toString(),
maxBlockExSteps: data[0].max_block_ex_steps.toString(),
maxBlockHeaderSize: data[0].max_bh_size,
maxBlockSize: data[0].max_block_size,
maxCollateralInputs: data[0].max_collateral_inputs,
maxTxExMem: data[0].max_tx_ex_mem.toString(),
maxTxExSteps: data[0].max_tx_ex_steps.toString(),
maxTxSize: data[0].max_tx_size,
maxValSize: data[0].max_val_size.toString(),
minFeeA: data[0].min_fee_a,
minFeeB: data[0].min_fee_b,
minPoolCost: data[0].min_pool_cost,
poolDeposit: data[0].pool_deposit,
priceMem: data[0].price_mem,
priceStep: data[0].price_step,
};
throw parseHttpError(data);
} catch (error) {
throw parseHttpError(error);
}
}Implement every function specified by the interface and test them.
Create a pull request if your provider benefits the Cardano developer community.