diff --git a/README.md b/README.md index e6d20aa4..a35f4e09 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,16 @@ Find the full documentation [here](https://paritytech.github.io/asset-transfer-a The below chart is focusing on what directions are supported for constructing asset transfers and in what XCM version. The goal is to have everything in green checkmarks. Note that local transfers (intra-chain) are not visualized here. -| Direction | V2 | V3 | -| --------------------- | ------------------ | ------------------ | -| System to Parachain | ✅ | ✅ | -| System to Relay | ✅ | ✅ | -| Relay to Parachain | ✅ | ✅ | -| Relay to System | ✅ | ✅ | -| Parachain to Parachain | ✅ | ✅ | -| Parachain to Relay | ✅ | ✅ | -| Parachain to System | ✅ | ✅ | -| System to System | ✅ | ✅ | +| Direction | V2 | V3 | V4 | +| --------------------- | ------------------ | ------------------ | ------------------ | +| System to Parachain | ✅ | ✅ | ✅ | +| System to Relay | ✅ | ✅ | ✅ | +| Relay to Parachain | ✅ | ✅ | ✅ | +| Relay to System | ✅ | ✅ | ✅ | +| Parachain to Parachain | ✅ | ✅ | ✅ | +| Parachain to Relay | ✅ | ✅ | ✅ | +| Parachain to System | ✅ | ✅ | ✅ | +| System to System | ✅ | ✅ | ✅ | ## Note on Parachain to Parachain Support Parachain To Parachain support is currently limited to XCM V2, with the exception of Parachain primary asset tx construction (e.g. MOVR, SDN, etc.). diff --git a/docs/assets/navigation.js b/docs/assets/navigation.js index 6693c65a..4b51b023 100644 --- a/docs/assets/navigation.js +++ b/docs/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAA42Tb2vCMBCHv0tey5wy3ea7/XEw2NgQXwxERkivNqxNSu4KyvC7L9NamzS2fdf2Hp78cndd/TKCLbEZe0AEWhquMAbzkEs2YDmnxFYyHRUp4NAnrhLKUov9SBWx2XjARCLTyIBis1UPrUi5rQW07eeMxnf79X5QHSC0QjKFIEt+Gp1JhGb0ANQvfS5fVazPQqkITMzFBWfJe+7JtG/euFCCpCWC9s5bTG+c1oAx2uDwkSPM/x+bfTkS3xXRqykVjXNVZGcp2LeA0sXdE9xR0i4HbIY8fO6V7FkaOPTPD3VUVGU/xDlCtXZmgx85YXDyR5uPtg19uV0AFim16UqkTeP/GW7Cg6e0hUjPfH1/O5qMa/an03pB9KJNximsbmBd3jZZP8ObFjw93Wbpbknd1uS6zO9AiY4u6Mpil2MBG2k7smsJ5iBdvi+RBRa5rqsTYdt6v/4DUimcANsFAAA=" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE42Tb2vCMBCHv0tey5wy3ea7/XEw2NgQXwxERkivNqxNSu4KyvC7L9NamzS2fdf2Hp78cndd/TKCLbEZe0AEWhquMAbzkEs2YDmnxFYyHRUp4NAnrhLKUov9SBWx2XjARCLTyIBis1UPrUi5rQW07eeMxnf79X5QHSC0QjKFIEt+Gp1JhGb0ANQvfS5fVazPQqkITMzFBWfJe+7JtG/euFCCpCWC9s5bTG+c1oAx2uDwkSPM/x+bfTkS3xXRqykVjXNVZGcp2LeA0sXdE9xR0i4HbIY8fO6V7FkaOPTPD3VUVGU/xDlCtXZmgx85YXDyR5uPtg19uV0AFim16UqkTeP/GW7Cg6e0hUjPfH1/O5qMa/an03pB9KJNximsbmBd3jZZP8ObFjw93Wbpbknd1uS6zO9AiY4u6Mpil2MBG2k7smsJ5iBdvi+RBRa5rqsTYdt6v/4DUimcANsFAAA=" \ No newline at end of file diff --git a/docs/assets/search.js b/docs/assets/search.js index 357d8f03..4652d739 100644 --- a/docs/assets/search.js +++ b/docs/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAA7WbbY+bSBKA/wvz1XKGfvPMfMtdEilSdjdKZvdOikYRa7cTLhh8gCeTHeW/b3cDpgqqbTDsp4lDvXU91UU3NM9Bnn0vgrtPz8G3ON0Ed2wRpNFOB3fBy6LQ5X0epcVW5y/3cbAIDnliLuyyzSHRxYuuwPJruUuM1DqJzBVjNAh+Lhq7Ibs5b7nW7Fse4GoR7KNcpyVlvw2DXYs2jnWuo1I3gu5vtC7jLL0woKtTBoeF2jPsiz1O4zKOkvgv/UF/iYsy/3Fp0KSlmaPd6nL99Y3Wb9NtdmmcHRszR7jR62yjXz+VeZwW8frSIPtmZoizLdgsNXwO69IIvM+zXVzo3qwkZE5OTCZVOzH3MSIUp6XOt9HaY7YWPz0ZqZhBW7hm4ug+Aq1ghOurSm+w+0bZF0ax1+tf7T8viQUozxdQtNX/Xe/+0HkBm9OosLompgSnxKCK3B5S1/zo8IbV6eBCaidJ+WNvdLvTwv3vyYnQ9oJXca7xfUCnh11j43j1dLhVGIBoO83eZesoGWD7qhE84aFVoX19/FGUenefvY/yaIjLjvwMnj/oJPoxxnWjMIPv6u8Y50eNCd5t7oZnHElP9jo421h8st/hme7IT/Dsgh/juqsw3fdQyFh8rF92fbsKZdviTCfv9yinXnuHAuPaVNfVmyzfRSXppLo0zfy/m6auNyc89aSmOXVt9bhKR7cL6LUvNs3tL7r8mm1oX/W1aQ66a8jf9iXtjRIc51pJydvJ8PmzvT7O1dVR6dRsIIfkWSbF6f+0LZHehmhMPEvCytgAl/XQPHFmjzrP401/4zYqTsLKzHHmteX7S9kuOxamx9et+A/AAV3qSGLk9AI7o/unD7o4JG17AovvylEjMXJVCBO+0cVQB1e17KmUHmP21WEef4nJ7QTl8Cg9xeUW9/gzLo/SU1w+ndw4UW6fzu+Thrne9O7R56AChSmOd+5uMtTrUXqKy/JpqDsnOdIVmopNk8i/FOgO1/fbkZwwNc8WLuVqYAF3x+MJYR/9KP4Tl/YR2G/nZi4ZDWVg5sBene5g58J6NaCpDQyq0OnGTOKXm00+NiCsOkcwcfEu3sVmUTE2FKg4RyDfdfzla+lsjg0Fq14YzMll4/hAloPWkD0zcCjetc/2PqYfAY6LbdlaujTGM8u0fZ5l24/xX3MEC239Q+EOuh+T8Q6/Lw+cDd+03r9M4sfRmYOKcwRS1nLv4v8f4s199k2PTg5t4sLg2gW22WNkefHiX1GhX9t/9p6rVgKfjwIDH7Ee5YvX6aH77KZnE0ufvo33IiYf37xNH6Mk3rxN94fyAu9XHf1hoXQsnYzM7YcmRNbozxiZM/lrVr7JDunmktC6BubP2vsoSSal7Whgxtgqm1MS17MwY3Qf6xdWxvr7PHuMN/qiEGkzM8bpWloxMUrKyPxV6Ht/NKYQX53dCk6J8BezvYrt803rYWqrIY3NGLOzv09MaaW/p+b2pp2P4uLZdMbejJE3D5+mzH3Cxvz1QLxdHl8HA94vXxSjGfnbneG1MzYum/Y9C7Nm0KzP0ijBy6NR2cMG/oG1hNnJ6qKYgLa1MGN0r+Ii+jPRG7P4vLDwehZmrbo3uuoM7/YXT17KyIwx/p4Wh/0+y01Rv04f4zxLbYVfEqjX0oXRPizMxmWjn4K75+Cxbgp3AVvy5a2xs411srGnDquBGC/Zzvl7qK/9Ye59xpaRqEReXAeLT9cLLpahEA8Pi0+Nhrvg/sOJhT6xEIkx8ytc8NslkxyJMSTGzS9GiXEkJswvTokJJCbNL0GJSSSmzC9JDUEhsZX5pShrKyR2Y36tKLEbJGagfLqhxG5xem22bym5sMMh9I0ixCRCh+KaNIlhhDbpYUjaxDxCm/eQBBdiJKFNfUiyCzGV0GY/JPGFGExoAYSSlMRsQssgJCGGGE9oMYQkxxATYpZDSKJkGBGzIEISJuvMFguCkYwYZsTcjKFnFmbELAhmGF0vVzdYECNilgMjETGMiFkOjETEMCJmOTASEcOImOXASEQMI2KWA1tR5ckwIm45MBIRx4i45cBIRBwj4pYDJxHxTk+zHDiJiGNE3PU1uv9hRtyC4CQjjhlxC4KTjDhmxFe+AuEYEbccONlrOEbEb70dhGNEwnLgJHaBEQnLgZMzU2BEwiEisQuMSHDf0EXnxuMIkfUhMCHhbj5kfQhMSFgMgqwPgQkJy0GQ9SEwImE5CPoWiREJ/ywSGJG0HAR9P8WIpOUgyNkuMSLJvKUkMSJpQQiyQCRmJC0IQRaI7KwPHCOyQCRmJB0jkrvEjKQFIUnuEjOSFoQkuUvMSFoQkuQuMSNlQUiSu8KMlFsvkDQVZqQsCEnSVJiRsiAkyUhhRsqCkCQjhRkpC0KSjFRnGefWcSQjhRkpt5QjGSnMSFkQimSkMCNlQSiSkcKMVhaEIhmtMKOVBaFIRivMaGVBKJLRCjNaWRCKXsdiRisLQpGMVhUjt+Uwew2zhXlbbT3M5qF5QfgcfK73I6a71Nun58Dcme+efy4C4f7+bPch9pd14b4saHWB6o1fI3afULRaqtVa+bTsFjHNym21R2x127NOz4H0Rmm1mxdAnZCvW/3rarDhQCuZO1sBEgfGwXzD/9PsA6vNoXbvdVp106/akSiP+vGMv/G/bz4nANloTah6ML7RrNsjo80pDDAUDoYifRbcx1xNQkr4MVdrKQSGPHaqT4J0+0kQyAlIiVe7QKFzEDr3KrUPj0GoIFazY3Lp476S2tRPVrJ914wCEFa+MdfPB47VgAZ9A0btG4H72GurdXcugdELn2ofN5hETNQjX9VTn3vMtJ/F5ccTlsAkoOA1UB0kJdVBDvi110D1YLCXQQWysPJruyd3UfPkDqiDslv55k+jXj3OB1MQVJH0dYFamSxDCZqo8jGsDcTV61WgDNqZ9DXTWnlnH/Mn9WuJ/jgAAeWrwtrSvn4pCLQBAXWGwNN61zwBgxBAHpU3j0XSHP8B8wfUnvDlwJ5QiKoTCkATdHDpK9uk+uYI9A3gL/QBc1rHXlmdxgXlDiqO+Xp/cwwRTHYQr/BledecqgfuQItivgzt6rdAaZYe3FsgVyJF/w4M7lvKF3uamV7lDCT7ngUFFwC+tJubfwxfUQB1kH/l69fN6VyQPJBz7ou7OURO9igwcO5rE9XUIFcuwL/yq+dRme3dVzGg4kDCQt/cqFTz6vsloAvaS+jLVqVb1J8BAWXQFJiv4Ox5xe9xae9O3ZuzAO1J+JoK0CeogaQL32Rzp7QKd0oL+AZFL335zvW2dIfRgB5chPjG3NQHBVqBQSvfbGsMdFfjHChz32oCKqOJzkET5b6Z5UqkX2QMeGZ+z06XqBTAifmybT+6pbs/0A59Oa+PoUbuGCrgBcYsfPPafoXshIAiqG3fvGjUDOb98YAEmNJw/e1LWZUrYlKDW2bomxuNcn9agyoNvaOutQlcIPLQV6OlOxziGzvoScpXasfdU/6l6G6fOMi/8CWvMZC4U3xldYoPsAfNTfq6gz1fD9wCFeEr1PIpr79uAYpwiev11Z2RIZhVoW+Uh/Zlo4YvG0FPAVGvfM6rM6dJdSIZJAnuEXx3DzMtydUpgxs6H2Z6TnNQY9z5XQSSTMHDIjDbW53EqRH+9PDz59+fj+DLhkUAAA=="; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/docs/classes/AssetTransferApi.AssetTransferApi.html b/docs/classes/AssetTransferApi.AssetTransferApi.html index bc2ee51f..80e24dd8 100644 --- a/docs/classes/AssetTransferApi.AssetTransferApi.html +++ b/docs/classes/AssetTransferApi.AssetTransferApi.html @@ -2,7 +2,7 @@ construct transactions for assets and estimating fees.

import { AssetTransferApi, constructApiPromise } from '@substrate/asset-transfer-api'

const main = () => {
const { api, specName, safeXcmVersion } = await constructApiPromise('wss://some_ws_url');
const assetsApi = new AssetTransferApi(api, specName, safeXcmVersion);
}
-

Methods

Methods

  • assetIds: string[]

    Array of assetId's to be transferred

  • amounts: string[]

    Array of the amounts of each token to transfer

  • opts: TransferArgsOpts<T> = {}

    Options

    -
  • Returns Promise<TxResult<T>>

    • Decodes the hex of an extrinsic into a string readable format.

      +

    Returns Promise<TxResult<T>>

    Returns string

    Returns Promise<null | RuntimeDispatchInfo | RuntimeDispatchInfoV1>

    \ No newline at end of file +

    Returns Promise<void>

    \ No newline at end of file diff --git a/docs/enums/errors_BaseError.BaseErrorsEnum.html b/docs/enums/errors_BaseError.BaseErrorsEnum.html index f73a5c27..f72c4e0b 100644 --- a/docs/enums/errors_BaseError.BaseErrorsEnum.html +++ b/docs/enums/errors_BaseError.BaseErrorsEnum.html @@ -1,5 +1,5 @@ BaseErrorsEnum | @substrate/asset-transfer-api - v0.1.7

    Errors that may be returned by the API.

    -

    Enumeration Members

    Enumeration Members

    AssetNotFound: "AssetNotFound"

    Not able to find the asset.

    -
    DisabledOption: "DisabledOption"

    The following option is disabled given the inputs.

    -
    InternalError: "InternalError"

    An issue has happened internally.

    -
    InvalidAddress: "InvalidAddress"

    The inputted address is invalid.

    -
    InvalidAsset: "InvalidAsset"

    The inputted asset is incorrect or invalid, and does not exist given the surrounding specs. +

    DisabledOption: "DisabledOption"

    The following option is disabled given the inputs.

    +
    InternalError: "InternalError"

    An issue has happened internally.

    +
    InvalidAddress: "InvalidAddress"

    The inputted address is invalid.

    +
    InvalidAsset: "InvalidAsset"

    The inputted asset is incorrect or invalid, and does not exist given the surrounding specs. This exlcudes MultiLocation assets which are handled using InvalidMultiLocationAsset.

    -
    InvalidDirection: "InvalidDirection"

    The direction in which these assets are going to be sent is incorrect.

    -
    InvalidInput: "InvalidInput"

    An input or lack of input to any public facing function by the user is incorrect, and or invalid. +

    InvalidDirection: "InvalidDirection"

    The direction in which these assets are going to be sent is incorrect.

    +
    InvalidInput: "InvalidInput"

    An input or lack of input to any public facing function by the user is incorrect, and or invalid. This may include using options incorrectly.

    -
    InvalidMultiLocationAsset: "InvalidMultiLocationAsset"

    The inputted multilocation is incorrect.

    -
    InvalidPallet: "InvalidPallet"

    The following pallet does not support the method to be used.

    -
    InvalidXcmVersion: "InvalidXcmVersion"

    The xcm version is invalid.

    -
    MultipleNonUniqueAssetsFound: "MultipleNonUniqueAssetsFound"

    Multiple assets have been found for a single token symbol.

    -
    NoFeeAssetLpFound: "NoFeeAssetLpFound"

    The provided paysWithFeeOrigin asset has no liquidity pool.

    -
    NotImplemented: "NotImplemented"

    Not Implemented yet.

    -
    PalletNotFound: "PalletNotFound"

    The following pallet is not found.

    -
    RegistryNotFound: "RegistryNotFound"

    Not able to find the pertinent registry to gather certain information. This can refer to xcAssets.

    -
    SpecNameNotProvided: "SpecNameNotProvided"

    The specName was not provided when injecting a new chain in the registry.

    -
    TokensNotProvided: "TokensNotProvided"

    The tokens were not provided when injecting a new chain in the registry.

    -
    UnsupportedEnvironment: "UnsupportedEnvironment"

    The provided JS environment is not supported, and the api will not run.

    -
    \ No newline at end of file +
    InvalidMultiLocationAsset: "InvalidMultiLocationAsset"

    The inputted multilocation is incorrect.

    +
    InvalidPallet: "InvalidPallet"

    The following pallet does not support the method to be used.

    +
    InvalidXcmVersion: "InvalidXcmVersion"

    The xcm version is invalid.

    +
    MultipleNonUniqueAssetsFound: "MultipleNonUniqueAssetsFound"

    Multiple assets have been found for a single token symbol.

    +
    NoFeeAssetLpFound: "NoFeeAssetLpFound"

    The provided paysWithFeeOrigin asset has no liquidity pool.

    +
    NotImplemented: "NotImplemented"

    Not Implemented yet.

    +
    PalletNotFound: "PalletNotFound"

    The following pallet is not found.

    +
    RegistryNotFound: "RegistryNotFound"

    Not able to find the pertinent registry to gather certain information. This can refer to xcAssets.

    +
    SpecNameNotProvided: "SpecNameNotProvided"

    The specName was not provided when injecting a new chain in the registry.

    +
    TokensNotProvided: "TokensNotProvided"

    The tokens were not provided when injecting a new chain in the registry.

    +
    UnsupportedEnvironment: "UnsupportedEnvironment"

    The provided JS environment is not supported, and the api will not run.

    +
    \ No newline at end of file diff --git a/docs/enums/types.Direction.html b/docs/enums/types.Direction.html index f1e47d93..daa7c5e1 100644 --- a/docs/enums/types.Direction.html +++ b/docs/enums/types.Direction.html @@ -1,5 +1,5 @@ Direction | @substrate/asset-transfer-api - v0.1.7

    Represents all possible tx directions

    -

    Enumeration Members

    Enumeration Members

    Local: "Local"

    Local tx

    -
    ParaToPara: "ParaToPara"

    Parachain to Parachain.

    -
    ParaToRelay: "ParaToRelay"

    Parachain to Relay chain.

    -
    ParaToSystem: "ParaToSystem"

    Parachain to System parachain.

    -
    RelayToPara: "RelayToPara"

    Relay chain to Parachain.

    -
    RelayToSystem: "RelayToSystem"

    Relay to System Parachain.

    -
    SystemToPara: "SystemToPara"

    System parachain to Parachain.

    -
    SystemToRelay: "SystemToRelay"

    System parachain to Relay chain.

    -
    SystemToSystem: "SystemToSystem"

    System parachain to System parachain chain.

    -
    \ No newline at end of file +
    ParaToPara: "ParaToPara"

    Parachain to Parachain.

    +
    ParaToRelay: "ParaToRelay"

    Parachain to Relay chain.

    +
    ParaToSystem: "ParaToSystem"

    Parachain to System parachain.

    +
    RelayToPara: "RelayToPara"

    Relay chain to Parachain.

    +
    RelayToSystem: "RelayToSystem"

    Relay to System Parachain.

    +
    SystemToPara: "SystemToPara"

    System parachain to Parachain.

    +
    SystemToRelay: "SystemToRelay"

    System parachain to Relay chain.

    +
    SystemToSystem: "SystemToSystem"

    System parachain to System parachain chain.

    +
    \ No newline at end of file diff --git a/docs/functions/constructApiPromise.constructApiPromise.html b/docs/functions/constructApiPromise.constructApiPromise.html index cc04e028..240d2ed4 100644 --- a/docs/functions/constructApiPromise.constructApiPromise.html +++ b/docs/functions/constructApiPromise.constructApiPromise.html @@ -3,4 +3,4 @@

    Parameters

    Returns Promise<ApiInfo>

    \ No newline at end of file +

    Returns Promise<ApiInfo>

    \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 4266967c..d855b8c3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -25,47 +25,56 @@

    Asset API used for common good parachains

    Direction V2 V3 +V4 System to Parachain ✅ ✅ +✅ System to Relay ✅ ✅ +✅ Relay to Parachain ✅ ✅ +✅ Relay to System ✅ ✅ +✅ Parachain to Parachain ✅ ✅ +✅ Parachain to Relay ✅ ✅ +✅ Parachain to System ✅ ✅ +✅ System to System ✅ ✅ +✅

    Note on Parachain to Parachain Support

    Parachain To Parachain support is currently limited to XCM V2, with the exception of Parachain primary asset tx construction (e.g. MOVR, SDN, etc.).

    diff --git a/docs/interfaces/constructApiPromise.ApiInfo.html b/docs/interfaces/constructApiPromise.ApiInfo.html index 83326490..132c73ac 100644 --- a/docs/interfaces/constructApiPromise.ApiInfo.html +++ b/docs/interfaces/constructApiPromise.ApiInfo.html @@ -1,8 +1,8 @@ ApiInfo | @substrate/asset-transfer-api - v0.1.7

    Return value for constructApiPromise

    -
    interface ApiInfo {
        api: ApiPromise;
        safeXcmVersion: number;
        specName: string;
    }

    Properties

    api +
    interface ApiInfo {
        api: ApiPromise;
        safeXcmVersion: number;
        specName: string;
    }

    Properties

    api: ApiPromise

    Polkadot-js ApiPromise

    -
    safeXcmVersion: number

    SafeXcmVersion for the chain which the api is connected too.

    -
    specName: string

    SpecName of the chain which the api is connected to.

    -
    \ No newline at end of file +
    safeXcmVersion: number

    SafeXcmVersion for the chain which the api is connected too.

    +
    specName: string

    SpecName of the chain which the api is connected to.

    +
    \ No newline at end of file diff --git a/docs/interfaces/types.TransferArgsOpts.html b/docs/interfaces/types.TransferArgsOpts.html index 5fb918ed..c303ddc2 100644 --- a/docs/interfaces/types.TransferArgsOpts.html +++ b/docs/interfaces/types.TransferArgsOpts.html @@ -1,5 +1,5 @@ TransferArgsOpts | @substrate/asset-transfer-api - v0.1.7

    The TransferArgsOpts are the options passed into createTransferTransaction.

    -
    interface TransferArgsOpts {
        format?: T;
        isLimited?: boolean;
        keepAlive?: boolean;
        paysWithFeeDest?: string;
        paysWithFeeOrigin?: string;
        sendersAddr?: string;
        transferLiquidToken?: boolean;
        weightLimit?: {
            proofSize?: string;
            refTime?: string;
        };
        xcmVersion?: number;
    }

    Type Parameters

    Properties

    interface TransferArgsOpts {
        format?: T;
        isLimited?: boolean;
        keepAlive?: boolean;
        paysWithFeeDest?: string;
        paysWithFeeOrigin?: string;
        sendersAddr?: string;
        transferLiquidToken?: boolean;
        weightLimit?: {
            proofSize?: string;
            refTime?: string;
        };
        xcmVersion?: number;
    }

    Type Parameters

    Properties

    format? isLimited? keepAlive? paysWithFeeDest? @@ -12,24 +12,24 @@ It can either be a payload, call, or submittable.

    Note: A submittable will return a SubmittableExtrinsic polkadot-js type, whereas a payload or call will return a hex.

    -
    isLimited?: boolean

    Boolean to declare if this will be with limited XCM transfers. +

    isLimited?: boolean

    Boolean to declare if this will be with limited XCM transfers. Deafult is unlimited.

    -
    keepAlive?: boolean

    For creating local asset transfers, this will allow for a transferKeepAlive as oppose +

    keepAlive?: boolean

    For creating local asset transfers, this will allow for a transferKeepAlive as oppose to a transfer.

    -
    paysWithFeeDest?: string

    AssetId to pay fee's on the destination parachain.

    -
    paysWithFeeOrigin?: string

    AssetId to pay fee's on the current common good parachain. +

    paysWithFeeDest?: string

    AssetId to pay fee's on the destination parachain.

    +
    paysWithFeeOrigin?: string

    AssetId to pay fee's on the current common good parachain. Polkadot AssetHub: default DOT Kusama AssetHub: default KSM

    -
    sendersAddr?: string

    The SS58 Address the tx will be sent from. This is specifically used for the format payload. +

    sendersAddr?: string

    The SS58 Address the tx will be sent from. This is specifically used for the format payload. It is necessary because the payload will need information such as the nonce.

    -
    transferLiquidToken?: boolean

    Boolean to declare if this will transfer liquidity tokens. +

    transferLiquidToken?: boolean

    Boolean to declare if this will transfer liquidity tokens. Default is false.

    -
    weightLimit?: {
        proofSize?: string;
        refTime?: string;
    }

    When isLimited is true, the option for applying a weightLimit is possible. +

    weightLimit?: {
        proofSize?: string;
        refTime?: string;
    }

    When isLimited is true, the option for applying a weightLimit is possible. If not inputted it will default to Unlimited.

    Type declaration

    • Optional proofSize?: string

      Provided when creating limited txs, represents the amount of storage in bytes that can be used by the tx

    • Optional refTime?: string

      Provided when creating limited txs, represents the allowed amount of computation time the tx can use

      -
    xcmVersion?: number

    Set the xcmVersion for message construction. If this is not present a supported version +

    xcmVersion?: number

    Set the xcmVersion for message construction. If this is not present a supported version will be queried, and if there is no supported version a safe version will be queried.

    -
    \ No newline at end of file +
    \ No newline at end of file diff --git a/docs/interfaces/types.TxResult.html b/docs/interfaces/types.TxResult.html index a256c69c..b612616f 100644 --- a/docs/interfaces/types.TxResult.html +++ b/docs/interfaces/types.TxResult.html @@ -1,6 +1,6 @@ TxResult | @substrate/asset-transfer-api - v0.1.7

    The TxResult is the result of constructing a transaction. T extends Format in the context of the options passed in for the Format the user expects.

    -
    interface TxResult {
        dest: string;
        direction: Direction | "local";
        format: Format | "local";
        method: Methods;
        origin: string;
        tx: ConstructedFormat<T>;
        xcmVersion: null | number;
    }

    Type Parameters

    • T

    Properties

    interface TxResult {
        dest: string;
        direction: Direction | "local";
        format: Format | "local";
        method: Methods;
        origin: string;
        tx: ConstructedFormat<T>;
        xcmVersion: null | number;
    }

    Type Parameters

    • T

    Properties

    Properties

    dest: string

    Description

    The destination specName of the transaction

    -
    direction: Direction | "local"

    Description

    The direction of the cross chain transfer.

    -
    format: Format | "local"

    Description

    The format type the tx is ouputted in.

    -
    method: Methods

    Description

    The method used in the transaction.

    -
    origin: string

    Description

    The origin specName of the transaction

    -

    Description

    The constructed transaction.

    -
    xcmVersion: null | number

    Description

    The xcm version that was used to construct the tx.

    -
    \ No newline at end of file +
    direction: Direction | "local"

    Description

    The direction of the cross chain transfer.

    +
    format: Format | "local"

    Description

    The format type the tx is ouputted in.

    +
    method: Methods

    Description

    The method used in the transaction.

    +
    origin: string

    Description

    The origin specName of the transaction

    +
    tx: ConstructedFormat<T>

    Description

    The constructed transaction.

    +
    xcmVersion: null | number

    Description

    The xcm version that was used to construct the tx.

    +
    \ No newline at end of file diff --git a/docs/modules/AssetTransferApi.html b/docs/modules/AssetTransferApi.html index a77c9a70..ba56d47c 100644 --- a/docs/modules/AssetTransferApi.html +++ b/docs/modules/AssetTransferApi.html @@ -1,2 +1,2 @@ -AssetTransferApi | @substrate/asset-transfer-api - v0.1.7

    Index

    Classes

    AssetTransferApi +AssetTransferApi | @substrate/asset-transfer-api - v0.1.7
    \ No newline at end of file diff --git a/docs/modules/constructApiPromise.html b/docs/modules/constructApiPromise.html index dcd9205f..b942ebcd 100644 --- a/docs/modules/constructApiPromise.html +++ b/docs/modules/constructApiPromise.html @@ -1,3 +1,3 @@ -constructApiPromise | @substrate/asset-transfer-api - v0.1.7

    Index

    Interfaces

    ApiInfo +constructApiPromise | @substrate/asset-transfer-api - v0.1.7
    \ No newline at end of file diff --git a/docs/modules/errors_BaseError.html b/docs/modules/errors_BaseError.html index 5e17e149..5efd4e01 100644 --- a/docs/modules/errors_BaseError.html +++ b/docs/modules/errors_BaseError.html @@ -1,2 +1,2 @@ -errors/BaseError | @substrate/asset-transfer-api - v0.1.7

    Index

    Enumerations

    BaseErrorsEnum +errors/BaseError | @substrate/asset-transfer-api - v0.1.7
    \ No newline at end of file diff --git a/docs/modules/types.html b/docs/modules/types.html index 37cf574c..d792c6bd 100644 --- a/docs/modules/types.html +++ b/docs/modules/types.html @@ -1,4 +1,4 @@ -types | @substrate/asset-transfer-api - v0.1.7

    Index

    Enumerations

    Direction +types | @substrate/asset-transfer-api - v0.1.7

    Index

    Enumerations

    Interfaces

    Type Aliases

    AssetTransferApiOpts diff --git a/docs/types/types.AssetTransferApiOpts.html b/docs/types/types.AssetTransferApiOpts.html index be4f6031..d9d9979c 100644 --- a/docs/types/types.AssetTransferApiOpts.html +++ b/docs/types/types.AssetTransferApiOpts.html @@ -3,4 +3,4 @@
  • Optional overrideRegistry?: RequireAtLeastOne<ChainInfoRegistry<InjectedChainInfoKeys>>

    Option to override the registry with the supplied chain information.

  • Optional registryType?: RegistryTypes

    Whether or not to apply the registry from the npm package asset-transfer-api-registry, or the hosted CDN which updates frequently.

    -
  • \ No newline at end of file +
    \ No newline at end of file diff --git a/docs/types/types.ConstructedFormat.html b/docs/types/types.ConstructedFormat.html index d715b67b..4c2ad71b 100644 --- a/docs/types/types.ConstructedFormat.html +++ b/docs/types/types.ConstructedFormat.html @@ -1,2 +1,2 @@ ConstructedFormat | @substrate/asset-transfer-api - v0.1.7
    ConstructedFormat<T>: T extends "payload"
        ? `0x${string}`
        : T extends "call"
            ? `0x${string}`
            : T extends "submittable"
                ? SubmittableExtrinsic<"promise", ISubmittableResult>
                : never

    The Format types possible for a constructed transaction.

    -

    Type Parameters

    • T

    \ No newline at end of file +

    Type Parameters

    • T

    \ No newline at end of file diff --git a/docs/types/types.Format.html b/docs/types/types.Format.html index e7abb6bc..dd7a9a8b 100644 --- a/docs/types/types.Format.html +++ b/docs/types/types.Format.html @@ -4,4 +4,4 @@
  • call: This returns a Polkadot-js Call as a hex.
  • submittable: This returns a Polkadot-js SubmittableExtrinsic.
  • -
    \ No newline at end of file +
    \ No newline at end of file diff --git a/docs/types/types.LocalTransferTypes.html b/docs/types/types.LocalTransferTypes.html index ea2bea28..23435119 100644 --- a/docs/types/types.LocalTransferTypes.html +++ b/docs/types/types.LocalTransferTypes.html @@ -1,2 +1,2 @@ LocalTransferTypes | @substrate/asset-transfer-api - v0.1.7
    LocalTransferTypes: "assets::transfer" | "assets::transferKeepAlive" | "foreignAssets::transfer" | "foreignAssets::transferKeepAlive" | "balances::transfer" | "balances::transferKeepAlive" | "poolAssets::transfer" | "poolAssets::transferKeepAlive" | "tokens::transfer" | "tokens::transferKeepAlive"

    The types of local transactions the api can construct.

    -
    \ No newline at end of file +
    \ No newline at end of file diff --git a/docs/types/types.Methods.html b/docs/types/types.Methods.html index 8d3caec6..b71639e2 100644 --- a/docs/types/types.Methods.html +++ b/docs/types/types.Methods.html @@ -1,2 +1,2 @@ Methods | @substrate/asset-transfer-api - v0.1.7
    Methods: LocalTransferTypes | "reserveTransferAssets" | "limitedReserveTransferAssets" | "teleportAssets" | "limitedTeleportAssets" | "transferMultiasset" | "transferMultiassets" | "transferMultiassetWithFee"

    The Methods are the collections of methods the API will use to construct a transaction.

    -
    \ No newline at end of file +
    \ No newline at end of file diff --git a/docs/types/types.RegistryTypes.html b/docs/types/types.RegistryTypes.html index 6fbaf9af..2a56d9c1 100644 --- a/docs/types/types.RegistryTypes.html +++ b/docs/types/types.RegistryTypes.html @@ -1,4 +1,4 @@ RegistryTypes | @substrate/asset-transfer-api - v0.1.7
    RegistryTypes: "CDN" | "NPM"

    Types that the registry can be initialized as.

    CDN - The registry will be initialized with the up to date version given the CDN NPM - The registry will be initialized with the NPM version which is updated less frequently.

    -
    \ No newline at end of file +
    \ No newline at end of file diff --git a/docs/types/types.XcmDirection.html b/docs/types/types.XcmDirection.html index 5940e39f..79c34aed 100644 --- a/docs/types/types.XcmDirection.html +++ b/docs/types/types.XcmDirection.html @@ -1,2 +1,2 @@ XcmDirection | @substrate/asset-transfer-api - v0.1.7
    XcmDirection: Exclude<Direction, "Local">

    The direction of the cross chain transfer. This only concerns XCM transactions.

    -
    \ No newline at end of file +
    \ No newline at end of file diff --git a/examples/WestendAssetHubToWestendCollectivesXcmV4.ts b/examples/WestendAssetHubToWestendCollectivesXcmV4.ts new file mode 100644 index 00000000..6b0c867a --- /dev/null +++ b/examples/WestendAssetHubToWestendCollectivesXcmV4.ts @@ -0,0 +1,45 @@ +/** + * When importing from @substrate/asset-transfer-api it would look like the following + * + * import { AssetTransferApi, constructApiPromise } from '@substrate/asset-transfer-api' + */ +import { AssetTransferApi, constructApiPromise } from '../src'; +import { TxResult } from '../src/types'; +import { GREEN, PURPLE, RESET } from './colors'; + +/** + * In this example we are creating a `teleportAssets` call to send 1 WND from a Westend AssetHub (System Parachain) account + * to a Westend Collectives (System Parachain) account, where the `xcmVersion` is set to `4` and `isLimited` is set to false. + * + * NOTE: When `isLimited` is true it will use the `limited` version of the either `reserveAssetTransfer`, or `teleportAssets`. + */ +const main = async () => { + const { api, specName, safeXcmVersion } = await constructApiPromise('wss://westend-asset-hub-rpc.polkadot.io'); + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion); + let callInfo: TxResult<'call'>; + try { + callInfo = await assetApi.createTransferTransaction( + '1001', + '5EWNeodpcQ6iYibJ3jmWVe85nsok1EDG8Kk3aFg8ZzpfY1qX', + ['WND'], + ['1000000000000'], + { + format: 'call', + isLimited: false, + xcmVersion: 4, + }, + ); + + console.log(`${PURPLE}The following call data that is returned:\n${GREEN}${JSON.stringify(callInfo, null, 4)}`); + } catch (e) { + console.error(e); + throw Error(e as string); + } + + const decoded = assetApi.decodeExtrinsic(callInfo.tx, 'call'); + console.log(`\n${PURPLE}The following decoded tx:\n${GREEN} ${JSON.stringify(JSON.parse(decoded), null, 4)}${RESET}`); +}; + +main() + .catch((err) => console.error(err)) + .finally(() => process.exit()); diff --git a/examples/WestendAssetHubToWestendXcmV4.ts b/examples/WestendAssetHubToWestendXcmV4.ts new file mode 100644 index 00000000..89df528b --- /dev/null +++ b/examples/WestendAssetHubToWestendXcmV4.ts @@ -0,0 +1,46 @@ +/** + * When importing from @substrate/asset-transfer-api it would look like the following + * + * import { AssetTransferApi, constructApiPromise } from '@substrate/asset-transfer-api' + */ +import { AssetTransferApi, constructApiPromise } from '../src'; +import { TxResult } from '../src/types'; +import { GREEN, PURPLE, RESET } from './colors'; + +/** + * In this example we are creating a `limitedTeleportAssets` call to send 1 WND from a Westend AssetHub (System Parachain) account + * to a Westend (Relay Chain) account, where the `xcmVersion` is set to `4`, `isLimited` is set to true and no `weightLimit` value is given declaring that + * we will allow `unlimited` weight to be used for the tx. + * + * NOTE: When `isLimited` is true it will use the `limited` version of the either `reserveAssetTransfer`, or `teleportAssets`. + */ +const main = async () => { + const { api, specName, safeXcmVersion } = await constructApiPromise('wss://westend-asset-hub-rpc.polkadot.io'); + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion); + let callInfo: TxResult<'call'>; + try { + callInfo = await assetApi.createTransferTransaction( + '0', + '5EWNeodpcQ6iYibJ3jmWVe85nsok1EDG8Kk3aFg8ZzpfY1qX', + ['WND'], + ['1000000000000'], + { + format: 'call', + isLimited: true, + xcmVersion: 4, + }, + ); + + console.log(`${PURPLE}The following call data that is returned:\n${GREEN}${JSON.stringify(callInfo, null, 4)}`); + } catch (e) { + console.error(e); + throw Error(e as string); + } + + const decoded = assetApi.decodeExtrinsic(callInfo.tx, 'call'); + console.log(`\n${PURPLE}The following decoded tx:\n${GREEN} ${JSON.stringify(JSON.parse(decoded), null, 4)}${RESET}`); +}; + +main() + .catch((err) => console.error(err)) + .finally(() => process.exit()); diff --git a/examples/WestendToAssetHubXcmV4.ts b/examples/WestendToAssetHubXcmV4.ts new file mode 100644 index 00000000..0d0718f8 --- /dev/null +++ b/examples/WestendToAssetHubXcmV4.ts @@ -0,0 +1,46 @@ +/** + * When importing from @substrate/asset-transfer-api it would look like the following + * + * import { AssetTransferApi, constructApiPromise } from '@substrate/asset-transfer-api' + */ +import { AssetTransferApi, constructApiPromise } from '../src'; +import { TxResult } from '../src/types'; +import { GREEN, PURPLE, RESET } from './colors'; + +/** + * In this example we are creating a `limitedTeleportAssets` call to send 1 WND from a Westend (Relay Chain) account + * to a Westend AssetHub (System Parachain) account, where the `xcmVersion` is set to `4`, `isLimited` is set to true and no `weightLimit` value is given declaring that + * we will allow `unlimited` weight to be used for the tx. + * + * NOTE: When `isLimited` is true it will use the `limited` version of the either `reserveAssetTransfer`, or `teleportAssets`. + */ +const main = async () => { + const { api, specName, safeXcmVersion } = await constructApiPromise('wss://westend-rpc.polkadot.io'); + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion); + let callInfo: TxResult<'call'>; + try { + callInfo = await assetApi.createTransferTransaction( + '1000', + '5EWNeodpcQ6iYibJ3jmWVe85nsok1EDG8Kk3aFg8ZzpfY1qX', + ['WND'], + ['1000000000000'], + { + format: 'call', + isLimited: true, + xcmVersion: 4, + }, + ); + + console.log(`${PURPLE}The following call data that is returned:\n${GREEN}${JSON.stringify(callInfo, null, 4)}`); + } catch (e) { + console.error(e); + throw Error(e as string); + } + + const decoded = assetApi.decodeExtrinsic(callInfo.tx, 'call'); + console.log(`\n${PURPLE}The following decoded tx:\n${GREEN} ${JSON.stringify(JSON.parse(decoded), null, 4)}${RESET}`); +}; + +main() + .catch((err) => console.error(err)) + .finally(() => process.exit()); diff --git a/src/AssetTransferApi.spec.ts b/src/AssetTransferApi.spec.ts index 3b92f629..a9a53eb7 100644 --- a/src/AssetTransferApi.spec.ts +++ b/src/AssetTransferApi.spec.ts @@ -723,7 +723,7 @@ describe('AssetTransferAPI', () => { describe('feeAssetItem', () => { it('Should correctly set the feeAssetItem when paysWithFeeDest option is provided for a limitedReserveTransferAssets call', async () => { const expected = - '{"args":{"dest":{"V3":{"parents":"1","interior":{"X1":{"Parachain":"2,000"}}}},"beneficiary":{"V3":{"parents":"0","interior":{"X1":{"AccountId32":{"network":null,"id":"0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b"}}}}},"assets":{"V3":[{"id":{"Concrete":{"parents":"1","interior":"Here"}},"fun":{"Fungible":"30,000,000,000,000"}},{"id":{"Concrete":{"parents":"0","interior":{"X2":[{"PalletInstance":"50"},{"GeneralIndex":"11"}]}}},"fun":{"Fungible":"10,000,000,000,000"}}]},"fee_asset_item":"1","weight_limit":{"Limited":{"refTime":"1,000","proofSize":"1,000"}}},"method":"limitedReserveTransferAssets","section":"polkadotXcm"}'; + '{"args":{"dest":{"V3":{"parents":"1","interior":{"X1":{"Parachain":"2,000"}}}},"beneficiary":{"V3":{"parents":"0","interior":{"X1":{"AccountId32":{"network":null,"id":"0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b"}}}}},"assets":{"V3":[{"id":{"Concrete":{"parents":"0","interior":{"X2":[{"PalletInstance":"50"},{"GeneralIndex":"11"}]}}},"fun":{"Fungible":"10,000,000,000,000"}},{"id":{"Concrete":{"parents":"1","interior":"Here"}},"fun":{"Fungible":"30,000,000,000,000"}}]},"fee_asset_item":"0","weight_limit":{"Limited":{"refTime":"1,000","proofSize":"1,000"}}},"method":"limitedReserveTransferAssets","section":"polkadotXcm"}'; const callTxResult = await systemAssetsApi.createTransferTransaction( '2000', '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', @@ -745,7 +745,7 @@ describe('AssetTransferAPI', () => { it('Should correctly set the feeAssetItem when paysWithFeeDest option is provided for a reserveTransferAssets call', async () => { const expected = - '{"args":{"dest":{"V3":{"parents":"1","interior":{"X1":{"Parachain":"2,000"}}}},"beneficiary":{"V3":{"parents":"0","interior":{"X1":{"AccountId32":{"network":null,"id":"0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b"}}}}},"assets":{"V3":[{"id":{"Concrete":{"parents":"1","interior":"Here"}},"fun":{"Fungible":"100"}},{"id":{"Concrete":{"parents":"0","interior":{"X2":[{"PalletInstance":"50"},{"GeneralIndex":"10"}]}}},"fun":{"Fungible":"2,000"}}]},"fee_asset_item":"1"},"method":"reserveTransferAssets","section":"polkadotXcm"}'; + '{"args":{"dest":{"V3":{"parents":"1","interior":{"X1":{"Parachain":"2,000"}}}},"beneficiary":{"V3":{"parents":"0","interior":{"X1":{"AccountId32":{"network":null,"id":"0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b"}}}}},"assets":{"V3":[{"id":{"Concrete":{"parents":"0","interior":{"X2":[{"PalletInstance":"50"},{"GeneralIndex":"10"}]}}},"fun":{"Fungible":"2,000"}},{"id":{"Concrete":{"parents":"1","interior":"Here"}},"fun":{"Fungible":"100"}}]},"fee_asset_item":"0"},"method":"reserveTransferAssets","section":"polkadotXcm"}'; const callTxResult = await systemAssetsApi.createTransferTransaction( '2000', '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', diff --git a/src/consts.ts b/src/consts.ts index 21b70d2c..205f09f2 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -43,10 +43,10 @@ export const ROCOCO_ASSET_HUB_SPEC_NAME = ['asset-hub-rococo']; */ export const DEFAULT_XCM_VERSION = 2; /** - * There should only ever be two supported versions. - * Therefore supported xcm versions should have a fixed length of 2 + * There should only ever be three supported versions. + * Therefore supported xcm versions will always have a fixed length of 3. */ -export const SUPPORTED_XCM_VERSIONS: [number, number] = [2, 3]; +export const SUPPORTED_XCM_VERSIONS: [number, number, number] = [2, 3, 4]; /** * The current maximum number of assets that can be transferred in an extrinsic * https://github.com/paritytech/polkadot/blob/e0ed7e862c8c8b6c75eda1731c449543642176ef/xcm/pallet-xcm/src/lib.rs#L1131 diff --git a/src/createXcmCalls/polkadotXcm/limitedReserveTransferAssets.spec.ts b/src/createXcmCalls/polkadotXcm/limitedReserveTransferAssets.spec.ts index aea607ba..668787c2 100644 --- a/src/createXcmCalls/polkadotXcm/limitedReserveTransferAssets.spec.ts +++ b/src/createXcmCalls/polkadotXcm/limitedReserveTransferAssets.spec.ts @@ -91,7 +91,7 @@ describe('limitedReserveTransferAssets', () => { isLiquidTokenTransfer, isForeignAssetsTransfer, }); - }).rejects.toThrowError("Can't find the `polkadotXcm` or `xcmPallet` pallet with the given API"); + }).rejects.toThrow("Can't find the `polkadotXcm` or `xcmPallet` pallet with the given API"); }); it('Should correctly construct a foreign asset tx for a system parachain with V2', async () => { diff --git a/src/createXcmCalls/polkadotXcm/limitedTeleportAssets.spec.ts b/src/createXcmCalls/polkadotXcm/limitedTeleportAssets.spec.ts index a644c391..5d991d4a 100644 --- a/src/createXcmCalls/polkadotXcm/limitedTeleportAssets.spec.ts +++ b/src/createXcmCalls/polkadotXcm/limitedTeleportAssets.spec.ts @@ -55,7 +55,7 @@ describe('limitedTeleportAssets', () => { isLiquidTokenTransfer, isForeignAssetsTransfer, }); - }).rejects.toThrowError("Can't find the `polkadotXcm` or `xcmPallet` pallet with the given API"); + }).rejects.toThrow("Can't find the `polkadotXcm` or `xcmPallet` pallet with the given API"); }); }); }); diff --git a/src/createXcmCalls/polkadotXcm/reserveTransferAssets.spec.ts b/src/createXcmCalls/polkadotXcm/reserveTransferAssets.spec.ts index 31beb699..3f804230 100644 --- a/src/createXcmCalls/polkadotXcm/reserveTransferAssets.spec.ts +++ b/src/createXcmCalls/polkadotXcm/reserveTransferAssets.spec.ts @@ -53,7 +53,7 @@ describe('reserveTransferAssets', () => { isLiquidTokenTransfer, isForeignAssetsTransfer, }); - }).rejects.toThrowError("Can't find the `polkadotXcm` or `xcmPallet` pallet with the given API"); + }).rejects.toThrow("Can't find the `polkadotXcm` or `xcmPallet` pallet with the given API"); }); it('Should correctly construct a foreign asset tx for a system parachain with V2', async () => { diff --git a/src/createXcmCalls/polkadotXcm/teleportAssets.spec.ts b/src/createXcmCalls/polkadotXcm/teleportAssets.spec.ts index 7059c26d..a4942f12 100644 --- a/src/createXcmCalls/polkadotXcm/teleportAssets.spec.ts +++ b/src/createXcmCalls/polkadotXcm/teleportAssets.spec.ts @@ -46,7 +46,7 @@ describe('teleportAssets', () => { isLiquidTokenTransfer, isForeignAssetsTransfer, }); - }).rejects.toThrowError("Can't find the `polkadotXcm` or `xcmPallet` pallet with the given API"); + }).rejects.toThrow("Can't find the `polkadotXcm` or `xcmPallet` pallet with the given API"); }); }); }); diff --git a/src/createXcmCalls/xTokens/transferMultiassets.ts b/src/createXcmCalls/xTokens/transferMultiassets.ts index 06045704..61cf633b 100644 --- a/src/createXcmCalls/xTokens/transferMultiassets.ts +++ b/src/createXcmCalls/xTokens/transferMultiassets.ts @@ -4,7 +4,7 @@ import type { SubmittableExtrinsic } from '@polkadot/api/submittable/types'; import type { ISubmittableResult } from '@polkadot/types/types'; import { createXcmTypes } from '../../createXcmTypes'; -import type { XcmDestBenificiaryXcAssets } from '../../createXcmTypes/types'; +import type { XcmDestBeneficiaryXcAssets } from '../../createXcmTypes/types'; import { UnionXcAssetsMultiAssets } from '../../createXcmTypes/types'; import { BaseError, BaseErrorsEnum } from '../../errors'; import type { CreateXcmCallOpts } from '../types'; @@ -32,7 +32,7 @@ export const transferMultiassets = async ( }); let assets: UnionXcAssetsMultiAssets; - let beneficiary: XcmDestBenificiaryXcAssets; + let beneficiary: XcmDestBeneficiaryXcAssets; if ( typeCreator.createXTokensAssets && diff --git a/src/createXcmTypes/ParaToPara.spec.ts b/src/createXcmTypes/ParaToPara.spec.ts index f813d07f..182544a0 100644 --- a/src/createXcmTypes/ParaToPara.spec.ts +++ b/src/createXcmTypes/ParaToPara.spec.ts @@ -85,6 +85,49 @@ describe('ParaToPara', () => { }, }; + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const beneficiary = ParaToPara.createBeneficiary( + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountId32: { + id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + }, + }, + ], + }, + }, + }; + + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4 for an Ethereum Address', () => { + const beneficiary = ParaToPara.createBeneficiary('0x96Bd611EbE3Af39544104e26764F4939924F6Ece', 4); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountKey20: { + key: '0x96Bd611EbE3Af39544104e26764F4939924F6Ece', + }, + }, + ], + }, + }, + }; + expect(beneficiary).toStrictEqual(expectedRes); }); }); @@ -119,6 +162,24 @@ describe('ParaToPara', () => { }, }; + expect(destination).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const destination = ParaToPara.createDest('100', 4); + + const expectedRes = { + V4: { + parents: 1, + interior: { + X1: [ + { + Parachain: '100', + }, + ], + }, + }, + }; + expect(destination).toStrictEqual(expectedRes); }); }); @@ -217,6 +278,49 @@ describe('ParaToPara', () => { ], }; + expect(assets).toStrictEqual(expectedRes); + }); + it('Should work for V4', async () => { + const assets = await ParaToPara.createAssets( + ['1000000', '20000000000'], + 4, + 'moonriver', + ['182365888117048807484804376330534607370', '311091173110107856861649819128533077277'], + { + registry, + isForeignAssetsTransfer, + isLiquidTokenTransfer, + api: mockMoonriverParachainApi, + }, + ); + + const expectedRes = { + V4: [ + { + id: { + Parents: '1', + Interior: { + X3: [{ Parachain: '1000' }, { PalletInstance: '50' }, { GeneralIndex: '8' }], + }, + }, + fun: { + Fungible: '1000000', + }, + }, + { + id: { + Parents: '1', + Interior: { + X3: [{ Parachain: '1000' }, { PalletInstance: '50' }, { GeneralIndex: '1984' }], + }, + }, + fun: { + Fungible: '20000000000', + }, + }, + ], + }; + expect(assets).toStrictEqual(expectedRes); }); }); diff --git a/src/createXcmTypes/ParaToPara.ts b/src/createXcmTypes/ParaToPara.ts index b7c2262c..d7e79ae5 100644 --- a/src/createXcmTypes/ParaToPara.ts +++ b/src/createXcmTypes/ParaToPara.ts @@ -15,24 +15,29 @@ import type { CreateAssetsOpts, CreateFeeAssetItemOpts, CreateWeightLimitOpts, + FungibleObjAsset, + FungibleObjAssetType, FungibleObjMultiAsset, + FungibleStrAsset, + FungibleStrAssetType, FungibleStrMultiAsset, ICreateXcmType, UnionXcAssetsMultiAsset, UnionXcAssetsMultiAssets, UnionXcAssetsMultiLocation, UnionXcmMultiAssets, - XcmDestBenificiary, - XcmDestBenificiaryXcAssets, + XcmDestBeneficiary, + XcmDestBeneficiaryXcAssets, XcmV3MultiLocation, + XcmV4Location, XcmWeight, } from './types'; import { constructForeignAssetMultiLocationFromAssetId } from './util/constructForeignAssetMultiLocationFromAssetId'; -import { dedupeMultiAssets } from './util/dedupeMultiAssets'; +import { dedupeAssets } from './util/dedupeAssets'; import { fetchPalletInstanceId } from './util/fetchPalletInstanceId'; import { getXcAssetMultiLocationByAssetId } from './util/getXcAssetMultiLocationByAssetId'; import { isParachainPrimaryNativeAsset } from './util/isParachainPrimaryNativeAsset'; -import { sortMultiAssetsAscending } from './util/sortMultiAssetsAscending'; +import { sortAssetsAscending } from './util/sortAssetsAscending'; export const ParaToPara: ICreateXcmType = { /** @@ -41,8 +46,8 @@ export const ParaToPara: ICreateXcmType = { * @param accountId The accountId of the beneficiary. * @param xcmVersion The accepted xcm version. */ - createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBenificiary => { - if (xcmVersion == 2) { + createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBeneficiary => { + if (xcmVersion === 2) { const X1 = isEthereumAddress(accountId) ? { AccountKey20: { network: 'Any', key: accountId } } : { AccountId32: { network: 'Any', id: accountId } }; @@ -57,10 +62,27 @@ export const ParaToPara: ICreateXcmType = { }; } - const X1 = isEthereumAddress(accountId) ? { AccountKey20: { key: accountId } } : { AccountId32: { id: accountId } }; + if (xcmVersion === 3) { + const X1 = isEthereumAddress(accountId) + ? { AccountKey20: { key: accountId } } + : { AccountId32: { id: accountId } }; + + return { + V3: { + parents: 0, + interior: { + X1, + }, + }, + }; + } + + const X1 = isEthereumAddress(accountId) + ? [{ AccountKey20: { key: accountId } }] + : [{ AccountId32: { id: accountId } }]; return { - V3: { + V4: { parents: 0, interior: { X1, @@ -74,7 +96,7 @@ export const ParaToPara: ICreateXcmType = { * @param destId The parachain Id of the destination. * @param xcmVersion The accepted xcm version. */ - createDest: (destId: string, xcmVersion?: number): XcmDestBenificiary => { + createDest: (destId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -88,17 +110,28 @@ export const ParaToPara: ICreateXcmType = { }; } - /** - * Ensure that the `parents` field is a `1` when sending a destination MultiLocation - * from a system parachain to a sovereign parachain. - */ + if (xcmVersion === 3) { + /** + * Ensure that the `parents` field is a `1` when sending a destination MultiLocation + * from a system parachain to a sovereign parachain. + */ + return { + V3: { + parents: 1, + interior: { + X1: { + Parachain: destId, + }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 1, interior: { - X1: { - Parachain: destId, - }, + X1: [{ Parachain: destId }], }, }, }; @@ -133,11 +166,15 @@ export const ParaToPara: ICreateXcmType = { if (xcmVersion === 2) { return Promise.resolve({ - V2: sortedAndDedupedMultiAssets, + V2: sortedAndDedupedMultiAssets as FungibleStrMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: sortedAndDedupedMultiAssets as FungibleStrMultiAsset[], }); } else { return Promise.resolve({ - V3: sortedAndDedupedMultiAssets, + V4: sortedAndDedupedMultiAssets as FungibleStrAsset[], }); } }, @@ -164,7 +201,7 @@ export const ParaToPara: ICreateXcmType = { */ createFeeAssetItem: async (api: ApiPromise, opts: CreateFeeAssetItemOpts): Promise => { const { registry, paysWithFeeDest, specName, assetIds, amounts, xcmVersion, isForeignAssetsTransfer } = opts; - if (xcmVersion && xcmVersion === 3 && specName && amounts && assetIds && paysWithFeeDest) { + if (xcmVersion && xcmVersion >= 3 && specName && amounts && assetIds && paysWithFeeDest) { const multiAssets = await createParaToParaMultiAssets( api, normalizeArrToStr(amounts), @@ -201,7 +238,7 @@ export const ParaToPara: ICreateXcmType = { destChainId: string, accountId: string, xcmVersion: number, - ): XcmDestBenificiaryXcAssets => { + ): XcmDestBeneficiaryXcAssets => { if (xcmVersion === 2) { return { V2: { @@ -215,8 +252,21 @@ export const ParaToPara: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 1, + interior: { + X2: isEthereumAddress(accountId) + ? [{ Parachain: destChainId }, { AccountKey20: { key: accountId } }] + : [{ Parachain: destChainId }, { AccountId32: { id: accountId } }], + }, + }, + }; + } + return { - V3: { + V4: { parents: 1, interior: { X2: isEthereumAddress(accountId) @@ -274,19 +324,32 @@ export const ParaToPara: ICreateXcmType = { const concreteMultiLocation = resolveMultiLocation(xcAssetMultiLocation, xcmVersion); - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: { Fungible: amount }, - }, - }; + let multiAsset: FungibleObjAssetType | undefined = undefined; + + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } if (xcmVersion === 2) { - return { V2: multiAsset }; + return { V2: multiAsset as FungibleObjMultiAsset }; + } else if (xcmVersion === 3) { + return { V3: multiAsset as FungibleObjMultiAsset }; } else { - return { V3: multiAsset }; + return { V4: multiAsset as FungibleObjAsset }; } }, /** @@ -310,11 +373,19 @@ export const ParaToPara: ICreateXcmType = { }; } - return { - V3: { - id: { - Concrete: paysWithFeeMultiLocation as XcmV3MultiLocation, + if (xcmVersion === 3) { + return { + V3: { + id: { + Concrete: paysWithFeeMultiLocation as XcmV3MultiLocation, + }, }, + }; + } + + return { + V4: { + id: paysWithFeeMultiLocation as XcmV4Location, }, }; } @@ -340,7 +411,8 @@ const createXTokensMultiAssets = async ( opts: CreateAssetsOpts, ): Promise => { const { registry, api } = opts; - let multiAssets: FungibleObjMultiAsset[] = []; + let multiAssets: FungibleObjAssetType[] = []; + let multiAsset: FungibleObjAssetType; for (let i = 0; i < assets.length; i++) { const amount = amounts[i]; @@ -358,27 +430,40 @@ const createXTokensMultiAssets = async ( const concreteMultiLocation = resolveMultiLocation(xcAssetMultiLocation, xcmVersion); - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: { Fungible: amount }, - }, - }; + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } multiAssets.push(multiAsset); } - multiAssets = sortMultiAssetsAscending(multiAssets) as FungibleObjMultiAsset[]; - const sortedAndDedupedMultiAssets = dedupeMultiAssets(multiAssets) as FungibleObjMultiAsset[]; + multiAssets = sortAssetsAscending(multiAssets) as FungibleObjAssetType[]; + const sortedAndDedupedMultiAssets = dedupeAssets(multiAssets) as FungibleObjAssetType[]; if (xcmVersion === 2) { return Promise.resolve({ - V2: sortedAndDedupedMultiAssets, + V2: sortedAndDedupedMultiAssets as FungibleObjMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: sortedAndDedupedMultiAssets as FungibleObjMultiAsset[], }); } else { return Promise.resolve({ - V3: sortedAndDedupedMultiAssets, + V4: sortedAndDedupedMultiAssets as FungibleObjAsset[], }); } }; @@ -402,9 +487,10 @@ const createParaToParaMultiAssets = async ( xcmVersion: number, registry: Registry, isForeignAssetsTransfer: boolean, -): Promise => { +): Promise => { const palletId = fetchPalletInstanceId(api, false, isForeignAssetsTransfer); - let multiAssets: FungibleStrMultiAsset[] = []; + let multiAssets: FungibleStrAssetType[] = []; + let multiAsset: FungibleStrAssetType | undefined = undefined; let concreteMultiLocation; const isPrimaryParachainNativeAsset = isParachainPrimaryNativeAsset( registry, @@ -422,14 +508,23 @@ const createParaToParaMultiAssets = async ( xcmVersion, ); - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: amounts[0], - }, - }; + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: amounts[0], + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: amounts[0], + }, + }; + } multiAssets.push(multiAsset); } else { @@ -452,22 +547,31 @@ const createParaToParaMultiAssets = async ( } else { concreteMultiLocation = resolveMultiLocation(xcAssetMultiLocation, xcmVersion); } + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: amount, + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: amount, + }, + }; + } - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: amount, - }, - }; multiAssets.push(multiAsset); } } - multiAssets = sortMultiAssetsAscending(multiAssets) as FungibleStrMultiAsset[]; + multiAssets = sortAssetsAscending(multiAssets) as FungibleStrAssetType[]; - const sortedAndDedupedMultiAssets = dedupeMultiAssets(multiAssets) as FungibleStrMultiAsset[]; + const sortedAndDedupedMultiAssets = dedupeAssets(multiAssets) as FungibleStrMultiAsset[]; return sortedAndDedupedMultiAssets; }; diff --git a/src/createXcmTypes/ParaToRelay.spec.ts b/src/createXcmTypes/ParaToRelay.spec.ts index b2a6b0e5..7b74da4a 100644 --- a/src/createXcmTypes/ParaToRelay.spec.ts +++ b/src/createXcmTypes/ParaToRelay.spec.ts @@ -54,6 +54,29 @@ describe('ParaToRelay', () => { }, }; + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const beneficiary = ParaToRelay.createBeneficiary( + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountId32: { + id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + }, + }, + ], + }, + }, + }; + expect(beneficiary).toStrictEqual(expectedRes); }); }); @@ -82,6 +105,18 @@ describe('ParaToRelay', () => { }; expect(dest).toStrictEqual(expected); }); + it('Should work for V4', () => { + const dest = ParaToRelay.createDest('', 4); + const expected = { + V4: { + parents: 1, + interior: { + Here: null, + }, + }, + }; + expect(dest).toStrictEqual(expected); + }); }); describe('Assets', () => { it('Should work for V2', async () => { @@ -126,6 +161,25 @@ describe('ParaToRelay', () => { }; expect(asset).toStrictEqual(expected); }); + it('Should work for V4', async () => { + const asset = await ParaToRelay.createAssets(['1000000'], 4, 'Moonriver', ['ksm'], assetOpts); + const expected = { + V4: [ + { + fun: { + Fungible: '1000000', + }, + id: { + interior: { + Here: '', + }, + parents: 1, + }, + }, + ], + }; + expect(asset).toStrictEqual(expected); + }); }); describe('WeightLimit', () => { it('Should work for unLimited', () => { @@ -199,6 +253,25 @@ describe('ParaToRelay', () => { }, }; + expect(xTokensBeneficiary).toStrictEqual(expected); + } + }); + it('Should work for V4', () => { + if (ParaToRelay.createXTokensBeneficiary) { + const xTokensBeneficiary = ParaToRelay.createXTokensBeneficiary( + '', + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + const expected = { + V4: { + parents: 1, + interior: { + X1: [{ AccountId32: { id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b' } }], + }, + }, + }; + expect(xTokensBeneficiary).toStrictEqual(expected); } }); @@ -246,5 +319,24 @@ describe('ParaToRelay', () => { expect(xTokensAsset).toStrictEqual(expected); } }); + it('Should work for V4', async () => { + if (ParaToRelay.createXTokensAsset) { + const xTokensAsset = await ParaToRelay.createXTokensAsset('1000000', 4, 'Moonriver', 'KSM', assetOpts); + const expected = { + V4: { + id: { + parents: 1, + interior: { + Here: null, + }, + }, + fun: { + Fungible: { Fungible: '1000000' }, + }, + }, + }; + expect(xTokensAsset).toStrictEqual(expected); + } + }); }); }); diff --git a/src/createXcmTypes/ParaToRelay.ts b/src/createXcmTypes/ParaToRelay.ts index 41714516..bda9908b 100644 --- a/src/createXcmTypes/ParaToRelay.ts +++ b/src/createXcmTypes/ParaToRelay.ts @@ -4,12 +4,17 @@ import type { ApiPromise } from '@polkadot/api'; import { CreateWeightLimitOpts, + FungibleObjAsset, + FungibleObjAssetType, + FungibleObjMultiAsset, + FungibleStrAsset, + FungibleStrAssetType, + FungibleStrMultiAsset, ICreateXcmType, UnionXcAssetsMultiAsset, UnionXcmMultiAssets, - XcmDestBenificiary, - XcmDestBenificiaryXcAssets, - XcmMultiAsset, + XcmDestBeneficiary, + XcmDestBeneficiaryXcAssets, XcmWeight, } from './types'; @@ -20,7 +25,7 @@ export const ParaToRelay: ICreateXcmType = { * @param accountId The accountId of the beneficiary. * @param xcmVersion The accepted xcm version. */ - createBeneficiary: (accountId: string, xcmVersion: number): XcmDestBenificiary => { + createBeneficiary: (accountId: string, xcmVersion: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -37,15 +42,32 @@ export const ParaToRelay: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + id: accountId, + }, + }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 0, interior: { - X1: { - AccountId32: { - id: accountId, + X1: [ + { + AccountId32: { + id: accountId, + }, }, - }, + ], }, }, }; @@ -56,7 +78,7 @@ export const ParaToRelay: ICreateXcmType = { * @param destId The destId in this case, which is the relay chain. * @param xcmVersion The accepted xcm version. */ - createDest: (_: string, xcmVersion: number): XcmDestBenificiary => { + createDest: (_: string, xcmVersion: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -68,8 +90,19 @@ export const ParaToRelay: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 1, + interior: { + Here: null, + }, + }, + }; + } + return { - V3: { + V4: { parents: 1, interior: { Here: null, @@ -84,32 +117,53 @@ export const ParaToRelay: ICreateXcmType = { * @param xcmVersion The accepted xcm version. */ createAssets: (amounts: string[], xcmVersion: number): Promise => { - const multiAssets: XcmMultiAsset[] = []; + const multiAssets: FungibleStrAssetType[] = []; const amount = amounts[0]; - const multiAsset = { - fun: { - Fungible: amount, - }, - id: { - Concrete: { + + let multiAsset: FungibleStrAssetType; + + if (xcmVersion < 4) { + multiAsset = { + fun: { + Fungible: amount, + }, + id: { + Concrete: { + interior: { + Here: '', + }, + parents: 1, + }, + }, + }; + } else { + multiAsset = { + fun: { + Fungible: amount, + }, + id: { interior: { Here: '', }, parents: 1, }, - }, - } as XcmMultiAsset; + }; + } multiAssets.push(multiAsset); if (xcmVersion === 2) { return Promise.resolve({ - V2: multiAssets, + V2: multiAssets as FungibleStrMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: multiAssets as FungibleStrMultiAsset[], }); } else { return Promise.resolve({ - V3: multiAssets, + V4: multiAssets as FungibleStrAsset[], }); } }, @@ -135,7 +189,7 @@ export const ParaToRelay: ICreateXcmType = { createFeeAssetItem: async (_: ApiPromise): Promise => { return await Promise.resolve(0); }, - createXTokensBeneficiary: (_: string, accountId: string, xcmVersion: number): XcmDestBenificiaryXcAssets => { + createXTokensBeneficiary: (_: string, accountId: string, xcmVersion: number): XcmDestBeneficiaryXcAssets => { if (xcmVersion === 2) { return { V2: { @@ -147,34 +201,63 @@ export const ParaToRelay: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 1, + interior: { + X1: { AccountId32: { id: accountId } }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 1, interior: { - X1: { AccountId32: { id: accountId } }, + X1: [{ AccountId32: { id: accountId } }], }, }, }; }, createXTokensAsset: (amount: string, xcmVersion: number): Promise => { - const multiAsset = { - id: { - Concrete: { + let multiAsset: FungibleObjAssetType; + + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: { + parents: 1, + interior: { + Here: null, + }, + }, + }, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } else { + multiAsset = { + id: { parents: 1, interior: { Here: null, }, }, - }, - fun: { - Fungible: { Fungible: amount }, - }, - }; + fun: { + Fungible: { Fungible: amount }, + }, + }; + } if (xcmVersion === 2) { - return Promise.resolve({ V2: multiAsset }); + return Promise.resolve({ V2: multiAsset as FungibleObjMultiAsset }); + } else if (xcmVersion === 3) { + return Promise.resolve({ V3: multiAsset as FungibleObjMultiAsset }); } else { - return Promise.resolve({ V3: multiAsset }); + return Promise.resolve({ V4: multiAsset as FungibleObjAsset }); } }, }; diff --git a/src/createXcmTypes/ParaToSystem.spec.ts b/src/createXcmTypes/ParaToSystem.spec.ts index 4e924849..ebaf1e39 100644 --- a/src/createXcmTypes/ParaToSystem.spec.ts +++ b/src/createXcmTypes/ParaToSystem.spec.ts @@ -48,6 +48,29 @@ describe('ParaToSystem', () => { }, }; + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const beneficiary = ParaToSystem.createBeneficiary( + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountId32: { + id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + }, + }, + ], + }, + }, + }; + expect(beneficiary).toStrictEqual(expectedRes); }); }); @@ -82,6 +105,24 @@ describe('ParaToSystem', () => { }, }; + expect(destination).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const destination = ParaToSystem.createDest('100', 4); + + const expectedRes = { + V4: { + parents: 1, + interior: { + X1: [ + { + Parachain: '100', + }, + ], + }, + }, + }; + expect(destination).toStrictEqual(expectedRes); }); }); @@ -180,6 +221,49 @@ describe('ParaToSystem', () => { ], }; + expect(assets).toStrictEqual(expectedRes); + }); + it('Should work for V4', async () => { + const assets = await ParaToSystem.createAssets( + ['1000000', '20000000000'], + 4, + 'moonriver', + ['182365888117048807484804376330534607370', '311091173110107856861649819128533077277'], + { + registry, + isForeignAssetsTransfer, + isLiquidTokenTransfer, + api: mockMoonriverParachainApi, + }, + ); + + const expectedRes = { + V4: [ + { + id: { + Parents: '1', + Interior: { + X3: [{ Parachain: '1000' }, { PalletInstance: '50' }, { GeneralIndex: '8' }], + }, + }, + fun: { + Fungible: '1000000', + }, + }, + { + id: { + Parents: '1', + Interior: { + X3: [{ Parachain: '1000' }, { PalletInstance: '50' }, { GeneralIndex: '1984' }], + }, + }, + fun: { + Fungible: '20000000000', + }, + }, + ], + }; + expect(assets).toStrictEqual(expectedRes); }); }); diff --git a/src/createXcmTypes/ParaToSystem.ts b/src/createXcmTypes/ParaToSystem.ts index 28d13fed..4634eb70 100644 --- a/src/createXcmTypes/ParaToSystem.ts +++ b/src/createXcmTypes/ParaToSystem.ts @@ -14,24 +14,29 @@ import type { CreateAssetsOpts, CreateFeeAssetItemOpts, CreateWeightLimitOpts, + FungibleObjAsset, + FungibleObjAssetType, FungibleObjMultiAsset, + FungibleStrAsset, + FungibleStrAssetType, FungibleStrMultiAsset, ICreateXcmType, UnionXcAssetsMultiAsset, UnionXcAssetsMultiAssets, UnionXcAssetsMultiLocation, UnionXcmMultiAssets, - XcmDestBenificiary, - XcmDestBenificiaryXcAssets, + XcmDestBeneficiary, + XcmDestBeneficiaryXcAssets, XcmV3MultiLocation, + XcmV4Location, XcmWeight, } from './types'; import { constructForeignAssetMultiLocationFromAssetId } from './util/constructForeignAssetMultiLocationFromAssetId'; -import { dedupeMultiAssets } from './util/dedupeMultiAssets'; +import { dedupeAssets } from './util/dedupeAssets'; import { fetchPalletInstanceId } from './util/fetchPalletInstanceId'; import { getXcAssetMultiLocationByAssetId } from './util/getXcAssetMultiLocationByAssetId'; import { isParachainPrimaryNativeAsset } from './util/isParachainPrimaryNativeAsset'; -import { sortMultiAssetsAscending } from './util/sortMultiAssetsAscending'; +import { sortAssetsAscending } from './util/sortAssetsAscending'; export const ParaToSystem: ICreateXcmType = { /** @@ -40,7 +45,7 @@ export const ParaToSystem: ICreateXcmType = { * @param accountId The accountId of the beneficiary. * @param xcmVersion The accepted xcm version. */ - createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBenificiary => { + createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion == 2) { return { V2: { @@ -52,11 +57,22 @@ export const ParaToSystem: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 0, + interior: { + X1: { AccountId32: { id: accountId } }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 0, interior: { - X1: { AccountId32: { id: accountId } }, + X1: [{ AccountId32: { id: accountId } }], }, }, }; @@ -67,7 +83,7 @@ export const ParaToSystem: ICreateXcmType = { * @param destId The parachain Id of the destination. * @param xcmVersion The accepted xcm version. */ - createDest: (destId: string, xcmVersion?: number): XcmDestBenificiary => { + createDest: (destId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -81,17 +97,36 @@ export const ParaToSystem: ICreateXcmType = { }; } + if (xcmVersion === 3) { + /** + * Ensure that the `parents` field is a `1` when sending a destination MultiLocation + * from a system parachain to a sovereign parachain. + */ + return { + V3: { + parents: 1, + interior: { + X1: { + Parachain: destId, + }, + }, + }, + }; + } + /** * Ensure that the `parents` field is a `1` when sending a destination MultiLocation * from a system parachain to a sovereign parachain. */ return { - V3: { + V4: { parents: 1, interior: { - X1: { - Parachain: destId, - }, + X1: [ + { + Parachain: destId, + }, + ], }, }, }; @@ -124,11 +159,15 @@ export const ParaToSystem: ICreateXcmType = { if (xcmVersion === 2) { return Promise.resolve({ - V2: sortedAndDedupedMultiAssets, + V2: sortedAndDedupedMultiAssets as FungibleStrMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: sortedAndDedupedMultiAssets as FungibleStrMultiAsset[], }); } else { return Promise.resolve({ - V3: sortedAndDedupedMultiAssets, + V4: sortedAndDedupedMultiAssets as FungibleStrAsset[], }); } }, @@ -155,7 +194,7 @@ export const ParaToSystem: ICreateXcmType = { */ createFeeAssetItem: async (api: ApiPromise, opts: CreateFeeAssetItemOpts): Promise => { const { registry, paysWithFeeDest, specName, assetIds, amounts, xcmVersion } = opts; - if (xcmVersion && xcmVersion === 3 && specName && amounts && assetIds && paysWithFeeDest) { + if (xcmVersion && xcmVersion >= 3 && specName && amounts && assetIds && paysWithFeeDest) { const multiAssets = await createParaToSystemMultiAssets( api, normalizeArrToStr(amounts), @@ -192,7 +231,7 @@ export const ParaToSystem: ICreateXcmType = { destChainId: string, accountId: string, xcmVersion: number, - ): XcmDestBenificiaryXcAssets => { + ): XcmDestBeneficiaryXcAssets => { if (xcmVersion === 2) { return { V2: { @@ -204,8 +243,19 @@ export const ParaToSystem: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 1, + interior: { + X2: [{ Parachain: destChainId }, { AccountId32: { id: accountId } }], + }, + }, + }; + } + return { - V3: { + V4: { parents: 1, interior: { X2: [{ Parachain: destChainId }, { AccountId32: { id: accountId } }], @@ -261,19 +311,32 @@ export const ParaToSystem: ICreateXcmType = { const concreteMultiLocation = resolveMultiLocation(xcAssetMultiLocation, xcmVersion); - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: { Fungible: amount }, - }, - }; + let multiAsset: FungibleObjAssetType; + + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } if (xcmVersion === 2) { - return { V2: multiAsset }; + return { V2: multiAsset as FungibleObjMultiAsset }; + } else if (xcmVersion === 3) { + return { V3: multiAsset as FungibleObjMultiAsset }; } else { - return { V3: multiAsset }; + return { V4: multiAsset as FungibleObjAsset }; } }, /** @@ -297,11 +360,19 @@ export const ParaToSystem: ICreateXcmType = { }; } - return { - V3: { - id: { - Concrete: paysWithFeeMultiLocation as XcmV3MultiLocation, + if (xcmVersion === 3) { + return { + V3: { + id: { + Concrete: paysWithFeeMultiLocation as XcmV3MultiLocation, + }, }, + }; + } + + return { + V4: { + id: paysWithFeeMultiLocation as XcmV4Location, }, }; } @@ -326,7 +397,8 @@ const createXTokensMultiAssets = async ( opts: CreateAssetsOpts, ): Promise => { const { registry, api } = opts; - let multiAssets: FungibleObjMultiAsset[] = []; + let multiAssets: FungibleObjAssetType[] = []; + let multiAsset: FungibleObjAssetType; for (let i = 0; i < assets.length; i++) { const amount = amounts[i]; @@ -344,27 +416,40 @@ const createXTokensMultiAssets = async ( const concreteMultiLocation = resolveMultiLocation(xcAssetMultiLocation, xcmVersion); - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: { Fungible: amount }, - }, - }; + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: { Fungible: amount }, + }, + }; + } multiAssets.push(multiAsset); } - multiAssets = sortMultiAssetsAscending(multiAssets) as FungibleObjMultiAsset[]; - const sortedAndDedupedMultiAssets = dedupeMultiAssets(multiAssets) as FungibleObjMultiAsset[]; + multiAssets = sortAssetsAscending(multiAssets) as FungibleObjAssetType[]; + const sortedAndDedupedMultiAssets = dedupeAssets(multiAssets) as FungibleObjAssetType[]; if (xcmVersion === 2) { return Promise.resolve({ - V2: sortedAndDedupedMultiAssets, + V2: sortedAndDedupedMultiAssets as FungibleObjMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: sortedAndDedupedMultiAssets as FungibleObjMultiAsset[], }); } else { return Promise.resolve({ - V3: sortedAndDedupedMultiAssets, + V4: sortedAndDedupedMultiAssets as FungibleObjAsset[], }); } }; @@ -387,9 +472,10 @@ const createParaToSystemMultiAssets = async ( xcmVersion: number, registry: Registry, isForeignAssetsTransfer: boolean, -): Promise => { +): Promise => { const palletId = fetchPalletInstanceId(api, false, isForeignAssetsTransfer); - let multiAssets: FungibleStrMultiAsset[] = []; + let multiAssets: FungibleStrAssetType[] = []; + let multiAsset: FungibleStrAssetType; let concreteMultiLocation; const isPrimaryParachainNativeAsset = isParachainPrimaryNativeAsset( registry, @@ -407,14 +493,23 @@ const createParaToSystemMultiAssets = async ( xcmVersion, ); - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: amounts[0], - }, - }; + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: amounts[0], + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: amounts[0], + }, + }; + } multiAssets.push(multiAsset); } else { @@ -438,20 +533,30 @@ const createParaToSystemMultiAssets = async ( concreteMultiLocation = resolveMultiLocation(xcAssetMultiLocation, xcmVersion); } - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: amount, - }, - }; + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: amount, + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: amount, + }, + }; + } + multiAssets.push(multiAsset); } } - multiAssets = sortMultiAssetsAscending(multiAssets) as FungibleStrMultiAsset[]; - const sortedAndDedupedMultiAssets = dedupeMultiAssets(multiAssets) as FungibleStrMultiAsset[]; + multiAssets = sortAssetsAscending(multiAssets) as FungibleStrAssetType[]; + const sortedAndDedupedMultiAssets = dedupeAssets(multiAssets) as FungibleStrAssetType[]; return sortedAndDedupedMultiAssets; }; diff --git a/src/createXcmTypes/RelayToPara.spec.ts b/src/createXcmTypes/RelayToPara.spec.ts index a167f7d5..b77ea903 100644 --- a/src/createXcmTypes/RelayToPara.spec.ts +++ b/src/createXcmTypes/RelayToPara.spec.ts @@ -87,6 +87,50 @@ describe('RelayToPara XcmVersioned Generation', () => { expect(beneficiary).toStrictEqual(expectedRes); }); + + it('Should work for V4', () => { + const beneficiary = RelayToPara.createBeneficiary( + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountId32: { + id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + }, + }, + ], + }, + }, + }; + + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4 Ethereum address', () => { + const beneficiary = RelayToPara.createBeneficiary('0x96Bd611EbE3Af39544104e26764F4939924F6Ece', 4); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountKey20: { + key: '0x96Bd611EbE3Af39544104e26764F4939924F6Ece', + }, + }, + ], + }, + }, + }; + + expect(beneficiary).toStrictEqual(expectedRes); + }); }); describe('Destination', () => { @@ -120,6 +164,24 @@ describe('RelayToPara XcmVersioned Generation', () => { }, }; + expect(destination).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const destination = RelayToPara.createDest('100', 4); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + Parachain: '100', + }, + ], + }, + }, + }; + expect(destination).toStrictEqual(expectedRes); }); }); @@ -181,6 +243,32 @@ describe('RelayToPara XcmVersioned Generation', () => { ], }; + expect(assets).toStrictEqual(expectedRes); + }); + it('Should work for V4', async () => { + const assets = await RelayToPara.createAssets(['100'], 4, '', [], { + registry, + isForeignAssetsTransfer, + isLiquidTokenTransfer, + api: mockRelayApi, + }); + + const expectedRes = { + V4: [ + { + fun: { + Fungible: '100', + }, + id: { + interior: { + Here: '', + }, + parents: 0, + }, + }, + ], + }; + expect(assets).toStrictEqual(expectedRes); }); }); diff --git a/src/createXcmTypes/RelayToPara.ts b/src/createXcmTypes/RelayToPara.ts index d1d7e51a..1b564a88 100644 --- a/src/createXcmTypes/RelayToPara.ts +++ b/src/createXcmTypes/RelayToPara.ts @@ -5,10 +5,12 @@ import { isEthereumAddress } from '@polkadot/util-crypto'; import { CreateWeightLimitOpts, + FungibleStrAsset, + FungibleStrAssetType, + FungibleStrMultiAsset, ICreateXcmType, UnionXcmMultiAssets, - XcmDestBenificiary, - XcmMultiAsset, + XcmDestBeneficiary, XcmWeight, } from './types'; @@ -22,7 +24,7 @@ export const RelayToPara: ICreateXcmType = { * @param accountId The accountId of the beneficiary. * @param xcmVersion The accepted xcm version. */ - createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBenificiary => { + createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion === 2) { const X1 = isEthereumAddress(accountId) ? { AccountKey20: { network: 'Any', key: accountId } } @@ -37,10 +39,27 @@ export const RelayToPara: ICreateXcmType = { }; } - const X1 = isEthereumAddress(accountId) ? { AccountKey20: { key: accountId } } : { AccountId32: { id: accountId } }; + if (xcmVersion === 3) { + const X1 = isEthereumAddress(accountId) + ? { AccountKey20: { key: accountId } } + : { AccountId32: { id: accountId } }; + + return { + V3: { + parents: 0, + interior: { + X1, + }, + }, + }; + } + + const X1 = isEthereumAddress(accountId) + ? [{ AccountKey20: { key: accountId } }] + : [{ AccountId32: { id: accountId } }]; return { - V3: { + V4: { parents: 0, interior: { X1, @@ -54,7 +73,7 @@ export const RelayToPara: ICreateXcmType = { * @param destId The parachain Id of the destination. * @param xcmVersion The accepted xcm version. */ - createDest: (destId: string, xcmVersion?: number): XcmDestBenificiary => { + createDest: (destId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -68,13 +87,28 @@ export const RelayToPara: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 0, + interior: { + X1: { + Parachain: destId, + }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 0, interior: { - X1: { - Parachain: destId, - }, + X1: [ + { + Parachain: destId, + }, + ], }, }, }; @@ -87,31 +121,50 @@ export const RelayToPara: ICreateXcmType = { */ createAssets: async (amounts: string[], xcmVersion: number): Promise => { const multiAssets = []; + let multiAsset: FungibleStrAssetType; const amount = amounts[0]; - const multiAsset = { - fun: { - Fungible: amount, - }, - id: { - Concrete: { + if (xcmVersion < 4) { + multiAsset = { + fun: { + Fungible: amount, + }, + id: { + Concrete: { + interior: { + Here: '', + }, + parents: 0, + }, + }, + }; + } else { + multiAsset = { + fun: { + Fungible: amount, + }, + id: { interior: { Here: '', }, parents: 0, }, - }, - } as XcmMultiAsset; + }; + } multiAssets.push(multiAsset); if (xcmVersion === 2) { return Promise.resolve({ - V2: multiAssets, + V2: multiAssets as FungibleStrMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: multiAssets as FungibleStrMultiAsset[], }); } else { return Promise.resolve({ - V3: multiAssets, + V4: multiAssets as FungibleStrAsset[], }); } }, diff --git a/src/createXcmTypes/RelayToSystem.spec.ts b/src/createXcmTypes/RelayToSystem.spec.ts index 41d771a3..0881d031 100644 --- a/src/createXcmTypes/RelayToSystem.spec.ts +++ b/src/createXcmTypes/RelayToSystem.spec.ts @@ -48,6 +48,29 @@ describe('RelayToSystem XcmVersioned Generation', () => { }, }; + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const beneficiary = RelayToSystem.createBeneficiary( + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountId32: { + id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + }, + }, + ], + }, + }, + }; + expect(beneficiary).toStrictEqual(expectedRes); }); }); @@ -83,6 +106,24 @@ describe('RelayToSystem XcmVersioned Generation', () => { }, }; + expect(destination).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const destination = RelayToSystem.createDest('100', 4); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + Parachain: '100', + }, + ], + }, + }, + }; + expect(destination).toStrictEqual(expectedRes); }); }); @@ -143,6 +184,32 @@ describe('RelayToSystem XcmVersioned Generation', () => { ], }; + expect(assets).toStrictEqual(expectedRes); + }); + it('Should work for V4', async () => { + const assets = await RelayToSystem.createAssets(['100'], 4, '', [], { + registry, + isForeignAssetsTransfer, + isLiquidTokenTransfer, + api: mockRelayApi, + }); + + const expectedRes = { + V4: [ + { + fun: { + Fungible: '100', + }, + id: { + interior: { + Here: '', + }, + parents: 0, + }, + }, + ], + }; + expect(assets).toStrictEqual(expectedRes); }); }); diff --git a/src/createXcmTypes/RelayToSystem.ts b/src/createXcmTypes/RelayToSystem.ts index f30f13b2..d26cf2aa 100644 --- a/src/createXcmTypes/RelayToSystem.ts +++ b/src/createXcmTypes/RelayToSystem.ts @@ -4,10 +4,12 @@ import type { ApiPromise } from '@polkadot/api'; import { CreateWeightLimitOpts, + FungibleStrAsset, + FungibleStrAssetType, + FungibleStrMultiAsset, ICreateXcmType, UnionXcmMultiAssets, - XcmDestBenificiary, - XcmMultiAsset, + XcmDestBeneficiary, XcmWeight, } from './types'; /** @@ -20,7 +22,7 @@ export const RelayToSystem: ICreateXcmType = { * @param accountId The accountId of the beneficiary. * @param xcmVersion The accepted xcm version. */ - createBeneficiary: (accountId: string, xcmVersion: number): XcmDestBenificiary => { + createBeneficiary: (accountId: string, xcmVersion: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -37,15 +39,32 @@ export const RelayToSystem: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + id: accountId, + }, + }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 0, interior: { - X1: { - AccountId32: { - id: accountId, + X1: [ + { + AccountId32: { + id: accountId, + }, }, - }, + ], }, }, }; @@ -56,7 +75,7 @@ export const RelayToSystem: ICreateXcmType = { * @param destId The parachain Id of the destination * @param xcmVersion The accepted xcm version */ - createDest: (destId: string, xcmVersion: number): XcmDestBenificiary => { + createDest: (destId: string, xcmVersion: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -70,13 +89,28 @@ export const RelayToSystem: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 0, + interior: { + X1: { + Parachain: destId, + }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 0, interior: { - X1: { - Parachain: destId, - }, + X1: [ + { + Parachain: destId, + }, + ], }, }, }; @@ -91,29 +125,49 @@ export const RelayToSystem: ICreateXcmType = { const multiAssets = []; const amount = amounts[0]; - const multiAsset = { - fun: { - Fungible: amount, - }, - id: { - Concrete: { + let multiAsset: FungibleStrAssetType; + + if (xcmVersion < 4) { + multiAsset = { + fun: { + Fungible: amount, + }, + id: { + Concrete: { + interior: { + Here: '', + }, + parents: 0, + }, + }, + }; + } else { + multiAsset = { + fun: { + Fungible: amount, + }, + id: { interior: { Here: '', }, parents: 0, }, - }, - } as XcmMultiAsset; + }; + } multiAssets.push(multiAsset); if (xcmVersion === 2) { return Promise.resolve({ - V2: multiAssets, + V2: multiAssets as FungibleStrMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: multiAssets as FungibleStrMultiAsset[], }); } else { return Promise.resolve({ - V3: multiAssets, + V4: multiAssets as FungibleStrAsset[], }); } }, diff --git a/src/createXcmTypes/SystemToPara.spec.ts b/src/createXcmTypes/SystemToPara.spec.ts index 5a496bd7..3ee49144 100644 --- a/src/createXcmTypes/SystemToPara.spec.ts +++ b/src/createXcmTypes/SystemToPara.spec.ts @@ -4,7 +4,7 @@ import { Registry } from '../registry'; import { mockSystemApi } from '../testHelpers/mockSystemApi'; import { SystemToPara } from './SystemToPara'; import { createSystemToParaMultiAssets } from './SystemToPara'; -import { FungibleStrMultiAsset } from './types'; +import { FungibleStrAsset, FungibleStrMultiAsset } from './types'; describe('SystemToPara XcmVersioned Generation', () => { const registry = new Registry('statemine', {}); @@ -87,6 +87,49 @@ describe('SystemToPara XcmVersioned Generation', () => { }, }; + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const beneficiary = SystemToPara.createBeneficiary( + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountId32: { + id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + }, + }, + ], + }, + }, + }; + + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4 for an Ethereum Address', () => { + const beneficiary = SystemToPara.createBeneficiary('0x96Bd611EbE3Af39544104e26764F4939924F6Ece', 4); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountKey20: { + key: '0x96Bd611EbE3Af39544104e26764F4939924F6Ece', + }, + }, + ], + }, + }, + }; + expect(beneficiary).toStrictEqual(expectedRes); }); }); @@ -122,6 +165,24 @@ describe('SystemToPara XcmVersioned Generation', () => { }, }; + expect(destination).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const destination = SystemToPara.createDest('100', 4); + + const expectedRes = { + V4: { + parents: 1, + interior: { + X1: [ + { + Parachain: '100', + }, + ], + }, + }, + }; + expect(destination).toStrictEqual(expectedRes); }); }); @@ -171,7 +232,7 @@ describe('SystemToPara XcmVersioned Generation', () => { expect(assets).toStrictEqual(expectedRes); }); - it('Should work for V3 for testing this', async () => { + it('Should work for V3', async () => { const assets = await SystemToPara.createAssets(['100', '100'], 3, 'statemine', ['1', '2'], { registry, isForeignAssetsTransfer, @@ -212,7 +273,44 @@ describe('SystemToPara XcmVersioned Generation', () => { expect(assets).toStrictEqual(expectedRes); }); - it('Should correctly construct a liquid token transfer', async () => { + it('Should work for V4', async () => { + const assets = await SystemToPara.createAssets(['100', '100'], 4, 'statemine', ['1', '2'], { + registry, + isForeignAssetsTransfer, + isLiquidTokenTransfer, + api: mockSystemApi, + }); + + const expectedRes = { + V4: [ + { + id: { + parents: 0, + interior: { + X2: [{ PalletInstance: '50' }, { GeneralIndex: '1' }], + }, + }, + fun: { + Fungible: '100', + }, + }, + { + id: { + parents: 0, + interior: { + X2: [{ PalletInstance: '50' }, { GeneralIndex: '2' }], + }, + }, + fun: { + Fungible: '100', + }, + }, + ], + }; + + expect(assets).toStrictEqual(expectedRes); + }); + it('Should correctly construct a liquid token transfer for V3', async () => { const assets = await SystemToPara.createAssets(['100', '100'], 3, 'statemine', ['1', '2'], { registry, isForeignAssetsTransfer, @@ -251,6 +349,43 @@ describe('SystemToPara XcmVersioned Generation', () => { ], }; + expect(assets).toStrictEqual(expectedRes); + }); + it('Should correctly construct a liquid token transfer for V4', async () => { + const assets = await SystemToPara.createAssets(['100', '100'], 4, 'statemine', ['1', '2'], { + registry, + isForeignAssetsTransfer, + isLiquidTokenTransfer: true, + api: mockSystemApi, + }); + + const expectedRes = { + V4: [ + { + id: { + parents: 0, + interior: { + X2: [{ PalletInstance: '55' }, { GeneralIndex: '1' }], + }, + }, + fun: { + Fungible: '100', + }, + }, + { + id: { + parents: 0, + interior: { + X2: [{ PalletInstance: '55' }, { GeneralIndex: '2' }], + }, + }, + fun: { + Fungible: '100', + }, + }, + ], + }; + expect(assets).toStrictEqual(expectedRes); }); }); @@ -286,8 +421,21 @@ describe('SystemToPara XcmVersioned Generation', () => { }); describe('createSystemToParaMultiAssets', () => { - it('Should correctly create system multi assets for SystemToPara xcm direction', async () => { + it('Should correctly create system multi assets for SystemToPara xcm direction for V2', async () => { const expected: FungibleStrMultiAsset[] = [ + { + fun: { + Fungible: '300000000000000', + }, + id: { + Concrete: { + interior: { + X2: [{ PalletInstance: '50' }, { GeneralIndex: '11' }], + }, + parents: 0, + }, + }, + }, { fun: { Fungible: '100000000000000', @@ -301,6 +449,26 @@ describe('SystemToPara XcmVersioned Generation', () => { }, }, }, + ]; + + const assets = ['ksm', 'usdt']; + const amounts = ['100000000000000', '300000000000000']; + const specName = 'statemine'; + const result = await createSystemToParaMultiAssets( + mockSystemApi, + amounts, + specName, + assets, + registry, + 2, + false, + false, + ); + + expect(result).toStrictEqual(expected); + }); + it('Should correctly create system multi assets for SystemToPara xcm direction for V3', async () => { + const expected: FungibleStrMultiAsset[] = [ { fun: { Fungible: '300000000000000', @@ -314,6 +482,19 @@ describe('SystemToPara XcmVersioned Generation', () => { }, }, }, + { + fun: { + Fungible: '100000000000000', + }, + id: { + Concrete: { + interior: { + Here: '', + }, + parents: 1, + }, + }, + }, ]; const assets = ['ksm', 'usdt']; @@ -325,7 +506,49 @@ describe('SystemToPara XcmVersioned Generation', () => { specName, assets, registry, - 2, + 3, + false, + false, + ); + + expect(result).toStrictEqual(expected); + }); + it('Should correctly create system multi assets for SystemToPara xcm direction for V4', async () => { + const expected: FungibleStrAsset[] = [ + { + fun: { + Fungible: '300000000000000', + }, + id: { + interior: { + X2: [{ PalletInstance: '50' }, { GeneralIndex: '11' }], + }, + parents: 0, + }, + }, + { + fun: { + Fungible: '100000000000000', + }, + id: { + interior: { + Here: '', + }, + parents: 1, + }, + }, + ]; + + const assets = ['ksm', 'usdt']; + const amounts = ['100000000000000', '300000000000000']; + const specName = 'statemine'; + const result = await createSystemToParaMultiAssets( + mockSystemApi, + amounts, + specName, + assets, + registry, + 4, false, false, ); diff --git a/src/createXcmTypes/SystemToPara.ts b/src/createXcmTypes/SystemToPara.ts index 15b7ec40..fdb4e855 100644 --- a/src/createXcmTypes/SystemToPara.ts +++ b/src/createXcmTypes/SystemToPara.ts @@ -13,22 +13,25 @@ import type { CreateAssetsOpts, CreateFeeAssetItemOpts, CreateWeightLimitOpts, + FungibleStrAsset, + FungibleStrAssetType, FungibleStrMultiAsset, ICreateXcmType, UnionXcmMultiAssets, UnionXcmMultiLocation, - XcmDestBenificiary, + XcmDestBeneficiary, XcmV2Junctions, XcmV3Junctions, + XcmV4Junctions, XcmWeight, } from './types'; import { constructForeignAssetMultiLocationFromAssetId } from './util/constructForeignAssetMultiLocationFromAssetId'; -import { dedupeMultiAssets } from './util/dedupeMultiAssets'; +import { dedupeAssets } from './util/dedupeAssets'; import { fetchPalletInstanceId } from './util/fetchPalletInstanceId'; import { getAssetId } from './util/getAssetId'; import { isRelayNativeAsset } from './util/isRelayNativeAsset'; import { isSystemChain } from './util/isSystemChain'; -import { sortMultiAssetsAscending } from './util/sortMultiAssetsAscending'; +import { sortAssetsAscending } from './util/sortAssetsAscending'; export const SystemToPara: ICreateXcmType = { /** @@ -37,7 +40,7 @@ export const SystemToPara: ICreateXcmType = { * @param accountId The accountId of the beneficiary. * @param xcmVersion The accepted xcm version. */ - createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBenificiary => { + createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion == 2) { const X1 = isEthereumAddress(accountId) ? { AccountKey20: { network: 'Any', key: accountId } } @@ -53,10 +56,27 @@ export const SystemToPara: ICreateXcmType = { }; } - const X1 = isEthereumAddress(accountId) ? { AccountKey20: { key: accountId } } : { AccountId32: { id: accountId } }; + if (xcmVersion === 3) { + const X1 = isEthereumAddress(accountId) + ? { AccountKey20: { key: accountId } } + : { AccountId32: { id: accountId } }; + + return { + V3: { + parents: 0, + interior: { + X1, + }, + }, + }; + } + + const X1 = isEthereumAddress(accountId) + ? [{ AccountKey20: { key: accountId } }] + : [{ AccountId32: { id: accountId } }]; return { - V3: { + V4: { parents: 0, interior: { X1, @@ -70,7 +90,7 @@ export const SystemToPara: ICreateXcmType = { * @param destId The parachain Id of the destination. * @param xcmVersion The accepted xcm version. */ - createDest: (destId: string, xcmVersion?: number): XcmDestBenificiary => { + createDest: (destId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -84,17 +104,32 @@ export const SystemToPara: ICreateXcmType = { }; } - /** - * Ensure that the `parents` field is a `1` when sending a destination MultiLocation - * from a system parachain to a sovereign parachain. - */ + if (xcmVersion === 3) { + /** + * Ensure that the `parents` field is a `1` when sending a destination MultiLocation + * from a system parachain to a sovereign parachain. + */ + return { + V3: { + parents: 1, + interior: { + X1: { + Parachain: destId, + }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 1, interior: { - X1: { - Parachain: destId, - }, + X1: [ + { + Parachain: destId, + }, + ], }, }, }; @@ -129,11 +164,15 @@ export const SystemToPara: ICreateXcmType = { if (xcmVersion === 2) { return Promise.resolve({ - V2: sortedAndDedupedMultiAssets, + V2: sortedAndDedupedMultiAssets as FungibleStrMultiAsset[], + }); + } else if (xcmVersion == 3) { + return Promise.resolve({ + V3: sortedAndDedupedMultiAssets as FungibleStrMultiAsset[], }); } else { return Promise.resolve({ - V3: sortedAndDedupedMultiAssets, + V4: sortedAndDedupedMultiAssets as FungibleStrAsset[], }); } }, @@ -227,8 +266,9 @@ export const createSystemToParaMultiAssets = async ( xcmVersion: number, isForeignAssetsTransfer: boolean, isLiquidTokenTransfer: boolean, -): Promise => { - let multiAssets: FungibleStrMultiAsset[] = []; +): Promise => { + let multiAssets: FungibleStrAssetType[] = []; + let multiAsset: FungibleStrAssetType; const palletId = fetchPalletInstanceId(api, isLiquidTokenTransfer, isForeignAssetsTransfer); const systemChainId = registry.lookupChainIdBySpecName(specName); @@ -258,7 +298,7 @@ export const createSystemToParaMultiAssets = async ( concreteMultiLocation = constructForeignAssetMultiLocationFromAssetId(assetId, palletId, xcmVersion); } else { const parents = isRelayNative ? 1 : 0; - const interior: RequireOnlyOne = isRelayNative + const interior: RequireOnlyOne = isRelayNative ? { Here: '' } : { X2: [{ PalletInstance: palletId }, { GeneralIndex: assetId }], @@ -270,21 +310,30 @@ export const createSystemToParaMultiAssets = async ( }; } - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: amount, - }, - }; + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: amount, + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: amount, + }, + }; + } multiAssets.push(multiAsset); } - multiAssets = sortMultiAssetsAscending(multiAssets) as FungibleStrMultiAsset[]; + multiAssets = sortAssetsAscending(multiAssets) as FungibleStrAssetType[]; - const sortedAndDedupedMultiAssets = dedupeMultiAssets(multiAssets) as FungibleStrMultiAsset[]; + const sortedAndDedupedMultiAssets = dedupeAssets(multiAssets) as FungibleStrAssetType[]; return sortedAndDedupedMultiAssets; }; diff --git a/src/createXcmTypes/SystemToRelay.spec.ts b/src/createXcmTypes/SystemToRelay.spec.ts index 5bfe867d..1bab924d 100644 --- a/src/createXcmTypes/SystemToRelay.spec.ts +++ b/src/createXcmTypes/SystemToRelay.spec.ts @@ -49,6 +49,29 @@ describe('SystemToRelay XcmVersioned Generation', () => { }, }; + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const beneficiary = SystemToRelay.createBeneficiary( + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountId32: { + id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + }, + }, + ], + }, + }, + }; + expect(beneficiary).toStrictEqual(expectedRes); }); }); @@ -79,6 +102,20 @@ describe('SystemToRelay XcmVersioned Generation', () => { }, }; + expect(destination).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const destination = SystemToRelay.createDest('0', 4); + + const expectedRes = { + V4: { + parents: 1, + interior: { + Here: null, + }, + }, + }; + expect(destination).toStrictEqual(expectedRes); }); }); @@ -139,6 +176,32 @@ describe('SystemToRelay XcmVersioned Generation', () => { ], }; + expect(assets).toStrictEqual(expectedRes); + }); + it('Should work for V4', async () => { + const assets = await SystemToRelay.createAssets(['100'], 4, '', [], { + registry, + isForeignAssetsTransfer, + isLiquidTokenTransfer, + api: mockSystemApi, + }); + + const expectedRes = { + V4: [ + { + id: { + parents: 1, + interior: { + Here: '', + }, + }, + fun: { + Fungible: '100', + }, + }, + ], + }; + expect(assets).toStrictEqual(expectedRes); }); }); diff --git a/src/createXcmTypes/SystemToRelay.ts b/src/createXcmTypes/SystemToRelay.ts index 3a56cad2..4fd8ca4b 100644 --- a/src/createXcmTypes/SystemToRelay.ts +++ b/src/createXcmTypes/SystemToRelay.ts @@ -4,10 +4,12 @@ import type { ApiPromise } from '@polkadot/api'; import { CreateWeightLimitOpts, + FungibleStrAsset, + FungibleStrAssetType, + FungibleStrMultiAsset, ICreateXcmType, UnionXcmMultiAssets, - XcmDestBenificiary, - XcmMultiAsset, + XcmDestBeneficiary, XcmWeight, } from './types'; @@ -18,7 +20,7 @@ export const SystemToRelay: ICreateXcmType = { * @param accountId The accountId of the beneficiary. * @param xcmVersion The accepted xcm version. */ - createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBenificiary => { + createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -35,15 +37,32 @@ export const SystemToRelay: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + id: accountId, + }, + }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 0, interior: { - X1: { - AccountId32: { - id: accountId, + X1: [ + { + AccountId32: { + id: accountId, + }, }, - }, + ], }, }, }; @@ -54,7 +73,7 @@ export const SystemToRelay: ICreateXcmType = { * @param destId The destId in this case, which is the relay chain. * @param xcmVersion The accepted xcm version. */ - createDest: (_: string, xcmVersion: number): XcmDestBenificiary => { + createDest: (_: string, xcmVersion: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -66,8 +85,19 @@ export const SystemToRelay: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 1, + interior: { + Here: null, + }, + }, + }; + } + return { - V3: { + V4: { parents: 1, interior: { Here: null, @@ -82,32 +112,52 @@ export const SystemToRelay: ICreateXcmType = { * @param xcmVersion The accepted xcm version. */ createAssets: async (amounts: string[], xcmVersion: number): Promise => { - const multiAssets: XcmMultiAsset[] = []; + const multiAssets: FungibleStrAssetType[] = []; + let multiAsset: FungibleStrAssetType; const amount = amounts[0]; - const multiAsset = { - fun: { - Fungible: amount, - }, - id: { - Concrete: { + + if (xcmVersion < 4) { + multiAsset = { + fun: { + Fungible: amount, + }, + id: { + Concrete: { + interior: { + Here: '', + }, + parents: 1, + }, + }, + }; + } else { + multiAsset = { + fun: { + Fungible: amount, + }, + id: { interior: { Here: '', }, parents: 1, }, - }, - } as XcmMultiAsset; + }; + } multiAssets.push(multiAsset); if (xcmVersion === 2) { return Promise.resolve({ - V2: multiAssets, + V2: multiAssets as FungibleStrMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: multiAssets as FungibleStrMultiAsset[], }); } else { return Promise.resolve({ - V3: multiAssets, + V4: multiAssets as FungibleStrAsset[], }); } }, diff --git a/src/createXcmTypes/SystemToSystem.spec.ts b/src/createXcmTypes/SystemToSystem.spec.ts index f923a0e3..77ebdb7b 100644 --- a/src/createXcmTypes/SystemToSystem.spec.ts +++ b/src/createXcmTypes/SystemToSystem.spec.ts @@ -49,6 +49,29 @@ describe('SystemToSystem XcmVersioned Generation', () => { }, }; + expect(beneficiary).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const beneficiary = SystemToSystem.createBeneficiary( + '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + 4, + ); + + const expectedRes = { + V4: { + parents: 0, + interior: { + X1: [ + { + AccountId32: { + id: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + }, + }, + ], + }, + }, + }; + expect(beneficiary).toStrictEqual(expectedRes); }); }); @@ -83,6 +106,24 @@ describe('SystemToSystem XcmVersioned Generation', () => { }, }; + expect(destination).toStrictEqual(expectedRes); + }); + it('Should work for V4', () => { + const destination = SystemToSystem.createDest('1002', 4); + + const expectedRes = { + V4: { + parents: 1, + interior: { + X1: [ + { + Parachain: '1002', + }, + ], + }, + }, + }; + expect(destination).toStrictEqual(expectedRes); }); }); @@ -147,6 +188,32 @@ describe('SystemToSystem XcmVersioned Generation', () => { expect(assets).toStrictEqual(expectedRes); }); + it('Should work for V4', async () => { + const assets = await SystemToSystem.createAssets(['100'], 4, 'bridge-hub-kusama', ['ksm'], { + registry, + isForeignAssetsTransfer, + isLiquidTokenTransfer, + api: mockSystemApi, + }); + + const expectedRes = { + V4: [ + { + id: { + Parents: '1', + Interior: { + Here: '', + }, + }, + fun: { + Fungible: '100', + }, + }, + ], + }; + + expect(assets).toStrictEqual(expectedRes); + }); it('Should error when asset ID is not found for V3', async () => { const expectedErrorMessage = 'bridge-hub-kusama has no associated token symbol usdc'; @@ -158,7 +225,7 @@ describe('SystemToSystem XcmVersioned Generation', () => { isLiquidTokenTransfer, api: mockSystemApi, }); - }).rejects.toThrowError(expectedErrorMessage); + }).rejects.toThrow(expectedErrorMessage); }); it('Should work for a liquid token transfer', async () => { const assets = await SystemToSystem.createAssets(['100'], 2, 'statemine', ['USDT'], { diff --git a/src/createXcmTypes/SystemToSystem.ts b/src/createXcmTypes/SystemToSystem.ts index cba1258d..7dd41356 100644 --- a/src/createXcmTypes/SystemToSystem.ts +++ b/src/createXcmTypes/SystemToSystem.ts @@ -13,21 +13,23 @@ import { CreateAssetsOpts, CreateFeeAssetItemOpts, CreateWeightLimitOpts, + FungibleStrAsset, + FungibleStrAssetType, FungibleStrMultiAsset, ICreateXcmType, UnionXcmMultiAssets, UnionXcmMultiLocation, - XcmDestBenificiary, + XcmDestBeneficiary, XcmV2Junctions, XcmV3Junctions, XcmWeight, } from './types'; -import { dedupeMultiAssets } from './util/dedupeMultiAssets'; +import { dedupeAssets } from './util/dedupeAssets'; import { fetchPalletInstanceId } from './util/fetchPalletInstanceId'; import { getAssetId } from './util/getAssetId'; import { isRelayNativeAsset } from './util/isRelayNativeAsset'; import { isSystemChain } from './util/isSystemChain'; -import { sortMultiAssetsAscending } from './util/sortMultiAssetsAscending'; +import { sortAssetsAscending } from './util/sortAssetsAscending'; export const SystemToSystem: ICreateXcmType = { /** @@ -36,7 +38,7 @@ export const SystemToSystem: ICreateXcmType = { * @param accountId The accountId of the beneficiary. * @param xcmVersion The accepted xcm version. */ - createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBenificiary => { + createBeneficiary: (accountId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion == 2) { return { V2: { @@ -48,11 +50,22 @@ export const SystemToSystem: ICreateXcmType = { }; } + if (xcmVersion === 3) { + return { + V3: { + parents: 0, + interior: { + X1: { AccountId32: { id: accountId } }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 0, interior: { - X1: { AccountId32: { id: accountId } }, + X1: [{ AccountId32: { id: accountId } }], }, }, }; @@ -63,7 +76,7 @@ export const SystemToSystem: ICreateXcmType = { * @param destId The parachain Id of the destination. * @param xcmVersion The accepted xcm version. */ - createDest: (destId: string, xcmVersion?: number): XcmDestBenificiary => { + createDest: (destId: string, xcmVersion?: number): XcmDestBeneficiary => { if (xcmVersion === 2) { return { V2: { @@ -76,17 +89,29 @@ export const SystemToSystem: ICreateXcmType = { }, }; } - /** - * Ensure that the `parents` field is a `1` when sending a destination MultiLocation - * from a system parachain to a sovereign parachain. - */ + + if (xcmVersion === 3) { + /** + * Ensure that the `parents` field is a `1` when sending a destination MultiLocation + * from a system parachain to a sovereign parachain. + */ + return { + V3: { + parents: 1, + interior: { + X1: { + Parachain: destId, + }, + }, + }, + }; + } + return { - V3: { + V4: { parents: 1, interior: { - X1: { - Parachain: destId, - }, + X1: [{ Parachain: destId }], }, }, }; @@ -122,11 +147,15 @@ export const SystemToSystem: ICreateXcmType = { if (xcmVersion === 2) { return Promise.resolve({ - V2: sortedAndDedupedMultiAssets, + V2: sortedAndDedupedMultiAssets as FungibleStrMultiAsset[], + }); + } else if (xcmVersion === 3) { + return Promise.resolve({ + V3: sortedAndDedupedMultiAssets as FungibleStrMultiAsset[], }); } else { return Promise.resolve({ - V3: sortedAndDedupedMultiAssets, + V4: sortedAndDedupedMultiAssets as FungibleStrAsset[], }); } }, @@ -221,8 +250,9 @@ export const createSystemToSystemMultiAssets = async ( xcmVersion: number, isForeignAssetsTransfer: boolean, isLiquidTokenTransfer: boolean, -): Promise => { - let multiAssets: FungibleStrMultiAsset[] = []; +): Promise => { + let multiAssets: FungibleStrAssetType[] = []; + let multiAsset: FungibleStrAssetType; const systemChainId = registry.lookupChainIdBySpecName(specName); const palletId = fetchPalletInstanceId(api, isLiquidTokenTransfer, isForeignAssetsTransfer); @@ -288,21 +318,30 @@ export const createSystemToSystemMultiAssets = async ( ); } - const multiAsset = { - id: { - Concrete: concreteMultiLocation, - }, - fun: { - Fungible: amount, - }, - }; + if (xcmVersion < 4) { + multiAsset = { + id: { + Concrete: concreteMultiLocation, + }, + fun: { + Fungible: amount, + }, + }; + } else { + multiAsset = { + id: concreteMultiLocation, + fun: { + Fungible: amount, + }, + }; + } multiAssets.push(multiAsset); } - multiAssets = sortMultiAssetsAscending(multiAssets) as FungibleStrMultiAsset[]; + multiAssets = sortAssetsAscending(multiAssets) as FungibleStrAssetType[]; - const sortedAndDedupedMultiAssets = dedupeMultiAssets(multiAssets) as FungibleStrMultiAsset[]; + const sortedAndDedupedMultiAssets = dedupeAssets(multiAssets) as FungibleStrAssetType[]; return sortedAndDedupedMultiAssets; }; diff --git a/src/createXcmTypes/types.ts b/src/createXcmTypes/types.ts index 4a796152..6825b642 100644 --- a/src/createXcmTypes/types.ts +++ b/src/createXcmTypes/types.ts @@ -6,11 +6,11 @@ import type { AnyJson } from '@polkadot/types/types'; import type { Registry } from '../registry'; import type { RequireOnlyOne } from '../types'; -export type XcmDestBenificiary = { +export type XcmDestBeneficiary = { [x: string]: { parents: number; interior: { - [x: string]: RequireOnlyOne | null; + [x: string]: RequireOnlyOne | XcmV4JunctionDestBeneficiary | null; }; }; }; @@ -27,6 +27,23 @@ export type XcmJunctionDestBeneficiary = { Parachain: string; }; +export type XcmV4JunctionDestBeneficiary = + | { + AccountId32: { + network?: string; + id: string; + }; + }[] + | { + Parachain: string; + }[] + | { + AccountKey20: { + network?: string; + key: string; + }; + }[]; + export type XcmV2MultiLocation = { parents: number; interior: RequireOnlyOne; @@ -110,15 +127,56 @@ export type XcmV3JunctionBase = { GlobalConsensus: string | AnyJson; }; -export type UnionXcmMultiLocation = XcmV3MultiLocation | XcmV2MultiLocation; +export type XcmV4Location = { + parents: number; + interior: RequireOnlyOne; +}; -export type UnionXcmMultiAssets = XcmV2MultiAssets | XcmV3MultiAssets; +export interface XcmV4Junctions { + Here: '' | null; + X1: XcmV4Junction; + X2: [XcmV4Junction, XcmV4Junction]; + X3: [XcmV4Junction, XcmV4Junction, XcmV4Junction]; + X4: [XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction]; + X5: [XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction]; + X6: [XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction]; + X7: [XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction, XcmV4Junction]; + X8: [ + XcmV4Junction, + XcmV4Junction, + XcmV4Junction, + XcmV4Junction, + XcmV4Junction, + XcmV4Junction, + XcmV4Junction, + XcmV4Junction, + ]; +} -export type UnionXcmMultiAsset = XcmV2MultiAsset | XcmV3MultiAsset; +export type XcmV4Junction = RequireOnlyOne; -export type UnionXcAssetsMultiAssets = XcAssetsV2MultiAssets | XcAssetsV3MultiAssets; +export type XcmV4JunctionBase = { + Parachain: number; + AccountId32: { network?: XcmV2Network; id: string }; + AccountIndex64: { network?: XcmV2Network; id: string }; + AccountKey20: { network?: XcmV2Network; id: string }; + PalletInstance: number; + GeneralIndex: string | number; + GeneralKey: string; + OnlyChild: AnyJson; + Plurality: { id: AnyJson; part: AnyJson }; + GlobalConsensus: string | AnyJson; +}; + +export type UnionXcmMultiLocation = XcmV4Location | XcmV3MultiLocation | XcmV2MultiLocation; + +export type UnionXcmMultiAssets = XcmV2MultiAssets | XcmV3MultiAssets | XcmV4Assets; + +export type UnionXcmMultiAsset = XcmV2MultiAsset | XcmV3MultiAsset | XcmV4Asset; -export type UnionXcAssetsMultiAsset = XcAssetsV2MultiAsset | XcAssetsV3MultiAsset; +export type UnionXcAssetsMultiAssets = XcAssetsV2MultiAssets | XcAssetsV3MultiAssets | XcAssetsV4MultiAssets; + +export type UnionXcAssetsMultiAsset = XcAssetsV2MultiAsset | XcAssetsV3MultiAsset | XcAssetsV4Asset; export interface XcmMultiAsset { id: { @@ -129,6 +187,14 @@ export interface XcmMultiAsset { }; } +// XCM V4 Asset +export interface XcmAsset { + id: UnionXcmMultiLocation; + fun: { + Fungible: string; + }; +} + export interface XcmV2MultiAssets { V2: XcmMultiAsset[]; } @@ -137,6 +203,10 @@ export interface XcmV3MultiAssets { V3: XcmMultiAsset[]; } +export interface XcmV4Assets { + V4: XcmAsset[]; +} + export interface XcmV2MultiAsset { V2: XcmMultiAsset; } @@ -145,6 +215,10 @@ export interface XcmV3MultiAsset { V3: XcmMultiAsset; } +export interface XcmV4Asset { + V4: XcmAsset; +} + export interface XcAssetsV2MultiAssets { V2: FungibleObjMultiAsset[]; } @@ -153,6 +227,10 @@ export interface XcAssetsV3MultiAssets { V3: FungibleObjMultiAsset[]; } +export interface XcAssetsV4MultiAssets { + V4: FungibleObjAsset[]; +} + export interface XcAssetsV2MultiAsset { V2: FungibleObjMultiAsset; } @@ -161,6 +239,10 @@ export interface XcAssetsV3MultiAsset { V3: FungibleObjMultiAsset; } +export interface XcAssetsV4Asset { + V4: FungibleObjAsset; +} + export type FungibleStrMultiAsset = { fun: { Fungible: string; @@ -170,6 +252,15 @@ export type FungibleStrMultiAsset = { }; }; +export type FungibleStrAsset = { + fun: { + Fungible: string; + }; + id: UnionXcmMultiLocation; +}; + +export type FungibleStrAssetType = FungibleStrMultiAsset | FungibleStrAsset; + export type FungibleObjMultiAsset = { fun: { Fungible: { Fungible: string }; @@ -179,7 +270,16 @@ export type FungibleObjMultiAsset = { }; }; -export type UnionXcAssetsMultiLocation = XcAssetsV2MultiLocation | XcAssetsV3MultiLocation; +export type FungibleObjAsset = { + fun: { + Fungible: { Fungible: string }; + }; + id: UnionXcmMultiLocation; +}; + +export type FungibleObjAssetType = FungibleObjMultiAsset | FungibleObjAsset; + +export type UnionXcAssetsMultiLocation = XcAssetsV2MultiLocation | XcAssetsV3MultiLocation | XcAssetsV4Location; export interface XcAssetsV2MultiLocation { V2: { @@ -197,7 +297,13 @@ export interface XcAssetsV3MultiLocation { }; } -export interface XcmV2DestBenificiary { +export interface XcAssetsV4Location { + V4: { + id: XcmV4Location; + }; +} + +export interface XcmV2DestBeneficiary { V2: { parents: string | number; interior: { @@ -206,7 +312,7 @@ export interface XcmV2DestBenificiary { }; } -export interface XcmV3DestBenificiary { +export interface XcmV3DestBeneficiary { V3: { parents: string | number; interior: { @@ -215,7 +321,16 @@ export interface XcmV3DestBenificiary { }; } -export interface XcmV2ParachainDestBenificiary { +export interface XcmV4DestBeneficiary { + V4: { + parents: string | number; + interior: { + X1: [{ AccountId32: { id: string } }]; + }; + }; +} + +export interface XcmV2ParachainDestBeneficiary { V2: { parents: string | number; interior: { @@ -226,7 +341,7 @@ export interface XcmV2ParachainDestBenificiary { }; } -export interface XcmV3ParachainDestBenificiary { +export interface XcmV3ParachainDestBeneficiary { V3: { parents: string | number; interior: { @@ -237,11 +352,24 @@ export interface XcmV3ParachainDestBenificiary { }; } -export type XcmDestBenificiaryXcAssets = - | XcmV3DestBenificiary - | XcmV2DestBenificiary - | XcmV2ParachainDestBenificiary - | XcmV3ParachainDestBenificiary; +export interface XcmV4ParachainDestBeneficiary { + V4: { + parents: string | number; + interior: { + X2: + | [{ Parachain: string }, { AccountId32: { id: string } }] + | [{ Parachain: string }, { AccountKey20: { key: string } }]; + }; + }; +} + +export type XcmDestBeneficiaryXcAssets = + | XcmV4DestBeneficiary + | XcmV3DestBeneficiary + | XcmV2DestBeneficiary + | XcmV2ParachainDestBeneficiary + | XcmV3ParachainDestBeneficiary + | XcmV4ParachainDestBeneficiary; export interface XcmWeightUnlimited { Unlimited: null | undefined; @@ -280,8 +408,8 @@ export interface CreateWeightLimitOpts { } export interface ICreateXcmType { - createBeneficiary: (accountId: string, xcmVersion: number) => XcmDestBenificiary; - createDest: (destId: string, xcmVersion: number) => XcmDestBenificiary; + createBeneficiary: (accountId: string, xcmVersion: number) => XcmDestBeneficiary; + createDest: (destId: string, xcmVersion: number) => XcmDestBeneficiary; createAssets: ( amounts: string[], xcmVersion: number, @@ -291,7 +419,7 @@ export interface ICreateXcmType { ) => Promise; createWeightLimit: (opts: CreateWeightLimitOpts) => XcmWeight; createFeeAssetItem: (api: ApiPromise, opts: CreateFeeAssetItemOpts) => Promise; - createXTokensBeneficiary?: (destChainId: string, accountId: string, xcmVersion: number) => XcmDestBenificiaryXcAssets; + createXTokensBeneficiary?: (destChainId: string, accountId: string, xcmVersion: number) => XcmDestBeneficiaryXcAssets; createXTokensAssets?: ( amounts: string[], xcmVersion: number, diff --git a/src/createXcmTypes/util/dedupeMultiAssets.spec.ts b/src/createXcmTypes/util/dedupeAssets.spec.ts similarity index 85% rename from src/createXcmTypes/util/dedupeMultiAssets.spec.ts rename to src/createXcmTypes/util/dedupeAssets.spec.ts index cd548306..2a3ce755 100644 --- a/src/createXcmTypes/util/dedupeMultiAssets.spec.ts +++ b/src/createXcmTypes/util/dedupeAssets.spec.ts @@ -1,10 +1,10 @@ // Copyright 2023 Parity Technologies (UK) Ltd. -import type { FungibleStrMultiAsset } from '../../createXcmTypes/types'; -import { dedupeMultiAssets } from './dedupeMultiAssets'; +import type { FungibleStrMultiAsset } from '../types'; +import { dedupeAssets } from './dedupeAssets'; -describe('dedupeMultiAssets', () => { - it('Should dedupe a sorted list of MultiAssets', () => { +describe('dedupeAssets', () => { + it('Should dedupe a sorted list of Assets', () => { const expected: FungibleStrMultiAsset[] = [ { fun: { @@ -33,7 +33,7 @@ describe('dedupeMultiAssets', () => { }, }, ]; - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrMultiAsset[] = [ { fun: { Fungible: '100000', @@ -75,15 +75,15 @@ describe('dedupeMultiAssets', () => { }, ]; - const deduped = dedupeMultiAssets(multiAssets); + const deduped = dedupeAssets(Assets); expect(deduped.length).toEqual(expected.length); expect(deduped[1].fun.Fungible).toEqual(expected[1].fun.Fungible); expect(JSON.stringify(deduped)).toEqual(JSON.stringify(expected)); }); - it('Should correctly dedupe a sorted list of foreign asset MultiAssets', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + it('Should correctly dedupe a sorted list of foreign asset Assets', () => { + const Assets: FungibleStrMultiAsset[] = [ { fun: { Fungible: '200000000', @@ -165,7 +165,7 @@ describe('dedupeMultiAssets', () => { }, }, ]; - const deduped = dedupeMultiAssets(multiAssets); + const deduped = dedupeAssets(Assets); expect(deduped.length).toEqual(expected.length); expect(deduped[1].fun.Fungible).toEqual(expected[1].fun.Fungible); diff --git a/src/createXcmTypes/util/dedupeAssets.ts b/src/createXcmTypes/util/dedupeAssets.ts new file mode 100644 index 00000000..a11eba7f --- /dev/null +++ b/src/createXcmTypes/util/dedupeAssets.ts @@ -0,0 +1,36 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +import type { FungibleObjAssetType, FungibleStrAssetType } from '../types'; + +/** + * This removes duplicate assets when given a sorted list + * + * @param assets FungibleStrAssetType[] | FungibleObjAssetType[] + */ +export const dedupeAssets = ( + assets: FungibleStrAssetType[] | FungibleObjAssetType[], +): FungibleStrAssetType[] | FungibleObjAssetType[] => { + const dedupedAssets = []; + + for (let i = 0; i < assets.length; i++) { + const multiAsset = assets[i]; + + if (i === 0) { + dedupedAssets.push(multiAsset); + continue; + } + + const previousAsset = dedupedAssets[dedupedAssets.length - 1]; + if (JSON.stringify(multiAsset) === JSON.stringify(previousAsset)) { + continue; + } + + dedupedAssets.push(multiAsset); + } + + if (typeof assets[0].fun.Fungible === 'string') { + return dedupedAssets as FungibleStrAssetType[]; + } + + return dedupedAssets as FungibleObjAssetType[]; +}; diff --git a/src/createXcmTypes/util/dedupeMultiAssets.ts b/src/createXcmTypes/util/dedupeMultiAssets.ts deleted file mode 100644 index e2a26127..00000000 --- a/src/createXcmTypes/util/dedupeMultiAssets.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 Parity Technologies (UK) Ltd. - -import type { FungibleObjMultiAsset, FungibleStrMultiAsset } from '../../createXcmTypes/types'; - -/** - * This removes duplicate multi assets when given a sorted list - * - * @param multiAssets MulitAsset[] - */ -export const dedupeMultiAssets = ( - multiAssets: FungibleStrMultiAsset[] | FungibleObjMultiAsset[], -): FungibleStrMultiAsset[] | FungibleObjMultiAsset[] => { - const dedupedAssets = []; - - for (let i = 0; i < multiAssets.length; i++) { - const multiAsset = multiAssets[i]; - - if (i === 0) { - dedupedAssets.push(multiAsset); - continue; - } - - const previousAsset = dedupedAssets[dedupedAssets.length - 1]; - if (JSON.stringify(multiAsset) === JSON.stringify(previousAsset)) { - continue; - } - - dedupedAssets.push(multiAsset); - } - - if (typeof multiAssets[0].fun.Fungible === 'string') { - return dedupedAssets as FungibleStrMultiAsset[]; - } - - return dedupedAssets as FungibleObjMultiAsset[]; -}; diff --git a/src/createXcmTypes/util/fetchPalletInstanceId.spec.ts b/src/createXcmTypes/util/fetchPalletInstanceId.spec.ts index 249b3026..28c71353 100644 --- a/src/createXcmTypes/util/fetchPalletInstanceId.spec.ts +++ b/src/createXcmTypes/util/fetchPalletInstanceId.spec.ts @@ -23,7 +23,7 @@ describe('fetchPalletInstanceId', () => { it('Should correctly error when both foreign assets and pool assets are true', () => { const err = () => fetchPalletInstanceId(mockSystemApi, true, true); - expect(err).toThrowError("Can't find the appropriate pallet when both liquid tokens and foreign assets"); + expect(err).toThrow("Can't find the appropriate pallet when both liquid tokens and foreign assets"); }); it('Should correctly return an empty string when the assets pallet is not found', () => { const res = fetchPalletInstanceId(mockBifrostParachainApi, false, false); diff --git a/src/createXcmTypes/util/foreignAssetsMultiLocationExists.spec.ts b/src/createXcmTypes/util/foreignAssetsMultiLocationExists.spec.ts index a415191c..58024377 100644 --- a/src/createXcmTypes/util/foreignAssetsMultiLocationExists.spec.ts +++ b/src/createXcmTypes/util/foreignAssetsMultiLocationExists.spec.ts @@ -31,7 +31,7 @@ describe('foreignMultiAssetMultiLocationExists', () => { await expect(async () => { await foreignAssetsMultiLocationExists(adjustedMockSystemApi, registry, multiLocation, 2); - }).rejects.toThrowError(expectedError); + }).rejects.toThrow(expectedError); }); it('Should throw an error when a comma is found in a multilocation keys value', async () => { @@ -41,7 +41,7 @@ describe('foreignMultiAssetMultiLocationExists', () => { await expect(async () => { await foreignAssetsMultiLocationExists(adjustedMockSystemApi, registry, multiLocation, 2); - }).rejects.toThrowError(expectedError); + }).rejects.toThrow(expectedError); }); it('Should correctly cache a valid foreign asset not found in the cache or registry', async () => { diff --git a/src/createXcmTypes/util/getAssetId.spec.ts b/src/createXcmTypes/util/getAssetId.spec.ts index e3e636b4..b42ef18e 100644 --- a/src/createXcmTypes/util/getAssetId.spec.ts +++ b/src/createXcmTypes/util/getAssetId.spec.ts @@ -104,13 +104,13 @@ describe('getAssetId', () => { it('Should correctly error when given an invalid xcAsset symbol', async () => { await expect(async () => { await getAssetId(moonriverAssetsApi.api, registry, 'TEST', 'moonriver', 2, true); - }).rejects.toThrowError(`parachain assetId TEST is not a valid symbol assetIid in moonriver`); + }).rejects.toThrow(`parachain assetId TEST is not a valid symbol assetIid in moonriver`); }); it('Should correctly error when given an invalid integer xcAssetId', async () => { await expect(async () => { await getAssetId(moonriverAssetsApi.api, registry, '25830838603860', 'moonriver', 2, true); - }).rejects.toThrowError(`parachain assetId 25830838603860 is not a valid integer assetIid in moonriver`); + }).rejects.toThrow(`parachain assetId 25830838603860 is not a valid integer assetIid in moonriver`); }); }); }); diff --git a/src/createXcmTypes/util/getXcAssetMultiLocationByAssetId.spec.ts b/src/createXcmTypes/util/getXcAssetMultiLocationByAssetId.spec.ts index 2ea74fe1..fe25298e 100644 --- a/src/createXcmTypes/util/getXcAssetMultiLocationByAssetId.spec.ts +++ b/src/createXcmTypes/util/getXcAssetMultiLocationByAssetId.spec.ts @@ -62,7 +62,7 @@ describe('getXcAssetMultiLocationByAssetId', () => { await expect(async () => { await getXcAssetMultiLocationByAssetId(bifrostApi.api, assetId, specName, xcmVersion, bifrostRegistry); - }).rejects.toThrowError(`parachain assetId vmover is not a valid symbol assetId in bifrost`); + }).rejects.toThrow(`parachain assetId vmover is not a valid symbol assetId in bifrost`); }); }); @@ -124,7 +124,7 @@ describe('getXcAssetMultiLocationByAssetId', () => { await expect(async () => { await getXcAssetMultiLocationByAssetId(moonriverApi.api, assetId, specName, xcmVersion, moonriverRegistry); - }).rejects.toThrowError(`parachain assetId mover is not a valid symbol assetIid in moonriver`); + }).rejects.toThrow(`parachain assetId mover is not a valid symbol assetIid in moonriver`); }); it('Should correctly error when given an invalid integer assetId ', async () => { @@ -134,7 +134,7 @@ describe('getXcAssetMultiLocationByAssetId', () => { await expect(async () => { await getXcAssetMultiLocationByAssetId(moonriverApi.api, assetId, specName, xcmVersion, moonriverRegistry); - }).rejects.toThrowError(`assetId 242424332422323423424 is not a valid symbol or integer asset id for moonriver`); + }).rejects.toThrow(`assetId 242424332422323423424 is not a valid symbol or integer asset id for moonriver`); }); }); }); diff --git a/src/createXcmTypes/util/sortMultiAssetsAscending.spec.ts b/src/createXcmTypes/util/sortAssetsAscending.spec.ts similarity index 90% rename from src/createXcmTypes/util/sortMultiAssetsAscending.spec.ts rename to src/createXcmTypes/util/sortAssetsAscending.spec.ts index 763d6e3b..85b0e8aa 100644 --- a/src/createXcmTypes/util/sortMultiAssetsAscending.spec.ts +++ b/src/createXcmTypes/util/sortAssetsAscending.spec.ts @@ -1,11 +1,11 @@ // Copyright 2023 Parity Technologies (UK) Ltd. -import { FungibleObjMultiAsset, FungibleStrMultiAsset } from '../../createXcmTypes/types'; -import { sortMultiAssetsAscending } from './sortMultiAssetsAscending'; +import { FungibleObjAssetType, FungibleStrAssetType } from '../types'; +import { sortAssetsAscending } from './sortAssetsAscending'; -describe('sortMultiAssetsAscending', () => { +describe('sortAssetsAscending', () => { it('Should sort an unsorted multi asset array in ascending order', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '300000000', @@ -47,7 +47,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '200000000', @@ -89,7 +89,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -98,7 +98,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should sort an unsorted multi foreign asset array of X1s in ascending order', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -146,7 +146,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -194,7 +194,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -203,7 +203,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should correctly sort an unsorted multi asset array with the same `parents` and `Junction`type based on their `Junction` keys', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -236,7 +236,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -269,7 +269,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -278,7 +278,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should correctly sort an unsorted multiasset array with in ascending order when parents values are different', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -326,17 +326,17 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', }, id: { Concrete: { - parents: 1, + parents: 0, interior: { X1: { - Parachain: '2023', + GeneralKey: '0xA83397cEfcCFdE9re7B23F3g0C462eF099E9E995', }, }, }, @@ -348,10 +348,10 @@ describe('sortMultiAssetsAscending', () => { }, id: { Concrete: { - parents: 2, + parents: 1, interior: { X1: { - PalletInstance: '50', + Parachain: '2023', }, }, }, @@ -363,10 +363,10 @@ describe('sortMultiAssetsAscending', () => { }, id: { Concrete: { - parents: 0, + parents: 2, interior: { X1: { - GeneralKey: '0xA83397cEfcCFdE9re7B23F3g0C462eF099E9E995', + PalletInstance: '50', }, }, }, @@ -374,16 +374,16 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + sortAssetsAscending(Assets); - expect(res.length).toEqual(expected.length); - expect(res[0].id).toEqual(expected[0].id); - expect(res[res.length - 1].fun).toEqual(expected[expected.length - 1].fun); - expect(JSON.stringify(res)).toEqual(JSON.stringify(expected)); + expect(Assets.length).toEqual(expected.length); + expect(Assets[0].id).toEqual(expected[0].id); + expect(Assets[Assets.length - 1].fun).toEqual(expected[expected.length - 1].fun); + expect(JSON.stringify(Assets)).toEqual(JSON.stringify(expected)); }); it('Should correctly sort based on differing first Junction key value for X2s when other fields are the same', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -412,7 +412,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -441,7 +441,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -450,7 +450,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should correctly sort based on differing third Junction key value for X3s when all other fields are the same', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '200000000', @@ -479,7 +479,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '200000000', @@ -508,7 +508,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -517,7 +517,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should correctly sort different X2 MultiLocations', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '200000000', @@ -559,7 +559,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '200000000', @@ -601,7 +601,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -610,7 +610,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should correctly sort based on differing GeneralIndex values for X3s when all other fields are the same', () => { - const multiAssets: FungibleObjMultiAsset[] = [ + const Assets: FungibleObjAssetType[] = [ { fun: { Fungible: { Fungible: '200000000' }, @@ -639,7 +639,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleObjMultiAsset[] = [ + const expected: FungibleObjAssetType[] = [ { fun: { Fungible: { Fungible: '200000000' }, @@ -668,7 +668,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets) as FungibleObjMultiAsset[]; + const res = sortAssetsAscending(Assets) as FungibleObjAssetType[]; expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -677,7 +677,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should correctly sort based on differing third Junction keys for X3s when all other fields are the same', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '200000000', @@ -710,7 +710,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '200000000', @@ -743,7 +743,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -752,7 +752,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should sort an unsorted multi foreign asset array of Here, X1s and X2s in ascending order', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -904,7 +904,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '200000000', @@ -1056,7 +1056,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); @@ -1065,7 +1065,7 @@ describe('sortMultiAssetsAscending', () => { }); it('Should correctly sort an unsorted multiasset array based on fungible value when all other values are the same', () => { - const multiAssets: FungibleStrMultiAsset[] = [ + const Assets: FungibleStrAssetType[] = [ { fun: { Fungible: '200000', @@ -1098,7 +1098,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const expected: FungibleStrMultiAsset[] = [ + const expected: FungibleStrAssetType[] = [ { fun: { Fungible: '100000', @@ -1131,7 +1131,7 @@ describe('sortMultiAssetsAscending', () => { }, ]; - const res = sortMultiAssetsAscending(multiAssets); + const res = sortAssetsAscending(Assets); expect(res.length).toEqual(expected.length); expect(res[0].id).toEqual(expected[0].id); diff --git a/src/createXcmTypes/util/sortMultiAssetsAscending.ts b/src/createXcmTypes/util/sortAssetsAscending.ts similarity index 52% rename from src/createXcmTypes/util/sortMultiAssetsAscending.ts rename to src/createXcmTypes/util/sortAssetsAscending.ts index bae1c321..11ba6b3a 100644 --- a/src/createXcmTypes/util/sortMultiAssetsAscending.ts +++ b/src/createXcmTypes/util/sortAssetsAscending.ts @@ -5,45 +5,115 @@ import { stringToHex } from '@polkadot/util'; import { BN } from 'bn.js'; +import { BaseError, BaseErrorsEnum } from '../../errors'; +import type { RequireOnlyOne } from '../../types'; +import { validateNumber } from '../../validate'; import type { - FungibleObjMultiAsset, - FungibleStrMultiAsset, + FungibleObjAssetType, + FungibleStrAssetType, XcmV2Junction, + XcmV2Junctions, XcmV3Junction, -} from '../../createXcmTypes/types'; -import { validateNumber } from '../../validate'; + XcmV3Junctions, + XcmV4Junction, + XcmV4Junctions, +} from '../types'; /** * This sorts a list of multiassets in ascending order based on their id. * - * @param multiAssets MultiAsset[] + * @param assets FungibleStrAssetType[] | FungibleObjAssetType[] */ -export const sortMultiAssetsAscending = (multiAssets: FungibleStrMultiAsset[] | FungibleObjMultiAsset[]) => { - return multiAssets.sort((a, b) => { +export const sortAssetsAscending = (assets: FungibleStrAssetType[] | FungibleObjAssetType[]) => { + return assets.sort((a, b) => { let parentSortOrder = 0; // sort order based on parents value let interiorMultiLocationTypeSortOrder = 0; // sort order based on interior multilocation type value (e.g. X1 < X2) let interiorMultiLocationSortOrder = 0; // sort order based on multilocation junction values let fungibleSortOrder = 0; // sort order based on fungible value if (typeof a.fun.Fungible === 'string' && typeof b.fun.Fungible === 'string') { - fungibleSortOrder = (a as FungibleStrMultiAsset).fun.Fungible.localeCompare( - (b as FungibleStrMultiAsset).fun.Fungible, - ); + fungibleSortOrder = a.fun.Fungible.localeCompare(b.fun.Fungible); + } else if (typeof a.fun.Fungible === 'object' && typeof b.fun.Fungible === 'object') { + fungibleSortOrder = a.fun.Fungible.Fungible.localeCompare(b.fun.Fungible.Fungible); + } + let aParents: string | number | undefined = undefined; + let bParents: string | number | undefined = undefined; + if ('Concrete' in a.id && 'Concrete' in b.id) { + if ('Parents' in a.id.Concrete && 'Parents' in b.id.Concrete) { + aParents = a.id.Concrete['Parents'] as string | number; + bParents = b.id.Concrete['Parents'] as string | number; + } else { + aParents = a.id.Concrete.parents as string | number; + bParents = b.id.Concrete.parents as string | number; + } + } else if ('parents' in a.id && 'parents' in b.id) { + aParents = a.id.parents as string | number; + bParents = b.id.parents as string | number; } else { - fungibleSortOrder = (a as FungibleObjMultiAsset).fun.Fungible.Fungible.localeCompare( - (b as FungibleObjMultiAsset).fun.Fungible.Fungible, + aParents = a.id['Parents'] as string | number; + bParents = b.id['Parents'] as string | number; + } + + // Should never hit this, this exists to make the typescript compiler happy. + if (aParents === undefined || bParents === undefined) { + throw new BaseError( + `Unable to determine parents value for assets ${JSON.stringify(a)} and ${JSON.stringify(b)}`, + BaseErrorsEnum.InternalError, ); } - const aParents = (a.id.Concrete.parents || a.id.Concrete['Parents']) as string | number; - const bParents = (b.id.Concrete.parents || b.id.Concrete['Parents']) as string | number; if (aParents < bParents) { parentSortOrder = -1; } else if (aParents > bParents) { parentSortOrder = 1; } - const aInterior = a.id.Concrete.interior || a.id.Concrete['Interior']; - const bInterior = b.id.Concrete.interior || b.id.Concrete['Interior']; + let aInterior: + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne + | undefined = undefined; + let bInterior: + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne + | undefined = undefined; + + if ('Concrete' in a.id && 'Concrete' in b.id) { + if ('Interior' in a.id.Concrete && 'Interior' in b.id.Concrete) { + aInterior = a.id.Concrete['Interior'] as + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne; + bInterior = b.id.Concrete['Interior'] as + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne; + } else { + aInterior = a.id.Concrete.interior; + bInterior = b.id.Concrete.interior; + } + } else if ('interior' in a.id && 'interior' in b.id) { + aInterior = a.id.interior; + bInterior = b.id.interior; + } else if ('Interior' in a.id && 'Interior' in b.id) { + aInterior = a.id['Interior'] as + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne; + bInterior = b.id['Interior'] as + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne; + } + + // Should never hit this, this exists to make the typescript compiler happy. + if (aInterior === undefined || bInterior === undefined) { + throw new BaseError( + `Unable to determine interior values for locations ${JSON.stringify(a)} and ${JSON.stringify(b)}`, + BaseErrorsEnum.InternalError, + ); + } + const aInteriorType = Object.keys(aInterior)[0]; const bInteriorType = Object.keys(bInterior)[0]; if (aInteriorType < bInteriorType) { @@ -61,13 +131,58 @@ export const sortMultiAssetsAscending = (multiAssets: FungibleStrMultiAsset[] | }; const getSameJunctionMultiLocationSortOrder = ( - a: FungibleStrMultiAsset | FungibleObjMultiAsset, - b: FungibleStrMultiAsset | FungibleObjMultiAsset, + a: FungibleStrAssetType | FungibleObjAssetType, + b: FungibleStrAssetType | FungibleObjAssetType, ): number => { let sortOrder = 0; - const aInterior = a.id.Concrete.interior || a.id.Concrete['Interior']; - const bInterior = b.id.Concrete.interior || b.id.Concrete['Interior']; + let aInterior: + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne + | undefined = undefined; + let bInterior: + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne + | undefined = undefined; + + if ('Concrete' in a.id && 'Concrete' in b.id) { + if ('Interior' in a.id.Concrete && 'Interior' in b.id.Concrete) { + aInterior = a.id.Concrete['Interior'] as + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne; + bInterior = b.id.Concrete['Interior'] as + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne; + } else { + aInterior = a.id.Concrete.interior; + bInterior = b.id.Concrete.interior; + } + } else if ('interior' in a.id && 'interior' in b.id) { + aInterior = a.id.interior; + bInterior = b.id.interior; + } else if ('Interior' in a.id && 'Interior' in b.id) { + aInterior = a.id['Interior'] as + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne; + bInterior = b.id['Interior'] as + | RequireOnlyOne + | RequireOnlyOne + | RequireOnlyOne; + } + + // Should never hit this, this exists to make the typescript compiler happy. + if (aInterior === undefined || bInterior === undefined) { + throw new BaseError( + `Unable to determine interior values for locations ${JSON.stringify(a)} and ${JSON.stringify(b)}`, + BaseErrorsEnum.InternalError, + ); + } + switch (Object.keys(aInterior)[0]) { case 'X1': const aX1Type = Object.keys(aInterior.X1!)[0]; @@ -114,7 +229,7 @@ const getSameJunctionMultiLocationSortOrder = ( return sortOrder; }; -type UnionJunction = XcmV3Junction | XcmV2Junction; +type UnionJunction = XcmV4Junction | XcmV3Junction | XcmV2Junction; type MultiLocationJunctions = | [UnionJunction, UnionJunction] diff --git a/src/errors/checkLocalTxInput/checkLocalTxInput.spec.ts b/src/errors/checkLocalTxInput/checkLocalTxInput.spec.ts index 9325fa08..4a267bdd 100644 --- a/src/errors/checkLocalTxInput/checkLocalTxInput.spec.ts +++ b/src/errors/checkLocalTxInput/checkLocalTxInput.spec.ts @@ -30,26 +30,26 @@ describe('checkLocalTxInput', () => { it('Should correctly throw an error for incorrect length on `assetIds`', async () => { await expect(async () => { await checkLocalTxInput(systemAssetsApi.api, ['1', '2'], ['10000'], specName, registry, 2, false, false); - }).rejects.toThrowError( + }).rejects.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 throw an error for incorrect length on `amounts`', async () => { await expect(async () => { await checkLocalTxInput(systemAssetsApi.api, ['1'], ['10000', '20000'], specName, registry, 2, false, false); - }).rejects.toThrowError( + }).rejects.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 throw an error with an incorrect assetId', async () => { await expect(async () => { await checkLocalTxInput(systemAssetsApi.api, ['TST'], ['10000'], specName, registry, 2, false, false); - }).rejects.toThrowError('assetId TST is not a valid symbol or integer asset id for statemine'); + }).rejects.toThrow('assetId TST is not a valid symbol or integer asset id for statemine'); }); it("Should correctly throw an error when the integer assetId doesn't exist", async () => { await expect(async () => { await checkLocalTxInput(systemAssetsApi.api, ['9876111'], ['10000'], specName, registry, 2, false, false); - }).rejects.toThrowError('general index for assetId 9876111 was not found'); + }).rejects.toThrow('general index for assetId 9876111 was not found'); }); it('Should correctly return ForeignAssets when given a valid multilocation', async () => { @@ -81,7 +81,7 @@ describe('checkLocalTxInput', () => { true, false, ); - }).rejects.toThrowError(expectedError); + }).rejects.toThrow(expectedError); }); it("Should correctly throw an error when the given multilocation doesn't exist", async () => { const nonExistentMultiLocationStr = @@ -99,7 +99,7 @@ describe('checkLocalTxInput', () => { true, false, ); - }).rejects.toThrowError(expectedError); + }).rejects.toThrow(expectedError); }); it('Should correctly throw an error when the given multilocation assetIds is empty', async () => { @@ -107,6 +107,6 @@ describe('checkLocalTxInput', () => { await expect(async () => { await checkLocalTxInput(systemAssetsApi.api, [], ['10000'], specName, registry, 2, true, false); - }).rejects.toThrowError(expectedError); + }).rejects.toThrow(expectedError); }); }); diff --git a/src/errors/checkXcmTxInputs.spec.ts b/src/errors/checkXcmTxInputs.spec.ts index 1541c907..90519af2 100644 --- a/src/errors/checkXcmTxInputs.spec.ts +++ b/src/errors/checkXcmTxInputs.spec.ts @@ -46,7 +46,7 @@ const runTests = async (tests: Test[]) => { false, false, ); - }).rejects.toThrowError(errorMessage); + }).rejects.toThrow(errorMessage); } }; @@ -181,7 +181,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError(errorMessage); + }).rejects.toThrow(errorMessage); } }); @@ -224,7 +224,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError(errorMessage); + }).rejects.toThrow(errorMessage); } }); @@ -248,7 +248,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError(errorMessage); + }).rejects.toThrow(errorMessage); } }); @@ -291,7 +291,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError(errorMessage); + }).rejects.toThrow(errorMessage); } }); it('Should error when direction is SystemToPara and the multilocation assetId is not found in the system parachains foreignAssets or chain state', async () => { @@ -321,7 +321,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError(errorMessage); + }).rejects.toThrow(errorMessage); } }); it('Should correctly error when direction is ParaToSystem and the provided integer assetId is not found in the system parachains assetIds', async () => { @@ -350,7 +350,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError(errorMessage); + }).rejects.toThrow(errorMessage); } }); it('Should correctly error when direction is ParaToSystem and the string assetId has no match in the parachains asset symbols', async () => { @@ -386,7 +386,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError(errorMessage); + }).rejects.toThrow(errorMessage); } }); it('Should error for an invalid erc20 token.', async () => { @@ -405,7 +405,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError('(ParaToSystem) assetId 0x1234, is not a valid erc20 token.'); + }).rejects.toThrow('(ParaToSystem) assetId 0x1234, is not a valid erc20 token.'); }); it('Should error when an invalid token is passed into a liquidTokenTransfer', async () => { const registry = new Registry('westmint', {}); @@ -424,7 +424,7 @@ describe('checkAssetIds', () => { false, isLiquidTokenTransfer, ); - }).rejects.toThrowError('Liquid Tokens must be valid Integers'); + }).rejects.toThrow('Liquid Tokens must be valid Integers'); }); it('Should error when a token does not exist in the registry or node', async () => { const registry = new Registry('westmint', {}); @@ -443,7 +443,7 @@ describe('checkAssetIds', () => { false, isLiquidTokenTransfer, ); - }).rejects.toThrowError( + }).rejects.toThrow( 'No liquid token asset was detected. When setting the option "transferLiquidToken" to true a valid liquid token assetId must be present.', ); }); @@ -543,9 +543,7 @@ describe('checkAssetIds', () => { false, false, ); - }).rejects.toThrowError( - "The current input for assetId's does not meet our specifications for ParaToRelay transfers.", - ); + }).rejects.toThrow("The current input for assetId's does not meet our specifications for ParaToRelay transfers."); }); }); @@ -558,7 +556,7 @@ describe('checkIfNativeRelayChainAssetPresentInMultiAssetIdList', () => { const registry = new Registry(specName, {}); const err = () => checkIfNativeRelayChainAssetPresentInMultiAssetIdList(assetIds, registry); - expect(err).toThrowError(expectErrorMessage); + expect(err).toThrow(expectErrorMessage); }); }); @@ -576,7 +574,7 @@ describe('checkMultiLocationsContainOnlyNativeOrForeignAssetsOfDestChain', () => const err = () => checkMultiLocationsContainOnlyNativeOrForeignAssetsOfDestChain(xcmDirection, destChainId, multiLocationAssetIds); - expect(err).toThrowError(expectedErrorMessage); + expect(err).toThrow(expectedErrorMessage); }); it('Should correctly avoid throwing an error when isForeignAssetsTransfer and only native multilocations are in assetIds for direction SystemToPara', () => { @@ -590,7 +588,7 @@ describe('checkMultiLocationsContainOnlyNativeOrForeignAssetsOfDestChain', () => const err = () => checkMultiLocationsContainOnlyNativeOrForeignAssetsOfDestChain(xcmDirection, destChainId, multiLocationAssetIds); - expect(err).not.toThrowError(); + expect(err).not.toThrow(); }); it('Should correctly avoid throwing an error when isForeignAssetsTransfer and only foreign multilocations are in assetIds for direction SystemToPara', () => { @@ -604,7 +602,7 @@ describe('checkMultiLocationsContainOnlyNativeOrForeignAssetsOfDestChain', () => const err = () => checkMultiLocationsContainOnlyNativeOrForeignAssetsOfDestChain(xcmDirection, destChainId, multiLocationAssetIds); - expect(err).not.toThrowError(); + expect(err).not.toThrow(); }); }); @@ -614,7 +612,7 @@ describe('checkAssetIdsLengthIsValid', () => { const err = () => checkAssetIdsLengthIsValid(assetIds); - expect(err).toThrowError('Maximum number of assets allowed for transfer is 2. Found 3 assetIds'); + expect(err).toThrow('Maximum number of assets allowed for transfer is 2. Found 3 assetIds'); }); it('Should correctly not error when less 2 or less assetIds are passed in', () => { const assetIds = ['ksm', '1984']; @@ -786,7 +784,7 @@ describe('checkParaAssets', () => { await expect(async () => { await checkParaAssets(adjustedMockMoonriverParachainApi, assetId, specName, registry, Direction.ParaToSystem); - }).rejects.toThrowError('(ParaToSystem) symbol assetId xcUSDfake not found for parachain moonriver'); + }).rejects.toThrow('(ParaToSystem) symbol assetId xcUSDfake not found for parachain moonriver'); }); it('Should correctly error when an invalid integer assetId is provided', async () => { const assetId = '2096586909097964981698161'; @@ -795,7 +793,7 @@ describe('checkParaAssets', () => { await expect(async () => { await checkParaAssets(adjustedMockMoonriverParachainApi, assetId, specName, registry, Direction.ParaToSystem); - }).rejects.toThrowError('(ParaToSystem) integer assetId 2096586909097964981698161 not found in moonriver'); + }).rejects.toThrow('(ParaToSystem) integer assetId 2096586909097964981698161 not found in moonriver'); }); it('Should correctly error when a valid assetId is not found in the xcAsset registry', async () => { const assetId = '999999999999999999999999999999999999999'; @@ -804,7 +802,7 @@ describe('checkParaAssets', () => { await expect(async () => { await checkParaAssets(adjustedMockMoonriverParachainApi, assetId, specName, registry, Direction.ParaToSystem); - }).rejects.toThrowError('unable to identify xcAsset with ID 999999999999999999999999999999999999999'); + }).rejects.toThrow('unable to identify xcAsset with ID 999999999999999999999999999999999999999'); }); describe('cache', () => { diff --git a/src/errors/checkXcmVersion.spec.ts b/src/errors/checkXcmVersion.spec.ts index 708813de..41e0c839 100644 --- a/src/errors/checkXcmVersion.spec.ts +++ b/src/errors/checkXcmVersion.spec.ts @@ -5,6 +5,6 @@ import { checkXcmVersion } from './checkXcmVersion'; describe('checkXcmVersion', () => { it('Should correctly throw an error on a unsupported version', () => { const err = () => checkXcmVersion(1); - expect(err).toThrowError('1 is not a supported xcm version. Supported versions are: 2 and 3'); + expect(err).toThrow('1 is not a supported xcm version. Supported versions are: 2, 3 and 4'); }); }); diff --git a/src/errors/checkXcmVersion.ts b/src/errors/checkXcmVersion.ts index 234710f4..4804a63f 100644 --- a/src/errors/checkXcmVersion.ts +++ b/src/errors/checkXcmVersion.ts @@ -11,7 +11,7 @@ import { BaseError, BaseErrorsEnum } from './BaseError'; export const checkXcmVersion = (version: number) => { if (!SUPPORTED_XCM_VERSIONS.includes(version)) { throw new BaseError( - `${version} is not a supported xcm version. Supported versions are: ${SUPPORTED_XCM_VERSIONS[0]} and ${SUPPORTED_XCM_VERSIONS[1]}`, + `${version} is not a supported xcm version. Supported versions are: ${SUPPORTED_XCM_VERSIONS[0]}, ${SUPPORTED_XCM_VERSIONS[1]} and ${SUPPORTED_XCM_VERSIONS[2]}`, BaseErrorsEnum.InvalidXcmVersion, ); } diff --git a/src/sanitize/sanitizeAddress.spec.ts b/src/sanitize/sanitizeAddress.spec.ts index 128df7d8..b3b1ef53 100644 --- a/src/sanitize/sanitizeAddress.spec.ts +++ b/src/sanitize/sanitizeAddress.spec.ts @@ -14,7 +14,7 @@ describe('sanitizeAddress', () => { ); }); it('Should error with an invalid address', () => { - expect(() => sanitizeAddress('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDwU')).toThrowError( + expect(() => sanitizeAddress('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDwU')).toThrow( 'Invalid decoded address checksum', ); }); diff --git a/src/util/getFeeAssetItemIndex.spec.ts b/src/util/getFeeAssetItemIndex.spec.ts index ecd579e9..fbd99432 100644 --- a/src/util/getFeeAssetItemIndex.spec.ts +++ b/src/util/getFeeAssetItemIndex.spec.ts @@ -282,7 +282,7 @@ describe('getFeeAssetItemIndex', () => { await expect(async () => { await getFeeAssetItemIndex(systemAssetsApi.api, registry, paysWithFeeDest, multiAssets, specName, 2, false); - }).rejects.toThrowError( + }).rejects.toThrow( 'Invalid paysWithFeeDest value. 1984 did not match any asset in assets: {"X2":[{"PalletInstance":"50"},{"GeneralIndex":"1337"}]},{"Here":""}', ); }); diff --git a/src/util/getFeeAssetItemIndex.ts b/src/util/getFeeAssetItemIndex.ts index 181be56e..895b523d 100644 --- a/src/util/getFeeAssetItemIndex.ts +++ b/src/util/getFeeAssetItemIndex.ts @@ -2,7 +2,7 @@ import { ApiPromise } from '@polkadot/api'; -import { FungibleStrMultiAsset } from '../createXcmTypes/types'; +import { FungibleStrAssetType } from '../createXcmTypes/types'; import { getAssetId } from '../createXcmTypes/util/getAssetId'; import { BaseError, BaseErrorsEnum } from '../errors'; import { Registry } from '../registry'; @@ -21,7 +21,7 @@ export const getFeeAssetItemIndex = async ( api: ApiPromise, registry: Registry, paysWithFeeDest: string, - multiAssets: FungibleStrMultiAsset[], + multiAssets: FungibleStrAssetType[], specName: string, xcmVersion: number, isForeignAssetsTransfer?: boolean, @@ -36,11 +36,14 @@ export const getFeeAssetItemIndex = async ( for (let i = 0; i < multiAssets.length; i++) { const multiAsset = multiAssets[i]; - const multiAssetInterior = multiAsset.id.Concrete.interior || multiAsset.id.Concrete['Interior']; + const multiAssetInterior = + 'Concrete' in multiAsset.id + ? multiAsset.id.Concrete.interior || multiAsset.id.Concrete['Interior'] + : multiAsset.id.interior || multiAsset.id['Interior']; if (isRelayFeeAsset) { // if the asset id is a relay asset, match Here interior - if (multiAsset.id.Concrete.interior.Here || multiAsset.id.Concrete.interior['here']) { + if (multiAssetInterior.Here || multiAssetInterior['here']) { result = i; break; } @@ -97,10 +100,18 @@ export const getFeeAssetItemIndex = async ( } if (result === -1) { + const assets = multiAssets + .map((asset) => { + if ('Concrete' in asset.id) { + return JSON.stringify(asset.id.Concrete.interior); + } else { + return JSON.stringify(asset.id.interior); + } + }) + .join(','); + throw new BaseError( - `Invalid paysWithFeeDest value. ${paysWithFeeDest} did not match any asset in assets: ${multiAssets - .map((asset) => JSON.stringify(asset.id.Concrete.interior)) - .join(',')}`, + `Invalid paysWithFeeDest value. ${paysWithFeeDest} did not match any asset in assets: ${assets}`, BaseErrorsEnum.InvalidInput, ); }