var img = document.createElement('img'); img.src = "https://terradocs.matomo.cloud//piwik.php?idsite=1&rec=1&url=https://docs.terra.money" + location.pathname; img.style = "border:0"; img.alt = "tracker"; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(img,s);
Skip to main content

Add modules

When a new module is added to Terra Core, you must also add it to several places in feather.js. 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

  1. 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 a 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


_37
import { JSONSerializable } from '../../../util/json';
_37
import { AccAddress } from '../../strings';
_37
_37
/**
_37
* Just a simple greeting on the blockchain.
_37
*/
_37
export class MsgHello extends JSONSerializable<MsgHello.Data> {
_37
constructor(public recipient: AccAddress) {
_37
super();
_37
}
_37
_37
public static fromData(data: MsgHello.Data): MsgHello {
_37
const {
_37
value: { recipient },
_37
} = data;
_37
return new MsgHello(recipient);
_37
}
_37
_37
public toData(): MsgHello.Data {
_37
const { recipient } = this;
_37
return {
_37
type: 'greeting/MsgHello',
_37
value: {
_37
recipient,
_37
},
_37
};
_37
}
_37
}
_37
_37
export namespace MsgHello {
_37
export interface Data {
_37
type: 'greeting/MsgHello';
_37
value: {
_37
recipient: AccAddress;
_37
};
_37
}
_37
}

  1. Create the following file, which will index your new messages.

src/core/greeting/msgs/index.ts


_11
import { MsgHello } from './MsgHello';
_11
import { MsgGoodbye } from './MsgGoodbye';
_11
_11
export * from './MsgHello';
_11
export * from './MsgGoodbye';
_11
_11
export type GreetingMsg = MsgHello | MsgGoodbye;
_11
_11
export namespace GreetingMsg {
_11
export type Data = MsgHello.Data | MsgGoodbye.Data;
_11
}

  1. Register the messages in src/core/Msg.ts so that they can be parsed correctly.

src/core/Msg.ts


_46
// import greeting module messages
_46
...
_46
import {
_46
MsgHello,
_46
MsgGoodbye,
_46
GreetingMsg
_46
} from './greeting/msgs';
_46
...
_46
_46
// register GreetingMsg
_46
export type Msg =
_46
| BankMsg
_46
| DistributionMsg
_46
| GovMsg
_46
| GreetingMsg // ADD HERE
_46
| MsgAuthMsg
_46
| SlashingMsg
_46
| StakingMsg
_46
| WasmMsg;
_46
_46
...
_46
_46
// register GreetingMsg.Data
_46
export namespace Msg {
_46
export type Data =
_46
| BankMsg.Data
_46
| DistributionMsg.Data
_46
| GovMsg.Data
_46
| Greeting.Data // ADD HERE
_46
| MsgAuthMsg.Data
_46
| SlashingMsg.Data
_46
| StakingMsg.Data
_46
| WasmMsg.Data;
_46
...
_46
_46
// register deserializer in Msg.fromData(...)
_46
export function fromData(data: Msg.Data): Msg {
_46
...
_46
// greeting
_46
case 'greeting/MsgHello':
_46
return MsgHello.fromData(data);
_46
case 'greeting/MsgGoodbye':
_46
return MsgGoodbye.fromData(data);
_46
...
_46
}
_46
}

  1. Register the messages to be exported in src/core/index.ts:

_3
...
_3
// greeting
_3
export 'greeting/msgs';

  1. Add parameter changes.

feather.js provides an easy way to generate ParameterChangeProposals, 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


_30
import { ParamChange } from '..';
_30
import { Convert } from '../../util/convert';
_30
_30
type MaxHellos = ParamChange.Type<'greeting', 'maxhellos', number>;
_30
_30
type MaxGoodbyes = ParamChange.Type<'greeting', 'maxgoodbyes', number>;
_30
_30
export type GreetingParamChange = MaxHellos | MaxGoodbyes;
_30
_30
export namespace GreetingParamChange {
_30
export type Data =
_30
| ParamChange.Data.Type<MaxHellos>
_30
| ParamChange.Data.Type<MaxGoodbyes>;
_30
}
_30
_30
export interface GreetingParamChanges {
_30
greeting?: {
_30
maxhellos?: number;
_30
maxgoodbyes?: number;
_30
};
_30
}
_30
_30
export namespace GreetingParamChanges {
_30
export const ConversionTable = {
_30
greeting: {
_30
maxhellos: [Convert.toNumber, Convert.toFixed],
_30
maxgoodbyes: [Convert.toNumber, Convert.toFixed],
_30
},
_30
};
_30
}

  1. Register parameter change types

src/core/params/ParamChange.ts


