UNPKG

lisk-framework

Version:

Lisk blockchain application platform

339 lines 20.5 kB
"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