Skip to content

Migration Guide — v11 ➡ v12

This guide covers all breaking changes and new features introduced in version v12 of the ParaSpell XCM Tools.
These changes affect XCM SDK, XCM API, XCM Router, and XCM Analyser.


Overview of Breaking Changes

v12 introduces following major changes:

  1. Removal of deprecated TNode after its prior replacement by TChain.
  2. Removal of currency: string fields from XCM Fee and Dry-run queries.
  3. Removal of legacy assetHub and bridgeHub objects.
  4. Rename of balance query:
    • getAssetBalance renamed to getBalance.
    • Removal of getNativeAssetBalance and getForeignAssetBalance.
    • currency parameter is now optional and defaults to the chain’s native asset.
  5. Migration to ESM-only modules across SDKs and Router (CommonJS no longer supported, except for XCM Analyser which now fully supports CommonJS).
  6. Deprecation of payment-info-based XCM fee estimators (getXcmFeeEstimate, getOriginXcmFeeEstimate).
  7. Asset registry unification:
    • Removal of nativeAssets and foreignAssets.
    • All assets are now stored under a single assets[] array.
  8. Refactored and expanded error model, replacing generic errors with more specific error classes.
  9. Mythos asset selection restricted to Substrate location only; Ethereum MYTH location is no longer accepted.

Each topic is detailed below.


1. Removal of deprecated TNode types (Issue 1167)

What changed

In earlier versions, the SDK exposed a TNode types to represent chain identifiers.
During v11, it was replaced by TChain types and marked as deprecated.
In v12, TNode types have been fully removed. All available TChain types can be found listed here.

Action Required:

  • Replace all usages of TNode with TChain types.

2. Removal of currency: Symbol field from XCM Fee queries and Dry-run queries (Issue 1193)

What changed

In early versions of our XCM Fee queries we have introduced currency identifier in form of symbol. As features grew more robust the need for full asset object was necessary. Mainly because primary selection method is by location. Some chains have multiple assets under similar or same symbols with only difference between them being capital or lower case letters. The currency: symbol grew obsolete after full asset object introducement. Keeping currency caused duplication and confusion.

Previously, query responses included both:

  • a currency: "SYMBOL" string field, and
  • a full asset object with detailed metadata.

Affected queries: XCM Fee queries and Dry-run queries.

Example Before

json
{
  "currency": "ASTR",
  "asset": {
    "symbol": "ASTR",
    "isNative": true,
    "decimals": 18,
    "existentialDeposit": "1000000",
    "location": {
      "parents": 1,
      "interior": {
        "X1": [{ "Parachain": 2006 }]
      }
    }
  }
}

Example after

json
{
  "asset": {
    "symbol": "ASTR",
    "isNative": true,
    "decimals": 18,
    "existentialDeposit": "1000000",
    "location": {
      "parents": 1,
      "interior": {
        "X1": [{ "Parachain": 2006 }]
      }
    }
  }
}

Action Required:

Remove usage of the currency field.

Use asset.symbol wherever currency was previously used.

3. Unified Multi-Hop representation (Issue 1168)

Previously, we only had AssetHub and BridgeHub as possible hop chains in XCM Fee and Dry-run queries. As the features grew more robust we added a hop array that is more generic and could house any parachain as hop. We removed AssetHub and BridgeHub objects because they would occur duplicitly both on their own and also in hops array which caused unnecessary confusion.

In v12, hops[] becomes the single canonical structure.
assetHub and bridgeHub top-level objects are removed.

Example Before

json
{
  "origin": {
    "weight": {
      "refTime": "30975850052",
      "proofSize": "7590"
    },
    "fee": "82694150037",
    "feeType": "dryRun",
    "sufficient": false,
    "currency": "BNC",
    "asset": {
      "symbol": "BNC",
      "decimals": 12,
      "existentialDeposit": "10000000000",
      "location": {
        "parents": 1,
        "interior": {
          "X2": [
            {
              "Parachain": 2030
            },
            {
              "GeneralKey": {
                "length": 2,
                "data": "0x0001000000000000000000000000000000000000000000000000000000000000"
              }
            }
          ]
        }
      },
      "isNative": true,
      "isFeeAsset": true
    }
  },
  "assetHub": {
    "fee": "79429",
    "feeType": "dryRun",
    "currency": "USDt",
    "asset": {
      "assetId": "1984",
      "symbol": "USDt",
      "decimals": 6,
      "location": {
        "parents": 1,
        "interior": {
          "X3": [
            {
              "Parachain": 1000
            },
            {
              "PalletInstance": 50
            },
            {
              "GeneralIndex": 1984
            }
          ]
        }
      },
      "existentialDeposit": "10000",
      "isFeeAsset": true
    }
  },
  "destination": {
    "fee": "393",
    "feeType": "dryRun",
    "currency": "USDT",
    "asset": {
      "assetId": "10",
      "symbol": "USDT",
      "decimals": 6,
      "existentialDeposit": "10000",
      "location": {
        "parents": 1,
        "interior": {
          "X3": [
            {
              "Parachain": 1000
            },
            {
              "PalletInstance": 50
            },
            {
              "GeneralIndex": 1984
            }
          ]
        }
      },
      "isFeeAsset": true,
      "alias": "USDT1"
    }
  },
  "hops": [
    {
      "chain": "AssetHubPolkadot",
      "result": {
        "fee": "79429",
        "feeType": "dryRun",
        "currency": "USDt",
        "asset": {
          "assetId": "1984",
          "symbol": "USDt",
          "decimals": 6,
          "location": {
            "parents": 1,
            "interior": {
              "X3": [
                {
                  "Parachain": 1000
                },
                {
                  "PalletInstance": 50
                },
                {
                  "GeneralIndex": 1984
                }
              ]
            }
          },
          "existentialDeposit": "10000",
          "isFeeAsset": true
        }
      }
    }
  ]
}

