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 setup
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 we list v14 pull request.
