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

Align the token support methods to the lip #8631

Merged
merged 7 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 81 additions & 5 deletions framework/src/modules/token/method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { BurnEvent } from './events/burn';
import { LockEvent } from './events/lock';
import { UnlockEvent } from './events/unlock';
import { TransferCrossChainEvent } from './events/transfer_cross_chain';
import { SupportedTokensStore } from './stores/supported_tokens';
import { ALL_SUPPORTED_TOKENS_KEY, SupportedTokensStore } from './stores/supported_tokens';
import { AllTokensSupportedEvent } from './events/all_tokens_supported';
import { AllTokensSupportRemovedEvent } from './events/all_tokens_supported_removed';
import { TokenIDSupportedEvent } from './events/token_id_supported';
Expand Down Expand Up @@ -687,15 +687,53 @@ export class TokenMethod extends BaseMethod {
methodContext: MethodContext,
chainID: Buffer,
): Promise<void> {
await this.stores.get(SupportedTokensStore).supportChain(methodContext, chainID);
const allTokensSupported = await this.stores
.get(SupportedTokensStore)
.has(methodContext, ALL_SUPPORTED_TOKENS_KEY);

if (allTokensSupported) {
return;
}

if (chainID.equals(this._config.ownChainID)) {
return;
}

await this.stores
.get(SupportedTokensStore)
.set(methodContext, chainID, { supportedTokenIDs: [] });

this.events.get(AllTokensFromChainSupportedEvent).log(methodContext, chainID);
}

public async removeAllTokensSupportFromChainID(
methodContext: MethodContext,
chainID: Buffer,
): Promise<void> {
await this.stores.get(SupportedTokensStore).removeSupportForChain(methodContext, chainID);
const allTokensSupported = await this.stores
.get(SupportedTokensStore)
.has(methodContext, ALL_SUPPORTED_TOKENS_KEY);

if (allTokensSupported) {
throw new Error('Invalid operation. All tokens from all chains are supported.');
}

if (chainID.equals(this._config.ownChainID)) {
throw new Error(
'Invalid operation. All tokens from all the specified chain should be supported.',
);
}

const isChainSupported = await this.stores
.get(SupportedTokensStore)
.has(methodContext, chainID);

if (!isChainSupported) {
return;
}

await this.stores.get(SupportedTokensStore).del(methodContext, chainID);

this.events.get(AllTokensFromChainSupportRemovedEvent).log(methodContext, chainID);
}

Expand All @@ -704,8 +742,46 @@ export class TokenMethod extends BaseMethod {
this.events.get(TokenIDSupportedEvent).log(methodContext, tokenID);
}

