Skip to content

Commit

Permalink
fix: improve error messages (#416)
Browse files Browse the repository at this point in the history
  • Loading branch information
marshacb authored Jun 19, 2024
1 parent 7e2da53 commit 5a11307
Show file tree
Hide file tree
Showing 18 changed files with 147 additions and 73 deletions.
2 changes: 1 addition & 1 deletion src/AssetTransferApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 +1104,7 @@ export class AssetTransferApi {
);
}
} else {
checkLocalRelayInput(assetIds, amounts);
checkLocalRelayInput(assetIds, amounts, this.registry);
/**
* By default local transaction on a relay chain will always be from the balances pallet
*/
Expand Down
9 changes: 9 additions & 0 deletions src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,12 @@ export const JS_ENV = detectJsEnv();
* Supported consensus system chain names
*/
export const KNOWN_GLOBAL_CONSENSUS_SYSTEM_NAMES = ['polkadot', 'kusama', 'westend', 'rococo', 'ethereum'];

/**
* The asset location of the native relay chain asset from the perspective of `System` and `Para` Chains
*/
export const SYSTEM_AND_PARACHAINS_RELAY_ASSET_LOCATION = '{"parents":"1","interior":{"Here":""}}';
/**
* The asset location of the native relay chain asset from the perspective of the Relay chain
*/
export const RELAY_CHAINS_NATIVE_ASSET_LOCATION = '{"parents":"0","interior":{"Here":""}}';
5 changes: 1 addition & 4 deletions src/createXcmTypes/SystemToBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,13 @@ export const createSystemToBridgeAssets = async (
let multiAssets: FungibleStrAssetType[] = [];
let multiAsset: FungibleStrAssetType;
const palletId = fetchPalletInstanceId(api, isLiquidTokenTransfer, isForeignAssetsTransfer);
const systemChainId = registry.lookupChainIdBySpecName(specName);

for (let i = 0; i < assets.length; i++) {
let assetId: string = assets[i];
const amount = amounts[i];

const { tokens } = registry.currentRelayRegistry[systemChainId];

const isValidInt = validateNumber(assetId);
const isRelayNative = isRelayNativeAsset(tokens, assetId);
const isRelayNative = isRelayNativeAsset(registry, assetId);
if (!isRelayNative && !isValidInt) {
assetId = await getAssetId(api, registry, assetId, specName, xcmVersion, isForeignAssetsTransfer);
}
Expand Down
4 changes: 1 addition & 3 deletions src/createXcmTypes/SystemToPara.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,12 @@ export const createSystemToParaMultiAssets = async (
);
}

const { tokens } = registry.currentRelayRegistry[systemChainId];

for (let i = 0; i < assets.length; i++) {
let assetId: string = assets[i];
const amount = amounts[i];

const isValidInt = validateNumber(assetId);
const isRelayNative = isRelayNativeAsset(tokens, assetId);
const isRelayNative = isRelayNativeAsset(registry, assetId);

if (!isRelayNative && !isValidInt) {
assetId = await getAssetId(api, registry, assetId, specName, xcmVersion, isForeignAssetsTransfer);
Expand Down
4 changes: 1 addition & 3 deletions src/createXcmTypes/SystemToSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,14 +260,12 @@ export const createSystemToSystemMultiAssets = async (
);
}

const { tokens } = registry.currentRelayRegistry[systemChainId];

for (let i = 0; i < assets.length; i++) {
let assetId: string = assets[i];
const amount = amounts[i];

const isValidInt = validateNumber(assetId);
const isRelayNative = isRelayNativeAsset(tokens, assetId);
const isRelayNative = isRelayNativeAsset(registry, assetId);

if (!isRelayNative && !isValidInt) {
assetId = await getAssetId(api, registry, assetId, specName, xcmVersion, isForeignAssetsTransfer);
Expand Down
3 changes: 1 addition & 2 deletions src/createXcmTypes/util/assetIdsContainsRelayAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ export const assetIdsContainRelayAsset = (assetIds: string[], registry: Registry
return true;
}

const { tokens } = registry.currentRelayRegistry['0'];
const relayAssetMultiLocation = `{"parents": 1, "interior": { "Here": ''}}`;

for (const asset of assetIds) {
// check relay tokens and if matched it is the relay asset
if (isRelayNativeAsset(tokens, asset)) {
if (isRelayNativeAsset(registry, asset)) {
return true;
}

Expand Down
4 changes: 1 addition & 3 deletions src/createXcmTypes/util/createAssetLocations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { ApiPromise } from '@polkadot/api';

import { ASSET_HUB_CHAIN_ID } from '../../consts';
import { Registry } from '../../registry';
import { RequireOnlyOne } from '../../types';
import { resolveMultiLocation } from '../../util/resolveMultiLocation';
Expand Down Expand Up @@ -37,7 +36,6 @@ export const createAssetLocations = async (
let multiAssets: FungibleStrAssetType[] = [];
let multiAsset: FungibleStrAssetType;

const { tokens } = registry.currentRelayRegistry[ASSET_HUB_CHAIN_ID];
const palletId = fetchPalletInstanceId(api, isLiquidTokenTransfer, assetIdsContainLocations);
const isRelayChain = originChainId === '0' ? true : false;

Expand All @@ -47,7 +45,7 @@ export const createAssetLocations = async (
let assetId = assetIds[i];

const isValidInt = validateNumber(assetId);
const isRelayNative = isRelayNativeAsset(tokens, assetId);
const isRelayNative = isRelayNativeAsset(registry, assetId);

if (!assetIdsContainLocations && !isRelayNative && !isValidInt) {
assetId = await getAssetId(api, registry, assetId, specName, xcmVersion, assetIdsContainLocations);
Expand Down
2 changes: 1 addition & 1 deletion src/createXcmTypes/util/getAssetId.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('getAssetId', () => {
it('Should error when an asset id symbol is given that is not present in the registry or chain state', async () => {
await expect(async () => {
await getAssetId(systemAssetsApi.api, registry, 'hello', 'statemine', 2, false);
}).rejects.toThrow('assetId hello is not a valid symbol or integer asset id');
}).rejects.toThrow('assetId hello is not a valid symbol, integer asset id or location for statemine');
});

it('Should correctly return the foreign asset multilocation when given a valid foreign asset multilocation', async () => {
Expand Down
2 changes: 1 addition & 1 deletion src/createXcmTypes/util/getAssetId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export const getAssetId = async (
}
} else {
throw new BaseError(
`assetId ${asset} is not a valid symbol or integer asset id for ${specName}`,
`assetId ${asset} is not a valid symbol, integer asset id or location for ${specName}`,
BaseErrorsEnum.InvalidAsset,
);
}
Expand Down
46 changes: 41 additions & 5 deletions src/createXcmTypes/util/isRelayNativeAsset.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,47 @@
// Copyright 2024 Parity Technologies (UK) Ltd.

import { Registry } from '../../registry';
import { isRelayNativeAsset } from './isRelayNativeAsset';

describe('isRelayNativeAsset', () => {
const tokens = ['KSM'];
it('Should return true', () => {
expect(isRelayNativeAsset(tokens, 'KSM')).toEqual(true);
describe('RelayToSystem', () => {
const specName = 'kusama';
const registry = new Registry(specName, {});

it('Should return true when an empty string is provided as the assetId', () => {
expect(isRelayNativeAsset(registry, '')).toEqual(true);
});
it('Should return true when a symbol assetId matches the relay assetId', () => {
expect(isRelayNativeAsset(registry, 'KSM')).toEqual(true);
});
it('Should return false when a symbol assetId does not match the relay assetId', () => {
expect(isRelayNativeAsset(registry, 'DOT')).toEqual(false);
});
it('Should return true when the relay chains asset location is given as input from the perspective of the relay chain', () => {
expect(isRelayNativeAsset(registry, '{"parents":"0","interior":{"Here":""}}')).toEqual(true);
});
it('Should return false when the relay chains asset location is given as input from the perspective of a child chain', () => {
expect(isRelayNativeAsset(registry, '{"parents":"1","interior":{"Here":""}}')).toEqual(false);
});
});
it('Should return false', () => {
expect(isRelayNativeAsset(tokens, 'DOT')).toEqual(false);
describe('SystemToRelay', () => {
const specName = 'statemint';
const registry = new Registry(specName, {});

it('Should return true when an empty string is provided as the assetId', () => {
expect(isRelayNativeAsset(registry, '')).toEqual(true);
});
it('Should return true when a symbol assetId matches the relay assetId', () => {
expect(isRelayNativeAsset(registry, 'DOT')).toEqual(true);
});
it('Should return false when a symbol assetId does not match the relay assetId', () => {
expect(isRelayNativeAsset(registry, 'KSM')).toEqual(false);
});
it('Should return true when the relay chains asset location is given as input from the persepect of AssetHub', () => {
expect(isRelayNativeAsset(registry, '{"parents":"1","interior":{"Here":""}}')).toEqual(true);
});
it('Should return false when the relay chains asset location is given as input from the perspective of the relay chain', () => {
expect(isRelayNativeAsset(registry, '{"parents":"0","interior":{"Here":""}}')).toEqual(false);
});
});
});
18 changes: 15 additions & 3 deletions src/createXcmTypes/util/isRelayNativeAsset.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
// Copyright 2023 Parity Technologies (UK) Ltd.
// Copyright 2024 Parity Technologies (UK) Ltd.

import { RELAY_CHAIN_IDS } from '../../consts';
import { Registry } from '../../registry';

export const isRelayNativeAsset = (registry: Registry, assetId: string): boolean => {
const relayChainId = RELAY_CHAIN_IDS[0];
const { tokens } = registry.currentRelayRegistry[relayChainId];
const originChainId = registry.lookupChainIdBySpecName(registry.specName);
const originIsRelayChain = originChainId === relayChainId;

export const isRelayNativeAsset = (tokens: string[], assetId: string): boolean => {
// if assetId is an empty string treat it as the relay asset
if (assetId === '') {
return true;
}

if (assetId === `{"parents":"0","interior":{"Here":""}}` || assetId === `{"parents":"1","interior":{"Here":""}}`) {
if (originIsRelayChain && assetId === `{"parents":"0","interior":{"Here":""}}`) {
return true;
}

if (!originIsRelayChain && assetId === `{"parents":"1","interior":{"Here":""}}`) {
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions src/errors/checkBaseInputTypes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('checkBaseInputTypes', () => {
['10000000000'],
);

expect(err).toThrow(`'assetIds' must be a array. Received: string`);
expect(err).toThrow(`'assetIds' must be an array. Received: string`);
});
it('Should error when assetIds has the wrong types in the array', () => {
const err = () =>
Expand All @@ -48,7 +48,7 @@ describe('checkBaseInputTypes', () => {
10000000000 as unknown as string[],
);

expect(err).toThrow(`'amounts' must be a array. Received: number`);
expect(err).toThrow(`'amounts' must be an array. Received: number`);
});
it('Should error when amounts has the wrong types in the array', () => {
const err = () =>
Expand Down
4 changes: 2 additions & 2 deletions src/errors/checkBaseInputTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const checkBaseInputTypes = (destChainId: string, destAddr: string, asset
}

if (!Array.isArray(assetIds)) {
throw new BaseError(`'assetIds' must be a array. Received: ${typeof assetIds}`, BaseErrorsEnum.InvalidInput);
throw new BaseError(`'assetIds' must be an array. Received: ${typeof assetIds}`, BaseErrorsEnum.InvalidInput);
} else {
for (let i = 0; i < assetIds.length; i++) {
if (typeof assetIds[i] !== 'string') {
Expand All @@ -33,7 +33,7 @@ export const checkBaseInputTypes = (destChainId: string, destAddr: string, asset
}

if (!Array.isArray(amounts)) {
throw new BaseError(`'amounts' must be a array. Received: ${typeof amounts}`, BaseErrorsEnum.InvalidInput);
throw new BaseError(`'amounts' must be an array. Received: ${typeof amounts}`, BaseErrorsEnum.InvalidInput);
} else {
for (let i = 0; i < amounts.length; i++) {
if (typeof amounts[i] !== 'string') {
Expand Down
16 changes: 13 additions & 3 deletions src/errors/checkLocalTxInput/checkLocalRelayInput.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
// Copyright 2023 Parity Technologies (UK) Ltd.
// Copyright 2024 Parity Technologies (UK) Ltd.

import { Registry } from '../../registry';
import { checkLocalRelayInput } from './checkLocalRelayInput';
import { LocalTxType } from './types';

describe('checkLocalRelayInput', () => {
const registry = new Registry('statemine', { chainName: 'rococo' });

it('Should return LocalTxType.Balances', () => {
const res = checkLocalRelayInput([], ['10000']);
const res = checkLocalRelayInput([], ['10000'], registry);
expect(res).toEqual(LocalTxType.Balances);
});
it('Should error with an invalid input', () => {
expect(() => checkLocalRelayInput(['dot', 'ksm'], ['100000'])).toThrow(
expect(() => checkLocalRelayInput(['roc', 'ksm'], ['100000'], registry)).toThrow(
'Local transactions must have the `assetIds` input be a length of 1 or 0, and the `amounts` input be a length of 1',
);
});
it('Should correctly error when a non native asset is passed in', () => {
expect(() =>
checkLocalRelayInput(['{"parents":"3","interior":{"X1":{"GlobalConsensus":"Westend"}}}'], ['100000'], registry),
).toThrow(
'AssetId {"parents":"3","interior":{"X1":{"GlobalConsensus":"Westend"}}} is not the native asset of rococo relay chain. Expected an assetId of ROC or asset location {"parents":"0","interior":{"Here":""}}',
);
});
});
17 changes: 15 additions & 2 deletions src/errors/checkLocalTxInput/checkLocalRelayInput.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
// Copyright 2023 Parity Technologies (UK) Ltd.
// Copyright 2024 Parity Technologies (UK) Ltd.

import { RELAY_CHAIN_IDS, RELAY_CHAINS_NATIVE_ASSET_LOCATION } from '../../consts';
import { isRelayNativeAsset } from '../../createXcmTypes/util/isRelayNativeAsset';
import { Registry } from '../../registry';
import { BaseError, BaseErrorsEnum } from '../BaseError';
import { LocalTxType } from './types';

export const checkLocalRelayInput = (assetIds: string[], amounts: string[]): LocalTxType => {
export const checkLocalRelayInput = (assetIds: string[], amounts: string[], registry: Registry): LocalTxType => {
if (assetIds.length > 1 || amounts.length !== 1) {
throw new BaseError(
'Local transactions must have the `assetIds` input be a length of 1 or 0, and the `amounts` input be a length of 1',
BaseErrorsEnum.InvalidInput,
);
}
const relayChainId = RELAY_CHAIN_IDS[0];
const relayChain = registry.currentRelayRegistry[relayChainId];
const relayChainNativeAsset = relayChain.tokens[0];

const assetIsRelayNativeAsset = assetIds.length === 0 ? true : isRelayNativeAsset(registry, assetIds[0]);
if (!assetIsRelayNativeAsset) {
throw new BaseError(
`AssetId ${assetIds[0]} is not the native asset of ${relayChain.specName} relay chain. Expected an assetId of ${relayChainNativeAsset} or asset location ${RELAY_CHAINS_NATIVE_ASSET_LOCATION}`,
);
}

return LocalTxType.Balances;
};
2 changes: 1 addition & 1 deletion src/errors/checkLocalTxInput/checkLocalTxInput.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('checkLocalTxInput', () => {
it('Should correctly throw an error with an incorrect assetId', async () => {
await expect(async () => {
await checkLocalTxInput(systemAssetsApi.api, ['TST'], ['10000'], specName, registry, 2, false, false);
}).rejects.toThrow('assetId TST is not a valid symbol or integer asset id for statemine');
}).rejects.toThrow('assetId TST is not a valid symbol, integer asset id or location for statemine');
});
it("Should correctly throw an error when the integer assetId doesn't exist", async () => {
await expect(async () => {
Expand Down
32 changes: 24 additions & 8 deletions src/errors/checkXcmTxInputs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,19 @@ describe('checkAssetIds', () => {
'Polkadot',
['1', 'DOT'],
Direction.RelayToSystem,
`(RelayToSystem) asset 1 is not polkadot's native asset. Expected DOT`,
`(RelayToSystem) assetId 1 is not the native asset of polkadot relay chain. Expected an assetId of DOT or asset location {"parents":"0","interior":{"Here":""}}`,
],
[
'Kusama',
['DOT', 'KSM'],
Direction.RelayToSystem,
`(RelayToSystem) asset DOT is not kusama's native asset. Expected KSM`,
`(RelayToSystem) assetId DOT is not the native asset of kusama relay chain. Expected an assetId of KSM or asset location {"parents":"0","interior":{"Here":""}}`,
],
[
'Westend',
['WND', '100000'],
Direction.RelayToSystem,
`(RelayToSystem) asset 100000 is not westend's native asset. Expected WND`,
`(RelayToSystem) assetId 100000 is not the native asset of westend relay chain. Expected an assetId of WND or asset location {"parents":"0","interior":{"Here":""}}`,
],
];

Expand All @@ -134,13 +134,13 @@ describe('checkAssetIds', () => {
'Polkadot',
['1', 'DOT'],
Direction.RelayToPara,
`(RelayToPara) asset 1 is not polkadot's native asset. Expected DOT`,
`(RelayToPara) assetId 1 is not the native asset of polkadot relay chain. Expected an assetId of DOT or asset location {"parents":"0","interior":{"Here":""}}`,
],
[
'Kusama',
['DOT', 'KSM'],
Direction.RelayToPara,
`(RelayToPara) asset DOT is not kusama's native asset. Expected KSM`,
`(RelayToPara) assetId DOT is not the native asset of kusama relay chain. Expected an assetId of KSM or asset location {"parents":"0","interior":{"Here":""}}`,
],
];

Expand All @@ -149,9 +149,24 @@ describe('checkAssetIds', () => {

it('Should error when direction is SystemToRelay and an assetId is not native to the relay chain', async () => {
const tests: Test[] = [
['Statemint', ['0'], Direction.SystemToRelay, `(SystemToRelay) assetId 0 not native to polkadot`],
['Statemine', ['MOVR', 'KSM'], Direction.SystemToRelay, `(SystemToRelay) assetId MOVR not native to kusama`],
['Westmint', ['WND', '250'], Direction.SystemToRelay, `(SystemToRelay) assetId 250 not native to westend`],
[
'Statemint',
['0'],
Direction.SystemToRelay,
`(SystemToRelay) assetId 0 is not the native asset of polkadot relay chain. Expected an assetId of DOT or asset location {"parents":"1","interior":{"Here":""}}`,
],
[
'Statemine',
['MOVR', 'KSM'],
Direction.SystemToRelay,
`(SystemToRelay) assetId MOVR is not the native asset of kusama relay chain. Expected an assetId of KSM or asset location {"parents":"1","interior":{"Here":""}}`,
],
[
'Westmint',
['WND', '250'],
Direction.SystemToRelay,
`(SystemToRelay) assetId 250 is not the native asset of westend relay chain. Expected an assetId of WND or asset location {"parents":"1","interior":{"Here":""}}`,
],
];

await runTests(tests);
Expand Down Expand Up @@ -855,6 +870,7 @@ describe('checkBridgeTxInputs', () => {
);
});
});

describe('checkPaysWithFeeDestAssetIdIsInAssets', () => {
it('Should correctly error when a paysWithFeeDestAsset is not found in the assetIds list', () => {
const assetIds = [`{"parents":"2","interior":{"X1":{"GlobalConsensus":"Rococo"}}}`];
Expand Down
Loading

0 comments on commit 5a11307

Please sign in to comment.