Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Update Token module endpoints - Closes #7579 #7660 #7705

Merged
merged 11 commits into from
Oct 29, 2022
95 changes: 72 additions & 23 deletions framework/src/modules/token/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,27 @@ import { validator } from '@liskhq/lisk-validator';
import * as cryptography from '@liskhq/lisk-cryptography';
import { NotFoundError } from '../../state_machine';
import { JSONObject, ModuleEndpointContext } from '../../types';
import { ModuleConfig } from './types';
import { BaseEndpoint } from '../base_endpoint';
import { TokenMethod } from './method';
import { LOCAL_ID_LENGTH, TOKEN_ID_LENGTH } from './constants';
import { CHAIN_ID_LENGTH, TOKEN_ID_LENGTH } from './constants';
import {
getBalanceRequestSchema,
getBalancesRequestSchema,
isSupportedRequestSchema,
SupplyStoreData,
UserStoreData,
} from './schemas';
import { EscrowStore, EscrowStoreData } from './stores/escrow';
import { SupplyStore } from './stores/supply';
import { UserStore } from './stores/user';
import { splitTokenID } from './utils';

const CHAIN_ID_ALIAS_NATIVE = Buffer.from([0, 0, 0, 1]);
import { SupportedTokensStore } from './stores/supported_tokens';

