@swaptoshi/liquid-pos-module
Version:
Klayr liquid PoS on-chain module
106 lines • 5.75 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InternalLiquidPosMethod = void 0;
const klayr_framework_1 = require("klayr-framework");
const codec_1 = require("@klayr/codec");
const validator = require("@klayr/validator");
const schemas_1 = require("klayr-framework/dist-node/modules/pos/schemas");
const bignumber_js_1 = require("bignumber.js");
const constants_1 = require("./constants");
const schema_1 = require("./schema");
const lst_mint_1 = require("./events/lst_mint");
const lst_burn_1 = require("./events/lst_burn");
const config_1 = require("./config");
bignumber_js_1.default.config({ ROUNDING_MODE: bignumber_js_1.default.ROUND_FLOOR });
class InternalLiquidPosMethod extends klayr_framework_1.Modules.BaseMethod {
async init(moduleConfig, genesisConfig) {
this._chainID = Buffer.from(genesisConfig.chainID, 'hex');
await this._assignLstTokenID(moduleConfig);
}
addDependencies(tokenMethod) {
this._tokenMethod = tokenMethod;
this._config = this.stores.get(config_1.LiquidPosGovernableConfig);
}
getLstTokenID() {
return this._lstTokenID;
}
async handleInitGenesisState(context) {
this.checkDependencies();
const isTokenIDAvailable = await this._tokenMethod.isTokenIDAvailable(context, this._lstTokenID);
if (!isTokenIDAvailable) {
const assetBytes = context.assets.getAsset(constants_1.POS_MODULE_NAME);
if (!assetBytes)
return;
const genesisStore = codec_1.codec.decode(schemas_1.genesisStoreSchema, assetBytes);
validator.validator.validate(schemas_1.genesisStoreSchema, genesisStore);
const totalPosStaked = genesisStore.stakers.reduce((accumulator, stakerSubstore) => accumulator + stakerSubstore.stakes.reduce((stakerAccumulator, stakes) => stakerAccumulator + stakes.amount, BigInt(0)), BigInt(0));
const totalSupply = await this._tokenMethod.getTotalSupply(context);
const lstTotalSupply = totalSupply.totalSupply.find(t => t.tokenID.equals(this._lstTokenID)).totalSupply;
const computedLstTotalSupply = await this._multiplyByRatio(context, totalPosStaked);
if (computedLstTotalSupply !== lstTotalSupply)
throw new Error('lstTokenID supply doesnt match computed totalPosStaked');
}
}
async handleAfterCommandExecute(context) {
this.checkDependencies();
if (context.transaction.module === constants_1.POS_MODULE_NAME && context.transaction.command === constants_1.POS_STAKE_COMMAND_NAME) {
let totalStake = BigInt(0);
const stakeParams = codec_1.codec.decode(schema_1.stakeCommandParamsSchema, context.transaction.params);
for (const stakes of stakeParams.stakes)
totalStake += stakes.amount;
if (totalStake > BigInt(0)) {
await this.mint(context, context.transaction.senderAddress, totalStake);
}
else if (totalStake < BigInt(0)) {
await this.burn(context, context.transaction.senderAddress, totalStake * BigInt(-1));
}
}
}
async mint(context, address, baseAmount) {
this.checkDependencies();
if (baseAmount < BigInt(0))
throw new Error("baseAmount minted can't be negative");
const isTokenIDAvailable = await this._tokenMethod.isTokenIDAvailable(context, this._lstTokenID);
if (isTokenIDAvailable)
await this._tokenMethod.initializeToken(context, this._lstTokenID);
const mintedAmount = await this._multiplyByRatio(context, baseAmount);
await this._tokenMethod.mint(context, address, this._lstTokenID, mintedAmount);
const events = this.events.get(lst_mint_1.LiquidStakingTokenMintEvent);
events.add(context, { address, tokenID: this._lstTokenID, amount: mintedAmount }, [address]);
}
async burn(context, address, baseBurned) {
this.checkDependencies();
if (baseBurned < BigInt(0))
throw new Error("baseBurned burned can't be negative");
const isTokenIDAvailable = await this._tokenMethod.isTokenIDAvailable(context, this._lstTokenID);
if (isTokenIDAvailable)
await this._tokenMethod.initializeToken(context, this._lstTokenID);
const burnedAmount = await this._multiplyByRatio(context, baseBurned);
await this._tokenMethod.burn(context, address, this._lstTokenID, burnedAmount);
const events = this.events.get(lst_burn_1.LiquidStakingTokenBurnEvent);
events.add(context, { address, tokenID: this._lstTokenID, amount: burnedAmount }, [address]);
}
checkDependencies() {
if (!this._chainID || !this._tokenMethod || !this._lstTokenID || !this._config) {
throw new Error('liquid_pos module dependencies is not configured, make sure LiquidPos.addDependencies() is called before module registration');
}
}
async _assignLstTokenID(config) {
const { tokenID } = config;
const chainID = this._chainID;
if (tokenID.length === 16) {
this._lstTokenID = Buffer.from(tokenID, 'hex');
}
else if (tokenID.length === 8) {
const buff = Buffer.from(tokenID, 'hex');
this._lstTokenID = Buffer.concat([chainID, buff]);
}
}
async _multiplyByRatio(context, amount) {
this.checkDependencies();
const config = await this._config.getConfig(context);
return BigInt(new bignumber_js_1.default(amount.toString()).multipliedBy(config.ratio).toFixed(0));
}
}
exports.InternalLiquidPosMethod = InternalLiquidPosMethod;
//# sourceMappingURL=internal_method.js.map