Example after:

json
{
  "origin": {
    "weight": {
      "refTime": "30975850052",
      "proofSize": "7590"
    },
    "fee": "82694150037",
    "feeType": "dryRun",
    "sufficient": false,
    "asset": {
      "symbol": "BNC",
      "decimals": 12,
      "existentialDeposit": "10000000000",
      "location": {
        "parents": 1,
        "interior": {
          "X2": [
            {
              "Parachain": 2030
            },
            {
              "GeneralKey": {
                "length": 2,
                "data": "0x0001000000000000000000000000000000000000000000000000000000000000"
              }
            }
          ]
        }
      },
      "isNative": true,
      "isFeeAsset": true
    }
  },
  "destination": {
    "fee": "393",
    "feeType": "dryRun",
    "asset": {
      "assetId": "10",
      "symbol": "USDT",
      "decimals": 6,
      "existentialDeposit": "10000",
      "location": {
        "parents": 1,
        "interior": {
          "X3": [
            {
              "Parachain": 1000
            },
            {
              "PalletInstance": 50
            },
            {
              "GeneralIndex": 1984
            }
          ]
        }
      },
      "isFeeAsset": true,
      "alias": "USDT1"
    }
  },
  "hops": [
    {
      "chain": "AssetHubPolkadot",
      "result": {
        "fee": "79429",
        "feeType": "dryRun",
        "asset": {
          "assetId": "1984",
          "symbol": "USDt",
          "decimals": 6,
          "location": {
            "parents": 1,
            "interior": {
              "X3": [
                {
                  "Parachain": 1000
                },
                {
                  "PalletInstance": 50
                },
                {
                  "GeneralIndex": 1984
                }
              ]
            }
          },
          "existentialDeposit": "10000",
          "isFeeAsset": true
        }
      }
    }
  ]
}

Action Required:

Remove all references to assetHub and bridgeHub objects in XCM Fee queries or Dry-run queries.

Adapt code to retrieve hop fees and assets exclusively from hops[] array.

4. Rename of Balance query and removal of Native/Foreign balance sub-queries (Issue 1477)

The SDK and API previously exposed native and foreign balance sub-queries.
These were already undocumented, and in v12 they have been fully removed. The unified getBalance query handles both cases automatically and is preffered.

Since they have been removed, we thought about making balance query more intuitive in name. It is now renamed from getAssetBalance to getBalance.

Additionally, the currency parameter in balance query is now optional.
If omitted, the query defaults to the selection of chain’s native asset.

diff
- getAssetBalance()
+ getBalance()

XCM API Endpoint Change

The XCM API balance query endpoint has also been updated for consistency with the renamed balance query:

diff
- POST /v4/balance/:chain/asset
+ POST /v5/balance/:chain

Action Required:

  • Replace all usage of getNativeAssetBalance or getForeignAssetBalance queries to getBalance query.
  • Rename getAssetBalance into getBalance.
  • If you previously specified a currency only to retrieve the native asset balance (Of the specified chain), you can now omit it.
  • Please update any API integrations to use the new /v5/balance/:chain endpoint.

Summary

Balance lookups are now simplified.
The SDK determines whether native or foreign resolution is required and defaults to the chain’s native asset when no currency is specified.


5. Migration to ESM-Only Modules (Issue 1499)

The SDKs and Router has migrated to ESM-only.
This aligns with modern JavaScript standards and is required to upgrade dependencies that are already ESM-only, including:

  • @noble/curves v2
  • @noble/hashes v2

Classic CommonJS (require(...)) is no longer supported by the SDKs or Router.

We have added support for CommonJS for XCM Analyser (As that package is mostly vanilla).

