Skip to content

Migration Guide v13 ➑ v14 ​

This guide covers all breaking changes introduced in version v14 of the ParaSpell XCM Tools. These changes affect XCM SDK and XCM API.

Overview of Breaking Changes ​

v14 is primarily a cleanup release, removing functionality that was marked as deprecated during v12/v13, plus a unification of the dry-run/XCM fee error shape.

v14 introduces the following major changes:

  1. Removal of deprecated asset claim functionality from builder (claimFrom).
  2. Removal of deprecated Override location specifier and raw multi-asset currency input.
  3. Removal of deprecated EvmBuilder.
  4. Removal of deprecated asset query functions (getAssetId, getAssetDecimals, hasSupportForAsset).
  5. Unification of dry-run and XCM fee error fields under a single dryRunError object.
  6. New required peer dependency: @paraspell/descriptors for @paraspell/sdk (PAPI version).

Each topic is detailed below.

1. Removal of deprecated asset claim functionality ​

What changed ​

Asset claim functionality (claiming trapped/unclaimed assets) was marked deprecated in earlier versions and is now fully removed.

Removed from the SDKs:

  • Builder().claimFrom(chain)

Removed from the XCM API:

  • POST /asset-claim endpoint and its module/controller/service

βœ… Action Required:

  • Remove any usage of Builder().claimFrom()
  • Remove any integrations calling the /asset-claim XCM API endpoint.

2. Removal of Override location specifier and raw multi-asset input ​

What changed ​

The Override currency selector (used to force a custom location for an asset) and the ability to pass a raw array of multi-asset objects directly as a currency were both deprecated in favor of the customAssets Builder option, and are now removed. Read more about custom assets here.

Removed:

  • Override(location) selector from @paraspell/assets
diff
- import { Override } from '@paraspell/assets'
-
- const tx = await Builder(api)
-   .from(chain)
-   .to('AssetHubPolkadot')
-   .currency({
-     location: Override({
-       parents: 0,
-       interior: { X2: [{ PalletInstance: 50 }, { GeneralIndex: 9999 }] }
-     }),
-     amount
-   })
-   .recipient(address)
-   .sender(sender)
-   .build()

+ const tx = await Builder({
+   customAssets: {
+     AssetHubPolkadot: [
+       {
+         symbol: 'MYNEWUSD',
+         decimals: '6',
+         assetId: '9999',
+         existentialDeposit: '1000000000',  //Needs to be defined in plancks
+         location: {
+           parents: 0,
+           interior: { X2: [{ PalletInstance: 50 }, { GeneralIndex: 9999 }] }
+         }
+         // forceOverride: true // needed only if this location collides with an existing registry asset
+       }
+     ]
+   }
+ })
+   .from(chain)
+   .to('AssetHubPolkadot')
+   .currency({ symbol: 'MYNEWUSD', amount })
+   .recipient(address)
+   .sender(sender)
+   .build()

If the custom asset's location collides with an asset already in the registry, add forceOverride: true on the custom asset entry to replace it β€” without this flag a colliding location throws a CustomAssetConflictError.

βœ… Action Required:

  • Replace any usage of Override(...) with the customAssets Builder option, selecting the registered asset by a normal currency selector.

3. Removal of deprecated EvmBuilder ​

What changed ​

EvmBuilder, used for constructing Ethereum β†’ Substrate transfers, was deprecated in favor of the unified Builder class and is now fully removed from @paraspell/sdk and @paraspell/sdk-pjs. @paraspell/sdk-dedot never had an EvmBuilder implementation, so it is unaffected.

diff
- EvmBuilder(provider, api)
-   .from('Ethereum')
-   .to(chain)
-   .currency(currency)
-   .recipient(address)
-   .signer(signer)
-   .build()

+ Builder(api)
+   .from('Ethereum')
+   .to(chain)
+   .currency(currency)
+   .recipient(address)
+   .sender(signer)
+   .build()

βœ… Action Required:

  • Replace all usages of EvmBuilder with the standard Builder class.
  • Make sure to setup EVM or EVM-Snowbridge extension to enable this feature.

4. Removal of deprecated asset query functions ​

What changed ​

The following functions, deprecated in v13 in favor of findAssetInfo, are now removed:

diff
- getAssetId(chain, symbol)
- getAssetDecimals(chain, symbol)
- hasSupportForAsset(chain, symbol)
+ // Returns assetInfo, null if asset not found
+const assetInfo = findAssetInfo('Hydration', { currency: { symbol: 'DOT' }})

+if(!assetInfo) throw new Error('Asset not found')

+console.log(assetInfo.assetId, assetInfo.decimals)

