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:
- Removal of deprecated asset claim functionality from Builder (
claimFrom). - Removal of deprecated
Overridelocation specifier and raw multi-asset currency input. - Removal of deprecated
EvmBuilder. - Removal of deprecated asset query functions (
getAssetId,getAssetDecimals,hasSupportForAsset). - Unification of dry-run and XCM fee error fields under a single
dryRunErrorobject. - New required peer dependency:
@paraspell/descriptorsfor@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-claimendpoint and its module/controller/service
✅ Action Required:
- Remove any usage of
Builder().claimFrom() - Remove any integrations calling the
/asset-claimXCM 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
- 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 thecustomAssetsBuilder 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.
- 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
EvmBuilderwith the standardBuilderclass. - Make sure to set up
EVM or EVM-Snowbridge extensionto 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:
- 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:
- 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, andhasSupportForAssetwithfindAssetInfo, 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-infoinstead, reading.assetId/.decimalsoff the returned asset (or checking it's non-null forhasSupportForAsset).
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.
type TDryRunError = {
reason: string
subReason?: string
chain: TChain
instructionIndex?: number
instruction?: object
}Dry-run result: Example Before
{
"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
{
"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
{
"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
{
"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/failureInstructionfields with the nesteddryRunError.reason/dryRunError.subReason/dryRunError.instructionIndex/dryRunError.instruction. - Replace usages of the flat
dryRunError/dryRunSubError/dryRunErrorIndex/dryRunErrorInstructionstring fields on XCM fee results with the nesteddryRunErrorobject.
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.
"dependencies": {
"@paraspell/sdk": "^14.0.0",
+ "@paraspell/descriptors": "^14.0.0",
"polkadot-api": "2.1.7"
}✅ Action Required:
- Install
@paraspell/descriptorsalongside@paraspell/sdk.
v14 Pull request
For a more detailed overview, see the v14 pull request.