public async removeSupportTokenID(methodContext: MethodContext, tokenID: Buffer): Promise<void> {
await this.stores.get(SupportedTokensStore).removeSupportForToken(methodContext, tokenID);
shuse2 marked this conversation as resolved.
Show resolved Hide resolved
public async removeSupport(methodContext: MethodContext, tokenID: Buffer): Promise<void> {
const [chainID] = splitTokenID(tokenID);

const allTokensSupported = await this.stores
.get(SupportedTokensStore)
.has(methodContext, ALL_SUPPORTED_TOKENS_KEY);

if (allTokensSupported) {
throw new Error('All tokens are supported.');
}

if (tokenID.equals(this.getMainchainTokenID()) || chainID.equals(this._config.ownChainID)) {
throw new Error('Cannot remove support for the specified token.');
}

const isChainSupported = await this.stores
.get(SupportedTokensStore)
.has(methodContext, chainID);

if (!isChainSupported) {
this.events.get(TokenIDSupportRemovedEvent).log(methodContext, tokenID);
return;
}

const supportedTokens = await this.stores.get(SupportedTokensStore).get(methodContext, chainID);

if (supportedTokens.supportedTokenIDs.length === 0) {
throw new Error('All tokens from the specified chain are supported.');
}

const tokenIndex = supportedTokens.supportedTokenIDs.indexOf(tokenID);

if (tokenIndex !== -1) {
supportedTokens.supportedTokenIDs.splice(tokenIndex, 1);

if (supportedTokens.supportedTokenIDs.length === 0) {
await this.stores.get(SupportedTokensStore).del(methodContext, chainID);
}
}
shuse2 marked this conversation as resolved.
Show resolved Hide resolved

this.events.get(TokenIDSupportRemovedEvent).log(methodContext, tokenID);
}

Expand Down
173 changes: 167 additions & 6 deletions framework/test/unit/modules/token/method.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ import { InternalMethod } from '../../../../src/modules/token/internal_method';
import { crossChainTransferMessageParams } from '../../../../src/modules/token/schemas';
import { EscrowStore } from '../../../../src/modules/token/stores/escrow';
import { SupplyStore } from '../../../../src/modules/token/stores/supply';
import { SupportedTokensStore } from '../../../../src/modules/token/stores/supported_tokens';
import {
ALL_SUPPORTED_TOKENS_KEY,
SupportedTokensStore,
} from '../../../../src/modules/token/stores/supported_tokens';
import { UserStore } from '../../../../src/modules/token/stores/user';
import { MethodContext, createMethodContext, EventQueue } from '../../../../src/state_machine';
import { PrefixedStateReadWriter } from '../../../../src/state_machine/prefixed_state_read_writer';
Expand Down Expand Up @@ -1223,7 +1226,48 @@ describe('token module', () => {
});

describe('supportAllTokensFromChainID', () => {
it('should call support chain', async () => {
it('should return early if all tokens are already supported', async () => {
await tokenModule.stores
.get(SupportedTokensStore)
.set(methodContext, ALL_SUPPORTED_TOKENS_KEY, { supportedTokenIDs: [] });

await expect(
method.supportAllTokensFromChainID(methodContext, Buffer.from([1, 2, 3, 4])),
).resolves.toBeUndefined();

expect(methodContext.eventQueue.getEvents()).toHaveLength(0);
});

it('should return early if the chain ID is the same as the own chain ID', async () => {
const chainID = Buffer.from([1, 2, 3, 4]);
method['_config'].ownChainID = chainID;

await method.supportAllTokensFromChainID(methodContext, chainID);

await expect(
method.supportAllTokensFromChainID(methodContext, chainID),
).resolves.toBeUndefined();

expect(methodContext.eventQueue.getEvents()).toHaveLength(0);
});

it('should set an empty array of supported token IDs for the chain ID', async () => {
await tokenModule.stores
.get(SupportedTokensStore)
.set(methodContext, ALL_SUPPORTED_TOKENS_KEY, { supportedTokenIDs: [] });

await expect(
method.supportAllTokensFromChainID(methodContext, Buffer.from([1, 2, 3, 4])),
).resolves.toBeUndefined();

const supportedTokens = await tokenModule.stores
.get(SupportedTokensStore)
.get(methodContext, ALL_SUPPORTED_TOKENS_KEY);

expect(supportedTokens.supportedTokenIDs).toHaveLength(0);
});

it('should log AllTokensFromChainSupportedEvent', async () => {
await expect(
method.supportAllTokensFromChainID(methodContext, Buffer.from([1, 2, 3, 4])),
).resolves.toBeUndefined();
Expand All @@ -1249,6 +1293,54 @@ describe('token module', () => {
new AllTokensFromChainSupportRemovedEvent('token').name,
);
});

it('should throw an error if all tokens from all chains are supported', async () => {
await tokenModule.stores
.get(SupportedTokensStore)
.set(methodContext, ALL_SUPPORTED_TOKENS_KEY, { supportedTokenIDs: [] });

await expect(
method.removeAllTokensSupportFromChainID(methodContext, Buffer.from([1, 2, 3, 4])),
).rejects.toThrow('Invalid operation. All tokens from all chains are supported.');

expect(methodContext.eventQueue.getEvents()).toHaveLength(0);
});

it('should throw an error if the chain ID is the same as the own chain ID', async () => {
const chainID = Buffer.from([1, 2, 3, 4]);
method['_config'].ownChainID = chainID;

await expect(
method.removeAllTokensSupportFromChainID(methodContext, chainID),
).rejects.toThrow(
'Invalid operation. All tokens from all the specified chain should be supported.',
);

expect(methodContext.eventQueue.getEvents()).toHaveLength(0);
});

it('should return early if there are no supportedTokens', async () => {
await expect(
method.removeAllTokensSupportFromChainID(methodContext, Buffer.from([1, 2, 3, 4])),
).resolves.toBeUndefined();

expect(methodContext.eventQueue.getEvents()).toHaveLength(0);
});

it('should remove the chain ID from the supported tokens store', async () => {
await tokenModule.stores
.get(SupportedTokensStore)
.supportChain(methodContext, Buffer.from([1, 2, 3, 4]));

await expect(
method.removeAllTokensSupportFromChainID(methodContext, Buffer.from([1, 2, 3, 4])),
).resolves.toBeUndefined();

expect(methodContext.eventQueue.getEvents()).toHaveLength(1);
expect(methodContext.eventQueue.getEvents()[0].toObject().name).toEqual(
new AllTokensFromChainSupportRemovedEvent('token').name,
);
});
});

describe('supportTokenID', () => {
Expand All @@ -1264,14 +1356,83 @@ describe('token module', () => {
});
});

describe('removeSupportTokenID', () => {
describe('removeSupport', () => {
it('should call remove support for token', async () => {
await expect(
method.removeSupport(methodContext, Buffer.from([1, 2, 3, 4, 0, 0, 0, 0])),
).resolves.toBeUndefined();

expect(methodContext.eventQueue.getEvents()).toHaveLength(1);
expect(methodContext.eventQueue.getEvents()[0].toObject().name).toEqual(
new TokenIDSupportRemovedEvent('token').name,
);
});

it('should throw an error if all tokens are supported', async () => {
await tokenModule.stores
.get(SupportedTokensStore)
.supportToken(methodContext, Buffer.from([1, 2, 3, 4, 0, 0, 0, 0]));
.set(methodContext, ALL_SUPPORTED_TOKENS_KEY, { supportedTokenIDs: [] });

await expect(
method.removeSupportTokenID(methodContext, Buffer.from([1, 2, 3, 4, 0, 0, 0, 0])),
).resolves.toBeUndefined();
method.removeSupport(methodContext, Buffer.from([1, 2, 3, 4, 0, 0, 0, 0])),
).rejects.toThrow('All tokens are supported.');

expect(methodContext.eventQueue.getEvents()).toHaveLength(0);
});

it('should throw an error if the specified token is the mainchain token or the own chain ID', async () => {
const tokenId = Buffer.from([1, 2, 3, 4, 0, 0, 0, 0]);
const chainID = Buffer.from([1, 2, 3, 4]);
method['_config'].ownChainID = chainID;

await expect(method.removeSupport(methodContext, tokenId)).rejects.toThrow(
'Cannot remove support for the specified token.',
);

expect(methodContext.eventQueue.getEvents()).toHaveLength(0);
});

it('should return early and log an event if the specified token is not supported', async () => {
const tokenId = Buffer.from([1, 2, 3, 4, 0, 0, 0, 0]);

await expect(method.removeSupport(methodContext, tokenId)).resolves.toBeUndefined();

expect(methodContext.eventQueue.getEvents()).toHaveLength(1);
expect(methodContext.eventQueue.getEvents()[0].toObject().name).toEqual(
new TokenIDSupportRemovedEvent('token').name,
);
});

it('should throw an error if all tokens from the specified chain are supported', async () => {
const tokenId = Buffer.from([1, 2, 3, 4, 0, 0, 0, 0]);
const chainID = Buffer.from([1, 2, 3, 4]);

await tokenModule.stores.get(SupportedTokensStore).supportChain(methodContext, chainID);

await expect(method.removeSupport(methodContext, tokenId)).rejects.toThrow(
'All tokens from the specified chain are supported.',
);

expect(methodContext.eventQueue.getEvents()).toHaveLength(0);
});

it('should remove the specified token from the supported tokens list', async () => {
const tokenId = Buffer.from([1, 2, 3, 4, 0, 0, 0, 0]);

await tokenModule.stores.get(SupportedTokensStore).supportToken(methodContext, tokenId);

await expect(method.removeSupport(methodContext, tokenId)).resolves.toBeUndefined();

expect(methodContext.eventQueue.getEvents()).toHaveLength(1);
expect(methodContext.eventQueue.getEvents()[0].toObject().name).toEqual(
new TokenIDSupportRemovedEvent('token').name,
);
});

it('should log an event when support is removed for token', async () => {
const tokenId = Buffer.from([1, 2, 3, 4, 0, 0, 0, 0]);

await expect(method.removeSupport(methodContext, tokenId)).resolves.toBeUndefined();

expect(methodContext.eventQueue.getEvents()).toHaveLength(1);
expect(methodContext.eventQueue.getEvents()[0].toObject().name).toEqual(
Expand Down