Action Required:

  • Update your environment to support ESM (Node.js ≥ 16+, "type": "module" in package.json, or use .mjs).
  • Replace require() imports with import syntax.
  • If bundling, ensure your bundler is configured for ESM packages.
  • If your project uses CommonJS and had to adapt to ESM because of XCM Analyser library, you can convert back to CommonJS as Analyser now supports CommonJS also.

6. Deprecation of payment info XCM fee estimators (Issue 1166)

The following fee estimation functions are now deprecated:

  • getXcmFeeEstimate
  • getOriginXcmFeeEstimate

These functions relied solely on payment info, which is often inaccurate for XCM.
Available alternatives:

  • getXcmFee
  • getOriginXcmFee

These use dry-run fee estimation when available and fall back to payment info only when necessary.

Action Required:

  • Replace all deprecated function calls with listed alternatives.

7. Asset registry overhaul and unification (Issue 1361)

Previously, our asset registry divided assets under each chain in two separate arrays:

  • nativeAssets
  • otherAssets

This structure was difficult to maintain and unnecessary.

In v12, asset definitions are unified under a single assets array, where native assets are identified by isNative: true flag. This allowed us to significantly improve code quality, save package space and optimalize certain types and functions that needed to pick between two arrays.

Example Before

json
"Polkadot": {
  "relaychainSymbol": "DOT",
  "nativeAssetSymbol": "DOT",
  "isEVM": false,
  "ss58Prefix": 0,
  "supportsDryRunApi": true,
  "supportsXcmPaymentApi": true,
  "nativeAssets": [
    {
      "symbol": "DOT",
      "isNative": true,
      "decimals": 10,
      "existentialDeposit": "10000000000",
      "location": {
        "parents": 1,
        "interior": { "Here": null }
      }
    }
  ],
  "otherAssets": []
}

Example after:

json
"Polkadot": {
  "relaychainSymbol": "DOT",
  "nativeAssetSymbol": "DOT",
  "isEVM": false,
  "ss58Prefix": 0,
  "supportsDryRunApi": true,
  "supportsXcmPaymentApi": true,
  "assets": [
    {
      "symbol": "DOT",
      "isNative": true,
      "decimals": 10,
      "existentialDeposit": "10000000000",
      "location": {
        "parents": 1,
        "interior": { "Here": null }
      }
    }
  ]
}

Action Required:

Update any code referencing nativeAssets or otherAssets from SDKs or Asset package.

Use assets array exclusively and filter by isNative if you search for native asset within the chain.

8. Error model refactor and expansion (Issue 1338)

Several broad or ambiguous errors were removed, and more specific and descriptive error classes have been added.

diff
-`ChainNotSupportedError`
-`IncompatibleChainsError`
-`InvalidParameterError`
+ `BatchValidationError`
+ `FeatureTemporarilyDisabledError`
+ `MissingParameterError`
+ `NumberFormatError`
+ `OverrideConflictError`
+ `ProviderUnavailableError`
+ `RoutingResolutionError`
+ `RuntimeApiUnavailableError`
+ `UnsupportedOperationError`

The new error model provides more accurate diagnostics and allows integrators to distinguish between configuration problems, runtime API unavailability, routing failures, invalid user inputs, and provider outages.

Full definitions for all errors can be found here.

Action Required:

  • Update error-handling logic to catch and react to the new error classes.
  • Remove or refactor checks relying on the deprecated generic errors.

9. Mythos asset selection restricted to Substrate location only

Prior to v12, Mythos transfers to Ethereum could be selected using either:

  1. Its native parachain location, or
  2. An Ethereum MYTH location, enabled by a temporary compatibility block in the SDK:
ts
// Temporary condition for Mythos to allow selecting by Ethereum MYTH location
// Will be removed in v12
if (chain === 'Mythos') {
  const mythEthAsset = getOtherAssets('Ethereum').find(a => a.symbol === 'MYTH')
  if (mythEthAsset && findAssetInfoByLoc([mythEthAsset], currency.location))
    asset = nativeAssets[0]
}

This change was done to optimize code and to unify selection of MYTH.

New Requirement

Mythos must now be selected exclusively through its native location:

json
{
  "parents": 1,
  "interior": {
    "X1": [
      { "Parachain": 3369 }
    ]
  }
}

No Longer Accepted

Ethereum GlobalConsensus representation of MYTH can no longer be used to select MYTH.

json
{
  "parents": 2,
  "interior": {
    "X2": [
      {
        "GlobalConsensus": {
          "Ethereum": { "chainId": 1 }
        }
      },
      {
        "AccountKey20": {
          "network": null,
          "key": "0xba41ddf06b7ffd89d1267b5a93bfef2424eb2003"
        }
      }
    ]
  }
}

This ensures deterministic asset selection and minimizes possible future bugs.

Action Required:

  • Update any integration that selected Mythos via Ethereum MYTH location.
  • Ensure all queries and XCM constructions use the provided substrate location only.

v12 Pull request

For a more detailed overview we list v12 pull request.