The corresponding XCM API endpoints have also been removed, in favor of the existing POST /assets/:chain/asset-info endpoint:

diff
- GET /assets/:chain/id?symbol=...
- GET /assets/:chain/decimals?symbol=...
- GET /assets/:chain/has-support?symbol=...

+ POST /assets/:chain/asset-info
+ // body: { "currency": { "symbol": "DOT" } }
+ // Returns assetInfo, null if asset not found

βœ… Action Required:

  • Replace calls to getAssetId, getAssetDecimals, and hasSupportForAsset with findAssetInfo, reading .assetId, .decimals, or checking for a truthy result respectively.
  • Update any integration relying on the removed XCM API endpoints to call POST /assets/:chain/asset-info instead, reading .assetId / .decimals off the returned asset (or checking it's non-null for hasSupportForAsset).

5. Unification of dry-run and XCM fee error fields under dryRunError ​

What changed ​

Previously, dry-run/XCM fee failure details were spread across several flat, inconsistently-named fields (failureReason, failureSubReason, failureIndex, failureInstruction on dry-run results; dryRunError, dryRunSubError, dryRunErrorIndex, dryRunErrorInstruction on XCM fee results). These are now unified into a single nested dryRunError object with consistent field names, on both dry-run and XCM fee results.

ts
type TDryRunError = {
  reason: string
  subReason?: string
  chain: TChain
  instructionIndex?: number
  instruction?: object
}
Dry-run result β€” Example Before
json
{
  "failureChain": "destination",
  "failureReason": "TooExpensive",
  "failureIndex": 2,
  "failureInstruction": {
    "type": "BuyExecution",
    "value": {
      "fees": {
        "id": {
          "parents": 1,
          "interior": {
            "type": "X1",
            "value": {
              "type": "Parachain",
              "value": 3388
            }
          }
        },
        "fun": {
          "type": "Fungible",
          "value": "10000000000"
        }
      },
      "weight_limit": {
        "type": "Unlimited"
      }
    }
  },
  "origin": {
    "success": true,
    "fee": "337011",
    "asset": {
      "symbol": "XRT",
      "isNative": true,
      "decimals": 9,
      "existentialDeposit": "1000",
      "location": {
        "parents": 1,
        "interior": {
          "X1": [
            {
              "Parachain": 3388
            }
          ]
        }
      }
    },
    "weight": {
      "refTime": "985652555",
      "proofSize": "10914"
    },
    "forwardedXcms": [
      {
        "type": "V5",
        "value": {
          "parents": 1,
          "interior": {
            "type": "X1",
            "value": {
              "type": "Parachain",
              "value": 1000
            }
          }
        }
      },
      [
        {
          "type": "V5",
          "value": [
            {
              "type": "ReceiveTeleportedAsset",
              "value": [
                {
                  "id": {
                    "parents": 1,
                    "interior": {
                      "type": "X1",
                      "value": {
                        "type": "Parachain",
                        "value": 3388
                      }
                    }
                  },
                  "fun": {
                    "type": "Fungible",
                    "value": "10000000000"
                  }
                }
              ]
            },
            {
              "type": "ClearOrigin"
            },
            {
              "type": "BuyExecution",
              "value": {
                "fees": {
                  "id": {
                    "parents": 1,
                    "interior": {
                      "type": "X1",
                      "value": {
                        "type": "Parachain",
                        "value": 3388
                      }
                    }
                  },
                  "fun": {
                    "type": "Fungible",
                    "value": "10000000000"
                  }
                },
                "weight_limit": {
                  "type": "Unlimited"
                }
              }
            },
            {
              "type": "DepositAsset",
              "value": {
                "assets": {
                  "type": "Wild",
                  "value": {
                    "type": "AllCounted",
                    "value": 1
                  }
                },
                "beneficiary": {
                  "parents": 0,
                  "interior": {
                    "type": "X1",
                    "value": {
                      "type": "AccountId32",
                      "value": {
                        "id": "0x920ff0c3caf1cf146c9561ce7cb35dcb3be505511070788a47f028f93cb12700"
                      }
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    ],
    "destParaId": 1000
  },
  "destination": {
    "success": false,
    "failureReason": "TooExpensive",
    "failureIndex": 2,
    "failureInstruction": {
      "type": "BuyExecution",
      "value": {
        "fees": {
          "id": {
            "parents": 1,
            "interior": {
              "type": "X1",
              "value": {
                "type": "Parachain",
                "value": 3388
              }
            }
          },
          "fun": {
            "type": "Fungible",
            "value": "10000000000"
          }
        },
        "weight_limit": {
          "type": "Unlimited"
        }
      }
    },
    "asset": {
      "symbol": "XRT",
      "decimals": 9,
      "location": {
        "parents": 1,
        "interior": {
          "X1": [
            {
              "Parachain": 3388
            }
          ]
        }
      },
      "existentialDeposit": "1000",
      "alias": "XRT1"
    }
  },
  "hops": []
}
Dry-run result β€” Example After
json
{
  "success": false,
  "dryRunError": {
    "chainKind": "destination",
    "chain": "AssetHubPolkadot",
    "reason": "TooExpensive",
    "instructionIndex": 2,
    "instruction": {
      "type": "BuyExecution",
      "value": {
        "fees": {
          "id": {
            "parents": 1,
            "interior": {
              "type": "X1",
              "value": {
                "type": "Parachain",
                "value": 3388
              }
            }
          },
          "fun": {
            "type": "Fungible",
            "value": "100000000000"
          }
        },
        "weight_limit": {
          "type": "Unlimited"
        }
      }
    }
  },
  "origin": {
    "success": true,
    "fee": "337011",
    "asset": {
      "symbol": "XRT",
      "isNative": true,
      "decimals": 9,
      "existentialDeposit": "1000",
      "location": {
        "parents": 1,
        "interior": {
          "X1": [
            {
              "Parachain": 3388
            }
          ]
        }
      }
    },
    "weight": {
      "refTime": "985652555",
      "proofSize": "10914"
    },
    "forwardedXcms": [
      {
        "type": "V5",
        "value": {
          "parents": 1,
          "interior": {
            "type": "X1",
            "value": {
              "type": "Parachain",
              "value": 1000
            }
          }
        }
      },
      [
        {
          "type": "V5",
          "value": [
            {
              "type": "ReceiveTeleportedAsset",
              "value": [
                {
                  "id": {
                    "parents": 1,
                    "interior": {
                      "type": "X1",
                      "value": {
                        "type": "Parachain",
                        "value": 3388
                      }
                    }
                  },
                  "fun": {
                    "type": "Fungible",
                    "value": "100000000000"
                  }
                }
              ]
            },
            {
              "type": "ClearOrigin"
            },
            {
              "type": "BuyExecution",
              "value": {
                "fees": {
                  "id": {
                    "parents": 1,
                    "interior": {
                      "type": "X1",
                      "value": {
                        "type": "Parachain",
                        "value": 3388
                      }
                    }
                  },
                  "fun": {
                    "type": "Fungible",
                    "value": "100000000000"
                  }
                },
                "weight_limit": {
                  "type": "Unlimited"
                }
              }
            },
            {
              "type": "DepositAsset",
              "value": {
                "assets": {
                  "type": "Wild",
                  "value": {
                    "type": "AllCounted",
                    "value": 1
                  }
                },
                "beneficiary": {
                  "parents": 0,
                  "interior": {
                    "type": "X1",
                    "value": {
                      "type": "AccountId32",
                      "value": {
                        "id": "0x920ff0c3caf1cf146c9561ce7cb35dcb3be505511070788a47f028f93cb12700"
                      }
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    ],
    "destParaId": 1000
  },
  "destination": {
    "success": false,
    "dryRunError": {
      "reason": "TooExpensive",
      "instructionIndex": 2,
      "instruction": {
        "type": "BuyExecution",
        "value": {
          "fees": {
            "id": {
              "parents": 1,
              "interior": {
                "type": "X1",
                "value": {
                  "type": "Parachain",
                  "value": 3388
                }
              }
            },
            "fun": {
              "type": "Fungible",
              "value": "100000000000"
            }
          },
          "weight_limit": {
            "type": "Unlimited"
          }
        }
      }
    },
    "asset": {
      "symbol": "XRT",
      "decimals": 9,
      "location": {
        "parents": 1,
        "interior": {
          "X1": [
            {
              "Parachain": 3388
            }
          ]
        }
      },
      "existentialDeposit": "1000",
      "alias": "XRT1"
    }
  },
  "hops": []
}
XCM fee result β€” Example Before
json
{
  "origin": {
    "weight": {
      "refTime": "985652555",
      "proofSize": "10914"
    },
    "fee": "337011",
    "feeType": "dryRun",
    "sufficient": false,
    "asset": {
      "symbol": "XRT",
      "isNative": true,
      "decimals": 9,
      "existentialDeposit": "1000",
      "location": {
        "parents": 1,
        "interior": {
          "X1": [
            {
              "Parachain": 3388
            }
          ]
        }
      }
    }
  },
  "destination": {
    "fee": "13336809",
    "feeType": "paymentInfo",
    "asset": {
      "symbol": "XRT",
      "decimals": 9,
      "location": {
        "parents": 1,
        "interior": {
          "X1": [
            {
              "Parachain": 3388
            }
          ]
        }
      },
      "existentialDeposit": "1000",
      "alias": "XRT1",
      "amount": "10000000000"
    },
    "dryRunError": "TooExpensive",
    "dryRunErrorIndex": 2,
    "dryRunErrorInstruction": {
      "type": "BuyExecution",
      "value": {
        "fees": {
          "id": {
            "parents": 1,
            "interior": {
              "type": "X1",
              "value": {
                "type": "Parachain",
                "value": 3388
              }
            }
          },
          "fun": {
            "type": "Fungible",
            "value": "510000000000"
          }
        },
        "weight_limit": {
          "type": "Unlimited"
        }
      }
    }
  },
  "hops": [],
  "failureChain": "destination",
  "failureReason": "TooExpensive",
  "failureIndex": 2,
  "failureInstruction": {
    "type": "BuyExecution",
    "value": {
      "fees": {
        "id": {
          "parents": 1,
          "interior": {
            "type": "X1",
            "value": {
              "type": "Parachain",
              "value": 3388
            }
          }
        },
        "fun": {
          "type": "Fungible",
          "value": "510000000000"
        }
      },
      "weight_limit": {
        "type": "Unlimited"
      }
    }
  }
}
XCM fee result β€” Example After
json
{
  "success": false,
  "origin": {
    "weight": {
      "refTime": "985652555",
      "proofSize": "10914"
    },
    "fee": "337011",
    "feeType": "dryRun",
    "sufficient": false,
    "asset": {
      "symbol": "XRT",
      "isNative": true,
      "decimals": 9,
      "existentialDeposit": "1000",
      "location": {
        "parents": 1,
        "interior": {
          "X1": [
            {
              "Parachain": 3388
            }
          ]
        }
      }
    }
  },
  "destination": {
    "fee": "13336809",
    "feeType": "paymentInfo",
    "asset": {
      "symbol": "XRT",
      "decimals": 9,
      "location": {
        "parents": 1,
        "interior": {
          "X1": [
            {
              "Parachain": 3388
            }
          ]
        }
      },
      "existentialDeposit": "1000",
      "alias": "XRT1",
      "amount": "100000000000"
    },
    "dryRunError": {
      "reason": "TooExpensive",
      "instructionIndex": 2,
      "instruction": {
        "type": "BuyExecution",
        "value": {
          "fees": {
            "id": {
              "parents": 1,
              "interior": {
                "type": "X1",
                "value": {
                  "type": "Parachain",
                  "value": 3388
                }
              }
            },
            "fun": {
              "type": "Fungible",
              "value": "600000000000"
            }
          },
          "weight_limit": {
            "type": "Unlimited"
          }
        }
      }
    }
  },
  "hops": [],
  "dryRunError": {
    "chainKind": "destination",
    "chain": "AssetHubPolkadot",
    "reason": "TooExpensive",
    "instructionIndex": 2,
    "instruction": {
      "type": "BuyExecution",
      "value": {
        "fees": {
          "id": {
            "parents": 1,
            "interior": {
              "type": "X1",
              "value": {
                "type": "Parachain",
                "value": 3388
              }
            }
          },
          "fun": {
            "type": "Fungible",
            "value": "600000000000"
          }
        },
        "weight_limit": {
          "type": "Unlimited"
        }
      }
    }
  }
}

βœ… Action Required:

  • Replace usages of the flat failureReason / failureSubReason / failureIndex / failureInstruction fields with the nested dryRunError.reason / dryRunError.subReason / dryRunError.instructionIndex / dryRunError.instruction.
  • Replace usages of the flat dryRunError / dryRunSubError / dryRunErrorIndex / dryRunErrorInstruction string fields on XCM fee results with the nested dryRunError object.

6. New required peer dependency: @paraspell/descriptors ​

What changed ​

@paraspell/sdk (PAPI-based SDK) now uses typed, generated API descriptors instead of the unsafe API. This is powered by a new package, @paraspell/descriptors, which is now a peer dependency of @paraspell/sdk.

diff
  "dependencies": {
    "@paraspell/sdk": "^14.0.0",
+   "@paraspell/descriptors": "^14.0.0",
    "polkadot-api": "2.1.7"
  }

βœ… Action Required:

  • Install @paraspell/descriptors alongside @paraspell/sdk.

v14 Pull request ​

For a more detailed overview we list v14 pull request.