export class TokenEndpoint extends BaseEndpoint {
// eslint-disable-next-line @typescript-eslint/no-empty-function
public init(_tokenMethod: TokenMethod) {}
private _moduleConfig!: ModuleConfig;

public init(moduleConfig: ModuleConfig) {
this._moduleConfig = moduleConfig;
}

public async getBalances(
context: ModuleEndpointContext,
Expand Down Expand Up @@ -93,30 +95,69 @@ export class TokenEndpoint extends BaseEndpoint {
context: ModuleEndpointContext,
): Promise<{ totalSupply: JSONObject<SupplyStoreData & { tokenID: string }>[] }> {
const supplyStore = this.stores.get(SupplyStore);
const supplyData = await supplyStore.iterate(context, {
gte: Buffer.concat([Buffer.alloc(LOCAL_ID_LENGTH, 0)]),
lte: Buffer.concat([Buffer.alloc(LOCAL_ID_LENGTH, 255)]),
});
const supplyData = await supplyStore.getAll(context);

return {
totalSupply: supplyData.map(({ key: localID, value: supply }) => ({
tokenID: Buffer.concat([CHAIN_ID_ALIAS_NATIVE, localID]).toString('hex'),
totalSupply: supplyData.map(({ key: tokenID, value: supply }) => ({
tokenID: tokenID.toString('hex'),
totalSupply: supply.totalSupply.toString(),
})),
};
}

// TODO: Update to use SupportedTokensStore #7579
// eslint-disable-next-line @typescript-eslint/require-await
public async getSupportedTokens(
_context: ModuleEndpointContext,
): Promise<{ tokenIDs: string[] }> {
return {
tokenIDs: [],
};
context: ModuleEndpointContext,
): Promise<{ supportedTokens: string[] }> {
const supportedTokensStore = this.stores.get(SupportedTokensStore);

if (await supportedTokensStore.allSupported(context)) {
return {
supportedTokens: ['*'],
};
}

const supportedTokens: string[] = [];

// main chain token
const mainchainTokenID = Buffer.concat([
context.chainID.slice(0, 1),
Buffer.alloc(TOKEN_ID_LENGTH - 1, 0),
]);
supportedTokens.push(mainchainTokenID.toString('hex'));

// native chain tokens
const supplyStore = this.stores.get(SupplyStore);
const supplyData = await supplyStore.getAll(context);

for (const tokenSupply of supplyData) {
supportedTokens.push(tokenSupply.key.toString('hex'));
}

// foreign chain tokens
const supportedTokensData = await supportedTokensStore.getAll(context);

for (const supportedToken of supportedTokensData) {
if (!supportedToken.value.supportedTokenIDs.length) {
supportedTokens.push(`${supportedToken.key.toString('hex')}${'********'}`); // key in supported token store is 4-byte chain ID
} else {
for (const token of supportedToken.value.supportedTokenIDs) {
supportedTokens.push(token.toString('hex'));
}
}
}

return { supportedTokens };
}

public async isSupported(context: ModuleEndpointContext) {
validator.validate<{ tokenID: string }>(isSupportedRequestSchema, context.params);

const tokenID = Buffer.from(context.params.tokenID, 'hex');
const supportedTokensStore = this.stores.get(SupportedTokensStore);

return { supported: await supportedTokensStore.isSupported(context, tokenID) };
}

// eslint-disable-next-line @typescript-eslint/require-await
public async getEscrowedAmounts(
context: ModuleEndpointContext,
): Promise<{
Expand All @@ -129,13 +170,21 @@ export class TokenEndpoint extends BaseEndpoint {
});
return {
escrowedAmounts: escrowData.map(({ key, value: escrow }) => {
const [escrowChainID, localID] = splitTokenID(key);
const escrowChainID = key.slice(0, CHAIN_ID_LENGTH);
const tokenID = key.slice(CHAIN_ID_LENGTH);
return {
escrowChainID: escrowChainID.toString('hex'),
amount: escrow.amount.toString(),
tokenID: Buffer.concat([CHAIN_ID_ALIAS_NATIVE, localID]).toString('hex'),
tokenID: tokenID.toString('hex'),
};
}),
};
}

public getInitializationFees() {
return {
userAccount: this._moduleConfig.userAccountInitializationFee.toString(),
escrowAccount: this._moduleConfig.escrowAccountInitializationFee.toString(),
};
}
}
35 changes: 22 additions & 13 deletions framework/src/modules/token/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ import {
getBalanceRequestSchema,
getBalanceResponseSchema,
getBalancesRequestSchema,
getBalancesResponseSchema,
getEscrowedAmountsResponseSchema,
getSupportedTokensResponseSchema,
isSupportedRequestSchema,
isSupportedResponseSchema,
getTotalSupplyResponseSchema,
} from './schemas';
import { TokenMethod } from './method';
import { TokenEndpoint } from './endpoint';
import { GenesisTokenStore, InteroperabilityMethod, ModuleConfigJSON } from './types';
import { GenesisTokenStore, InteroperabilityMethod, ModuleConfig, ModuleConfigJSON } from './types';
import { splitTokenID } from './utils';
import { CCTransferCommand } from './commands/cc_transfer';
import { BaseInteroperableModule } from '../interoperability/base_interoperable_module';
Expand Down Expand Up @@ -126,7 +129,7 @@ export class TokenModule extends BaseInteroperableModule {
{
name: this.endpoint.getBalances.name,
request: getBalancesRequestSchema,
response: getBalancesRequestSchema,
response: getBalancesResponseSchema,
},
{
name: this.endpoint.getTotalSupply.name,
Expand All @@ -136,6 +139,11 @@ export class TokenModule extends BaseInteroperableModule {
name: this.endpoint.getSupportedTokens.name,
response: getSupportedTokensResponseSchema,
},
{
name: this.endpoint.isSupported.name,
request: isSupportedRequestSchema,
response: isSupportedResponseSchema,
},
{
name: this.endpoint.getEscrowedAmounts.name,
response: getEscrowedAmountsResponseSchema,
Expand Down Expand Up @@ -163,29 +171,30 @@ export class TokenModule extends BaseInteroperableModule {
const { moduleConfig, genesisConfig } = args;
this._ownChainID = Buffer.from(genesisConfig.chainID, 'hex');

const config = objects.mergeDeep(
const rawConfig = objects.mergeDeep(
{},
defaultConfig,
{ feeTokenID: Buffer.concat([this._ownChainID, Buffer.alloc(4, 0)]).toString('hex') },
moduleConfig,
) as ModuleConfigJSON;
validator.validate(configSchema, config);
validator.validate(configSchema, rawConfig);

const config: ModuleConfig = {
userAccountInitializationFee: BigInt(rawConfig.userAccountInitializationFee),
escrowAccountInitializationFee: BigInt(rawConfig.escrowAccountInitializationFee),
feeTokenID: Buffer.from(rawConfig.feeTokenID, 'hex'),
};

this.stores.get(SupportedTokensStore).registerOwnChainID(this._ownChainID);
this.crossChainTransferCommand.init({ ownChainID: this._ownChainID });

this.method.init({
ownChainID: this._ownChainID,
escrowAccountInitializationFee: BigInt('50000000'),
feeTokenID: Buffer.from(config.feeTokenID, 'hex'),
userAccountInitializationFee: BigInt('50000000'),
});
this.method.init({ ...config, ownChainID: this._ownChainID });
this.crossChainMethod.init(this._ownChainID);
this.endpoint.init(this.method);
this.endpoint.init(config);
this._transferCommand.init({
method: this.method,
accountInitializationFee: BigInt(config.userAccountInitializationFee),
feeTokenID: Buffer.from(config.feeTokenID, 'hex'),
accountInitializationFee: config.userAccountInitializationFee,
feeTokenID: config.feeTokenID,
});
}

Expand Down
25 changes: 25 additions & 0 deletions framework/src/modules/token/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,3 +619,28 @@ export const getEscrowedAmountsResponseSchema = {
},
},
};

export const isSupportedRequestSchema = {
$id: '/token/endpoint/isSupportedRequest',
type: 'object',
properties: {
tokenID: {
type: 'string',
format: 'hex',
minLength: TOKEN_ID_LENGTH * 2,
maxLength: TOKEN_ID_LENGTH * 2,
},
},
required: ['tokenID'],
};

export const isSupportedResponseSchema = {
$id: '/token/endpoint/isSupportedResponse',
type: 'object',
properties: {
supported: {
dataType: 'boolean',
},
},
required: ['supported'],
};
26 changes: 13 additions & 13 deletions framework/src/modules/token/stores/supported_tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const supportedTokensStoreSchema = {
},
};

export const ALL_SUPPORTED_TOKENS_KEY = Buffer.from([0, 0, 0, 0]);
export const ALL_SUPPORTED_TOKENS_KEY = Buffer.alloc(0);

export class SupportedTokensStore extends BaseStore<SupportedTokensStoreData> {
public schema = supportedTokensStoreSchema;
Expand Down Expand Up @@ -77,25 +77,25 @@ export class SupportedTokensStore extends BaseStore<SupportedTokensStoreData> {
return false;
}

public async removeAll(context: StoreGetter): Promise<void> {
// check if exist
const allSupportedTokens = await this.iterate(context, {
gte: Buffer.alloc(4, 0),
lte: Buffer.alloc(4, 255),
public async getAll(
context: ImmutableStoreGetter,
): Promise<{ key: Buffer; value: SupportedTokensStoreData }[]> {
return this.iterate(context, {
gte: Buffer.alloc(TOKEN_ID_LENGTH, 0),
lte: Buffer.alloc(TOKEN_ID_LENGTH, 255),
});
}

public async removeAll(context: StoreGetter): Promise<void> {
const allSupportedTokens = await this.getAll(context);

for (const { key } of allSupportedTokens) {
await this.del(context, key);
}
}

public async supportAll(context: StoreGetter): Promise<void> {
const allSupportedTokens = await this.iterate(context, {
gte: Buffer.alloc(4, 0),
lte: Buffer.alloc(4, 255),
});
for (const { key } of allSupportedTokens) {
await this.del(context, key);
}
await this.removeAll(context);
await this.set(context, ALL_SUPPORTED_TOKENS_KEY, { supportedTokenIDs: [] });
}

Expand Down
Loading