Add Modules
When a new module is added to XPLA Chain core, you must add it to several places in xpla.js too. To add a module, complete the following steps:
Create a New Folder
In the src/core
folder, create a new folder and name it after the new module. For example,src/core/greeting
.
Add Messages
- Register new messages in a subdirectory in the folder that you created for your new module, as shown in the following example:
src/core/greeting/msgs
For this example, let’s assume that you are creating two new messages, MsgHello
and MsgGoodbye
. The following example shows the code for MsgHello
, and you can extrapolate from it the way to implement MsgGoodbye
.
src/core/greeting/msgs/MsgHello.ts
import { JSONSerializable } from "../../../util/json";
import { AccAddress } from "../../strings";
// Just a simple greeting on the blockchain.
export class MsgHello extends JSONSerializable<MsgHello.Data> {
constructor(public recipient: AccAddress) {
super();
}
public static fromData(data: MsgHello.Data): MsgHello {
const {
value: { recipient },
} = data;
return new MsgHello(recipient);
}
public toData(): MsgHello.Data {
const { recipient } = this;
return {
type: "greeting/MsgHello",
value: {
recipient,
},
};
}
}
export namespace MsgHello {
export interface Data {
type: "greeting/MsgHello";
value: {
recipient: AccAddress;
};
}
}
- Create the following file, which will index your new messages.
src/core/greeting/msgs/index.ts
import { MsgHello } from "./MsgHello";
import { MsgGoodbye } from "./MsgGoodbye";
export * from "./MsgHello";
export * from "./MsgGoodbye";
export type GreetingMsg = MsgHello | MsgGoodbye;
export namespace GreetingMsg {
export type Data = MsgHello.Data | MsgGoodbye.Data;
}
- Register the messages in
src/core/Msg.ts
so that they can be parsed correctly.
src/core/Msg.ts
// import greeting module messages
...
import {
MsgHello,
MsgGoodbye,
GreetingMsg
} from './greeting/msgs';
...
// register GreetingMsg
export type Msg =
| BankMsg
| DistributionMsg
| GovMsg
| GreetingMsg // ADD HERE
| MsgAuthMsg
| SlashingMsg
| StakingMsg
| WasmMsg;
...
// register GreetingMsg.Data
export namespace Msg {
export type Data =
| BankMsg.Data
| DistributionMsg.Data
| GovMsg.Data
| Greeting.Data // ADD HERE
| MsgAuthMsg.Data
| SlashingMsg.Data
| StakingMsg.Data
| WasmMsg.Data;
...
// register deserializer in Msg.fromData(...)
export function fromData(data: Msg.Data): Msg {
...
// greeting
case 'greeting/MsgHello':
return MsgHello.fromData(data);
case 'greeting/MsgGoodbye':
return MsgGoodbye.fromData(data);
...
}
}
- Register the messages to be exported in
src/core/index.ts
:
...
// greeting
export 'greeting/msgs';
- Add parameter changes.
xpla.js provides an easy way to generate ParameterChangeProposal
s, which is a proposal for changing the blockchain parameters associated with a module. If your module has parameters that can be changed via proposal, you should create the following files:
src/core/greeting/params.ts
import { ParamChange } from "..";
import { Convert } from "../../util/convert";
type MaxHellos = ParamChange.Type<"greeting", "maxhellos", number>;
type MaxGoodbyes = ParamChange.Type<"greeting", "maxgoodbyes", number>;
export type GreetingParamChange = MaxHellos | MaxGoodbyes;
export namespace GreetingParamChange {
export type Data =
| ParamChange.Data.Type<MaxHellos>
| ParamChange.Data.Type<MaxGoodbyes>;
}
export interface GreetingParamChanges {
greeting?: {
maxhellos?: number;
maxgoodbyes?: number;
};
}
export namespace GreetingParamChanges {
export const ConversionTable = {
greeting: {
maxhellos: [Convert.toNumber, Convert.toFixed],
maxgoodbyes: [Convert.toNumber, Convert.toFixed],
},
};
}
- Register parameter change types
src/core/params/ParamChange.ts
...
import { GreetingParamChange, GreetingParamChanges } from '../greeting/params';
...
export type ParamChanges = DistributionParamChanges &
GovParamChanges &
GreetingParamChanges & // ADD HERE
SlashingParamChanges &
StakingParamChanges &
WasmParamChanges;
export namespace ParamChanges {
export const ConversionTable = {
...DistributionParamChanges.ConversionTable,
...GovParamChanges.ConversionTable,
...GreetingParamChanges.ConverstionTable, // ADD HERE
...SlashingParamChanges.ConversionTable,
...StakingParamChanges.ConversionTable,
...WasmParamChanges.ConversionTable,
};
...
export type ParamChange =
| DistributionParamChange
| GovParamChange
| GreetingParamChange // ADD HERE
| SlashingParamChange
| StakingParamChange
| WasmParamChange;
...
Add API Functionality to the LCDClient
If there are API endpoints that exist for the new module, you will need to add this functionality to LCDClient
so that they are accessible.
Assume that the greeting
module has the following endpoints:
GET /greeting/hello/{accAddress}
GET /greeting/parameters
- Create
src/client/lcd/api/GreetingAPI.ts
with the following:
import { BaseAPI } from "./BaseAPI";
import { AccAddress } from "../../../core/strings";
export interface GreetingParams {
max_hellos: number;
max_goodbyes: number;
}
export namespace GreetingParams {
export interface Data {
max_hellos: string;
max_goodbyes: string;
}
}
export class GreetingAPI extends BaseAPI {
public async hello(accAddress: AccAddress): Promise<AccAddress[]> {
return this.c
.get<AccAddress[]>(`/greeting/hello/${accAddress}`)
.then((d) => d.result);
}
public async parameters(): Promise<GreetingParams> {
return this.c
.get<GreetingParams.Data>(`/greeting/parameters`)
.then((d) => d.result)
.then((d) => ({
max_hellos: Number.parseInt(d.max_hellos),
max_goodbyes: Number.parseInt(d.max_goodbyes),
}));
}
}
- Register the API functionality inside
src/client/lcd/api/index.ts
:
export * from "./AuthAPI";
export * from "./BankAPI";
export * from "./DistributionAPI";
export * from "./GovAPI";
export * from "./GreetingAPI"; // ADD HERE
export * from "./MsgAuthAPI";
export * from "./SlashingAPI";
export * from "./StakingAPI";
export * from "./SupplyAPI";
export * from "./TendermintAPI";
export * from "./TxAPI";
export * from "./WasmAPI";
- Add the functionality to
src/client/lcd/LCDClient.ts
:
...
import {
AuthAPI,
BankAPI,
DistributionAPI,
GovAPI,
GreetingAPI, // ADD HERE
MsgAuthAPI,
SlashingAPI,
StakingAPI,
SupplyAPI,
TendermintAPI,
TxAPI,
WasmAPI,
} from './api';
...
export class LCDClient {
public config: LCDClientConfig;
public apiRequester: APIRequester;
// API access
public auth: AuthAPI;
public bank: BankAPI;
public distribution: DistributionAPI;
public gov: GovAPI;
public greeting: GreetingAPI; // ADD HERE
public msgauth: MsgAuthAPI;
public slashing: SlashingAPI;
public staking: StakingAPI;
public supply: SupplyAPI;
public tendermint: TendermintAPI;
public wasm: WasmAPI;
public tx: TxAPI;
//
// Creates a new LCD client with the specified configuration.
//
// @param config LCD configuration
//
constructor(config: LCDClientConfig) {
this.config = {
...DEFAULT_LCD_OPTIONS,
...config,
};
this.apiRequester = new APIRequester(this.config.URL);
// instantiate APIs
this.auth = new AuthAPI(this.apiRequester);
this.bank = new BankAPI(this.apiRequester);
this.distribution = new DistributionAPI(this.apiRequester);
this.gov = new GovAPI(this.apiRequester);
this.greeting = new GreetingAPI(this.apiRequester); // ADD HERE
this.msgauth = new MsgAuthAPI(this.apiRequester);
this.slashing = new SlashingAPI(this.apiRequester);
this.staking = new StakingAPI(this.apiRequester);
this.supply = new SupplyAPI(this.apiRequester);
this.tendermint = new TendermintAPI(this.apiRequester);
this.wasm = new WasmAPI(this.apiRequester);
this.tx = new TxAPI(this);
}