lisk-framework
Version:
Lisk blockchain application platform
339 lines • 20.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenModule = void 0;
const lisk_validator_1 = require("@liskhq/lisk-validator");
const lisk_codec_1 = require("@liskhq/lisk-codec");
const lisk_utils_1 = require("@liskhq/lisk-utils");
const lisk_cryptography_1 = require("@liskhq/lisk-cryptography");
const constants_1 = require("./constants");
const transfer_1 = require("./commands/transfer");
const schemas_1 = require("./schemas");
const method_1 = require("./method");
const endpoint_1 = require("./endpoint");
const utils_1 = require("./utils");
const base_interoperable_module_1 = require("../interoperability/base_interoperable_module");
const cc_method_1 = require("./cc_method");
const user_1 = require("./stores/user");
const escrow_1 = require("./stores/escrow");
const supply_1 = require("./stores/supply");
const supported_tokens_1 = require("./stores/supported_tokens");
const transfer_2 = require("./events/transfer");
const transfer_cross_chain_1 = require("./events/transfer_cross_chain");
const ccm_transfer_1 = require("./events/ccm_transfer");
const mint_1 = require("./events/mint");
const burn_1 = require("./events/burn");
const lock_1 = require("./events/lock");
const unlock_1 = require("./events/unlock");
const initialize_token_1 = require("./events/initialize_token");
const initialize_user_account_1 = require("./events/initialize_user_account");
const initialize_escrow_account_1 = require("./events/initialize_escrow_account");
const recover_1 = require("./events/recover");
const before_ccc_execution_1 = require("./events/before_ccc_execution");
const before_ccm_forwarding_1 = require("./events/before_ccm_forwarding");
const all_tokens_supported_1 = require("./events/all_tokens_supported");
const all_tokens_supported_removed_1 = require("./events/all_tokens_supported_removed");
const all_tokens_from_chain_supported_1 = require("./events/all_tokens_from_chain_supported");
const all_tokens_from_chain_supported_removed_1 = require("./events/all_tokens_from_chain_supported_removed");
const token_id_supported_1 = require("./events/token_id_supported");
const token_id_supported_removed_1 = require("./events/token_id_supported_removed");
const cc_transfer_1 = require("./cc_commands/cc_transfer");
const transfer_cross_chain_2 = require("./commands/transfer_cross_chain");
const internal_method_1 = require("./internal_method");
class TokenModule extends base_interoperable_module_1.BaseInteroperableModule {
constructor() {
super();
this.method = new method_1.TokenMethod(this.stores, this.events, this.name);
this.endpoint = new endpoint_1.TokenEndpoint(this.stores, this.offchainStores);
this.crossChainMethod = new cc_method_1.TokenInteroperableMethod(this.stores, this.events);
this.crossChainTransferCommand = new cc_transfer_1.CrossChainTransferCommand(this.stores, this.events);
this.crossChainCommand = [this.crossChainTransferCommand];
this._transferCommand = new transfer_1.TransferCommand(this.stores, this.events);
this._ccTransferCommand = new transfer_cross_chain_2.TransferCrossChainCommand(this.stores, this.events);
this._internalMethod = new internal_method_1.InternalMethod(this.stores, this.events);
this.commands = [this._transferCommand, this._ccTransferCommand];
this.stores.register(user_1.UserStore, new user_1.UserStore(this.name, 0));
this.stores.register(supply_1.SupplyStore, new supply_1.SupplyStore(this.name, 1));
this.stores.register(escrow_1.EscrowStore, new escrow_1.EscrowStore(this.name, 2));
this.stores.register(supported_tokens_1.SupportedTokensStore, new supported_tokens_1.SupportedTokensStore(this.name, 3));
this.events.register(transfer_2.TransferEvent, new transfer_2.TransferEvent(this.name));
this.events.register(transfer_cross_chain_1.TransferCrossChainEvent, new transfer_cross_chain_1.TransferCrossChainEvent(this.name));
this.events.register(ccm_transfer_1.CcmTransferEvent, new ccm_transfer_1.CcmTransferEvent(this.name));
this.events.register(mint_1.MintEvent, new mint_1.MintEvent(this.name));
this.events.register(burn_1.BurnEvent, new burn_1.BurnEvent(this.name));
this.events.register(lock_1.LockEvent, new lock_1.LockEvent(this.name));
this.events.register(unlock_1.UnlockEvent, new unlock_1.UnlockEvent(this.name));
this.events.register(initialize_token_1.InitializeTokenEvent, new initialize_token_1.InitializeTokenEvent(this.name));
this.events.register(initialize_user_account_1.InitializeUserAccountEvent, new initialize_user_account_1.InitializeUserAccountEvent(this.name));
this.events.register(initialize_escrow_account_1.InitializeEscrowAccountEvent, new initialize_escrow_account_1.InitializeEscrowAccountEvent(this.name));
this.events.register(recover_1.RecoverEvent, new recover_1.RecoverEvent(this.name));
this.events.register(before_ccc_execution_1.BeforeCCCExecutionEvent, new before_ccc_execution_1.BeforeCCCExecutionEvent(this.name));
this.events.register(before_ccm_forwarding_1.BeforeCCMForwardingEvent, new before_ccm_forwarding_1.BeforeCCMForwardingEvent(this.name));
this.events.register(all_tokens_supported_1.AllTokensSupportedEvent, new all_tokens_supported_1.AllTokensSupportedEvent(this.name));
this.events.register(all_tokens_supported_removed_1.AllTokensSupportRemovedEvent, new all_tokens_supported_removed_1.AllTokensSupportRemovedEvent(this.name));
this.events.register(all_tokens_from_chain_supported_1.AllTokensFromChainSupportedEvent, new all_tokens_from_chain_supported_1.AllTokensFromChainSupportedEvent(this.name));
this.events.register(all_tokens_from_chain_supported_removed_1.AllTokensFromChainSupportRemovedEvent, new all_tokens_from_chain_supported_removed_1.AllTokensFromChainSupportRemovedEvent(this.name));
this.events.register(token_id_supported_1.TokenIDSupportedEvent, new token_id_supported_1.TokenIDSupportedEvent(this.name));
this.events.register(token_id_supported_removed_1.TokenIDSupportRemovedEvent, new token_id_supported_removed_1.TokenIDSupportRemovedEvent(this.name));
}
addDependencies(interoperabilityMethod, feeMethod) {
this._interoperabilityMethod = interoperabilityMethod;
this.method.addDependencies(interoperabilityMethod, this._internalMethod);
this.crossChainMethod.addDependencies(interoperabilityMethod, this._internalMethod);
this._internalMethod.addDependencies(feeMethod);
}
metadata() {
return {
...this.baseMetadata(),
endpoints: [
{
name: this.endpoint.getBalance.name,
request: schemas_1.getBalanceRequestSchema,
response: schemas_1.getBalanceResponseSchema,
},
{
name: this.endpoint.getBalances.name,
request: schemas_1.getBalancesRequestSchema,
response: schemas_1.getBalancesResponseSchema,
},
{
name: this.endpoint.getTotalSupply.name,
response: schemas_1.getTotalSupplyResponseSchema,
},
{
name: this.endpoint.getSupportedTokens.name,
response: schemas_1.getSupportedTokensResponseSchema,
},
{
name: this.endpoint.isSupported.name,
request: schemas_1.isSupportedRequestSchema,
response: schemas_1.isSupportedResponseSchema,
},
{
name: this.endpoint.getEscrowedAmounts.name,
response: schemas_1.getEscrowedAmountsResponseSchema,
},
{
name: this.endpoint.getInitializationFees.name,
response: schemas_1.getInitializationFeesResponseSchema,
},
{
name: this.endpoint.hasUserAccount.name,
request: schemas_1.hasUserAccountRequestSchema,
response: schemas_1.hasUserAccountResponseSchema,
},
{
name: this.endpoint.hasEscrowAccount.name,
request: schemas_1.hasEscrowAccountRequestSchema,
response: schemas_1.hasEscrowAccountResponseSchema,
},
],
assets: [
{
version: 0,
data: schemas_1.genesisTokenStoreSchema,
},
],
};
}
async init(args) {
const { moduleConfig, genesisConfig } = args;
const ownChainID = Buffer.from(genesisConfig.chainID, 'hex');
const rawConfig = lisk_utils_1.objects.mergeDeep({}, constants_1.defaultConfig, moduleConfig);
lisk_validator_1.validator.validate(schemas_1.configSchema, rawConfig);
const config = {
userAccountInitializationFee: BigInt(rawConfig.userAccountInitializationFee),
escrowAccountInitializationFee: BigInt(rawConfig.escrowAccountInitializationFee),
};
this._internalMethod.init(config);
this.stores.get(supported_tokens_1.SupportedTokensStore).registerOwnChainID(ownChainID);
this.crossChainTransferCommand.init({
internalMethod: this._internalMethod,
tokenMethod: this.method,
});
this._ccTransferCommand.init({
internalMethod: this._internalMethod,
interoperabilityMethod: this._interoperabilityMethod,
method: this.method,
moduleName: this.name,
});
this.method.init({ ...config, ownChainID });
this.endpoint.init(config);
this._transferCommand.init({
method: this.method,
internalMethod: this._internalMethod,
});
}
async initGenesisState(context) {
var _a, _b;
const assetBytes = context.assets.getAsset(this.name);
if (!assetBytes) {
return;
}
const genesisStore = lisk_codec_1.codec.decode(schemas_1.genesisTokenStoreSchema, assetBytes);
lisk_validator_1.validator.validate(schemas_1.genesisTokenStoreSchema, genesisStore);
const userStore = this.stores.get(user_1.UserStore);
const copiedUserStore = [...genesisStore.userSubstore];
copiedUserStore.sort((a, b) => {
if (!a.address.equals(b.address)) {
return a.address.compare(b.address);
}
return a.tokenID.compare(b.tokenID);
});
const userKeySet = new lisk_utils_1.dataStructures.BufferSet();
for (let i = 0; i < genesisStore.userSubstore.length; i += 1) {
const userData = genesisStore.userSubstore[i];
const key = userStore.getKey(userData.address, userData.tokenID);
if (userKeySet.has(key)) {
throw new Error(`Address ${lisk_cryptography_1.address.getLisk32AddressFromAddress(userData.address)} and tokenID ${userData.tokenID.toString('hex')} pair is duplicated.`);
}
userKeySet.add(key);
if (!userData.address.equals(copiedUserStore[i].address) ||
!userData.tokenID.equals(copiedUserStore[i].tokenID)) {
throw new Error('UserSubstore must be sorted by address and tokenID.');
}
const lockedBalanceModuleIDSet = new Set();
let lastModule = '';
for (const lockedBalance of userData.lockedBalances) {
lockedBalanceModuleIDSet.add(lockedBalance.module);
if (lockedBalance.amount === BigInt(0)) {
throw new Error(`Address ${lisk_cryptography_1.address.getLisk32AddressFromAddress(userData.address)} contains 0 amount locked balance.`);
}
if (lockedBalance.module.localeCompare(lastModule, 'en') < 0) {
throw new Error('Locked balances must be sorted by module.');
}
lastModule = lockedBalance.module;
}
if (lockedBalanceModuleIDSet.size !== userData.lockedBalances.length) {
throw new Error(`Address ${lisk_cryptography_1.address.getLisk32AddressFromAddress(userData.address)} has duplicate module in locked balances.`);
}
await userStore.save(context, userData.address, userData.tokenID, userData);
}
const copiedSupplyStore = [...genesisStore.supplySubstore];
copiedSupplyStore.sort((a, b) => a.tokenID.compare(b.tokenID));
const supplyStoreKeySet = new lisk_utils_1.dataStructures.BufferSet();
const supplyStore = this.stores.get(supply_1.SupplyStore);
for (let i = 0; i < genesisStore.supplySubstore.length; i += 1) {
const supplyData = genesisStore.supplySubstore[i];
if (supplyStoreKeySet.has(supplyData.tokenID)) {
throw new Error(`Supply store token ID ${supplyData.tokenID.toString('hex')} is duplicated.`);
}
supplyStoreKeySet.add(supplyData.tokenID);
if (!supplyData.tokenID.equals(copiedSupplyStore[i].tokenID)) {
throw new Error('SupplySubstore must be sorted by tokenID.');
}
await supplyStore.set(context, supplyData.tokenID, { totalSupply: supplyData.totalSupply });
}
const copiedEscrowStore = [...genesisStore.escrowSubstore];
copiedEscrowStore.sort((a, b) => {
if (!a.escrowChainID.equals(b.escrowChainID)) {
return a.escrowChainID.compare(b.escrowChainID);
}
return a.tokenID.compare(b.tokenID);
});
const escrowStore = this.stores.get(escrow_1.EscrowStore);
const escrowKeySet = new lisk_utils_1.dataStructures.BufferSet();
for (let i = 0; i < genesisStore.escrowSubstore.length; i += 1) {
const escrowData = genesisStore.escrowSubstore[i];
const key = Buffer.concat([escrowData.escrowChainID, escrowData.tokenID]);
if (escrowKeySet.has(key)) {
throw new Error(`Escrow store escrowChainID ${escrowData.escrowChainID.toString('hex')} and tokenID ${escrowData.tokenID.toString('hex')} pair is duplicated.`);
}
escrowKeySet.add(key);
if (!escrowData.escrowChainID.equals(copiedEscrowStore[i].escrowChainID) ||
!escrowData.tokenID.equals(copiedEscrowStore[i].tokenID)) {
throw new Error('EscrowSubstore must be sorted by escrowChainID and tokenID.');
}
await escrowStore.set(context, key, { amount: escrowData.amount });
}
const copiedSupportedTokenIDsStore = [...genesisStore.supportedTokensSubstore];
if (copiedSupportedTokenIDsStore.length !== 0) {
if (copiedSupportedTokenIDsStore.length === 1 &&
copiedSupportedTokenIDsStore[0].chainID.length === 0) {
if (copiedSupportedTokenIDsStore[0].supportedTokenIDs.length !== 0) {
throw new Error('supportedTokenIds must be an empty array when all tokens are supported.');
}
await this.stores.get(supported_tokens_1.SupportedTokensStore).supportAll(context);
}
else {
copiedSupportedTokenIDsStore.sort((a, b) => a.chainID.compare(b.chainID));
const supportedTokenIDsStore = this.stores.get(supported_tokens_1.SupportedTokensStore);
const supportedTokenIDsSet = new lisk_utils_1.dataStructures.BufferSet();
for (let i = 0; i < genesisStore.supportedTokensSubstore.length; i += 1) {
const supportedTokenIDsData = genesisStore.supportedTokensSubstore[i];
if (supportedTokenIDsData.chainID.length !== constants_1.CHAIN_ID_LENGTH) {
throw new Error(`supportedTokensSubstore chainIDs must be of length ${constants_1.CHAIN_ID_LENGTH}.`);
}
if (supportedTokenIDsSet.has(supportedTokenIDsData.chainID)) {
throw new Error(`supportedTokenIDsSet chain ID ${supportedTokenIDsData.chainID.toString('hex')} is duplicated.`);
}
supportedTokenIDsSet.add(supportedTokenIDsData.chainID);
if (!supportedTokenIDsData.chainID.equals(copiedSupportedTokenIDsStore[i].chainID)) {
throw new Error('supportedTokensSubstore must be sorted by chainID.');
}
if (!lisk_utils_1.objects.bufferArrayUniqueItems(supportedTokenIDsData.supportedTokenIDs) ||
!lisk_utils_1.objects.isBufferArrayOrdered(supportedTokenIDsData.supportedTokenIDs)) {
throw new Error('supportedTokensSubstore tokenIDs must be unique and sorted by lexicographically.');
}
for (const tokenID of supportedTokenIDsData.supportedTokenIDs) {
if (!tokenID.slice(0, constants_1.CHAIN_ID_LENGTH).equals(supportedTokenIDsData.chainID)) {
throw new Error('supportedTokensSubstore tokenIDs must match the chainID.');
}
}
await supportedTokenIDsStore.set(context, supportedTokenIDsData.chainID, {
supportedTokenIDs: supportedTokenIDsData.supportedTokenIDs,
});
}
}
}
const computedSupply = new lisk_utils_1.dataStructures.BufferMap();
const allUsers = await userStore.iterate(context, {
gte: Buffer.alloc(constants_1.ADDRESS_LENGTH + constants_1.TOKEN_ID_LENGTH, 0),
lte: Buffer.alloc(constants_1.ADDRESS_LENGTH + constants_1.TOKEN_ID_LENGTH, 255),
});
for (const { key, value: user } of allUsers) {
const tokenID = key.slice(constants_1.ADDRESS_LENGTH);
const [chainID] = (0, utils_1.splitTokenID)(tokenID);
if (chainID.equals(context.chainID)) {
const existingSupply = (_a = computedSupply.get(tokenID)) !== null && _a !== void 0 ? _a : BigInt(0);
computedSupply.set(tokenID, existingSupply +
user.availableBalance +
user.lockedBalances.reduce((prev, current) => prev + current.amount, BigInt(0)));
}
}
const allEscrows = await escrowStore.iterate(context, {
gte: Buffer.alloc(constants_1.CHAIN_ID_LENGTH + constants_1.TOKEN_ID_LENGTH, 0),
lte: Buffer.alloc(constants_1.CHAIN_ID_LENGTH + constants_1.TOKEN_ID_LENGTH, 255),
});
for (const { key, value } of allEscrows) {
const tokenID = key.slice(constants_1.CHAIN_ID_LENGTH);
const existingSupply = (_b = computedSupply.get(tokenID)) !== null && _b !== void 0 ? _b : BigInt(0);
computedSupply.set(tokenID, existingSupply + value.amount);
}
for (const [tokenID, supply] of computedSupply.entries()) {
if (!(0, lisk_validator_1.isUInt64)(supply)) {
throw new Error(`Total supply for tokenID: ${tokenID.toString('hex')} exceeds uint64 range.`);
}
}
const storedSupply = new lisk_utils_1.dataStructures.BufferMap();
const allSupplies = await supplyStore.iterate(context, {
gte: Buffer.alloc(constants_1.TOKEN_ID_LENGTH, 0),
lte: Buffer.alloc(constants_1.TOKEN_ID_LENGTH, 255),
});
for (const { key, value } of allSupplies) {
storedSupply.set(key, value.totalSupply);
}
for (const [tokenID, supply] of computedSupply.entries()) {
const stored = storedSupply.get(tokenID);
if (!stored || stored !== supply) {
throw new Error('Stored total supply conflicts with computed supply.');
}
}
for (const [tokenID, supply] of storedSupply.entries()) {
if (!computedSupply.has(tokenID) && supply !== BigInt(0)) {
throw new Error('Stored total supply is non zero but cannot be computed.');
}
}
}
}
exports.TokenModule = TokenModule;
//# sourceMappingURL=module.js.map