_32
...
_32
import { GreetingParamChange, GreetingParamChanges } from '../greeting/params';
_32
...
_32
_32
export type ParamChanges = DistributionParamChanges &
_32
GovParamChanges &
_32
GreetingParamChanges & // ADD HERE
_32
SlashingParamChanges &
_32
StakingParamChanges &
_32
WasmParamChanges;
_32
_32
export namespace ParamChanges {
_32
export const ConversionTable = {
_32
...DistributionParamChanges.ConversionTable,
_32
...GovParamChanges.ConversionTable,
_32
...GreetingParamChanges.ConverstionTable, // ADD HERE
_32
...SlashingParamChanges.ConversionTable,
_32
...StakingParamChanges.ConversionTable,
_32
...WasmParamChanges.ConversionTable,
_32
};
_32
_32
...
_32
_32
export type ParamChange =
_32
| DistributionParamChange
_32
| GovParamChange
_32
| GreetingParamChange // ADD HERE
_32
| SlashingParamChange
_32
| StakingParamChange
_32
| WasmParamChange;
_32
_32
...

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
  1. Create src/client/lcd/api/GreetingAPI.ts with the following:

_32
import { BaseAPI } from './BaseAPI';
_32
import { AccAddress } from '../../../core/strings';
_32
_32
export interface GreetingParams {
_32
max_hellos: number;
_32
max_goodbyes: number;
_32
}
_32
_32
export namespace GreetingParams {
_32
export interface Data {
_32
max_hellos: string;
_32
max_goodbyes: string;
_32
}
_32
}
_32
_32
export class GreetingAPI extends BaseAPI {
_32
public async hello(accAddress: AccAddress): Promise<AccAddress[]> {
_32
return this.c
_32
.get<AccAddress[]>(`/greeting/hello/${accAddress}`)
_32
.then((d) => d.result);
_32
}
_32
_32
public async parameters(): Promise<GreetingParams> {
_32
return this.c
_32
.get<GreetingParams.Data>(`/greeting/parameters`)
_32
.then((d) => d.result)
_32
.then((d) => ({
_32
max_hellos: Number.parseInt(d.max_hellos),
_32
max_goodbyes: Number.parseInt(d.max_goodbyes),
_32
}));
_32
}
_32
}

  1. Register the API functionality inside src/client/lcd/api/index.ts:

_12
export * from './AuthAPI';
_12
export * from './BankAPI';
_12
export * from './DistributionAPI';
_12
export * from './GovAPI';
_12
export * from './GreetingAPI'; // ADD HERE
_12
export * from './MsgAuthAPI';
_12
export * from './SlashingAPI';
_12
export * from './StakingAPI';
_12
export * from './SupplyAPI';
_12
export * from './TendermintAPI';
_12
export * from './TxAPI';
_12
export * from './WasmAPI';

  1. Add the functionality to src/client/lcd/LCDClient.ts:

_63
...
_63
import {
_63
AuthAPI,
_63
BankAPI,
_63
DistributionAPI,
_63
GovAPI,
_63
GreetingAPI, // ADD HERE
_63
MsgAuthAPI,
_63
SlashingAPI,
_63
StakingAPI,
_63
SupplyAPI,
_63
TendermintAPI,
_63
TxAPI,
_63
WasmAPI,
_63
} from './api';
_63
...
_63
_63
_63
export class LCDClient {
_63
public config: LCDClientConfig;
_63
public apiRequester: APIRequester;
_63
_63
// API access
_63
public auth: AuthAPI;
_63
public bank: BankAPI;
_63
public distribution: DistributionAPI;
_63
public gov: GovAPI;
_63
public greeting: GreetingAPI; // ADD HERE
_63
public msgauth: MsgAuthAPI;
_63
public slashing: SlashingAPI;
_63
public staking: StakingAPI;
_63
public supply: SupplyAPI;
_63
public tendermint: TendermintAPI;
_63
public wasm: WasmAPI;
_63
public tx: TxAPI;
_63
_63
/**
_63
* Creates a new LCD client with the specified configuration.
_63
*
_63
* @param config LCD configuration
_63
*/
_63
constructor(config: LCDClientConfig) {
_63
this.config = {
_63
...DEFAULT_LCD_OPTIONS,
_63
...config,
_63
};
_63
_63
this.apiRequester = new APIRequester(this.config.URL);
_63
_63
// instantiate APIs
_63
this.auth = new AuthAPI(this.apiRequester);
_63
this.bank = new BankAPI(this.apiRequester);
_63
this.distribution = new DistributionAPI(this.apiRequester);
_63
this.gov = new GovAPI(this.apiRequester);
_63
this.greeting = new GreetingAPI(this.apiRequester); // ADD HERE
_63
this.msgauth = new MsgAuthAPI(this.apiRequester);
_63
this.slashing = new SlashingAPI(this.apiRequester);
_63
this.staking = new StakingAPI(this.apiRequester);
_63
this.supply = new SupplyAPI(this.apiRequester);
_63
this.tendermint = new TendermintAPI(this.apiRequester);
_63
this.wasm = new WasmAPI(this.apiRequester);
_63
this.tx = new TxAPI(this);
_63
}