Skip to content

Commit eaf2e57

Browse files
authored
feat: paraspell migration (#60)
+ Integrate Paraspell for native token XCM transfers (supports both direct and multi-hop channels) + Refactor and fix test cases + Update system prompts
1 parent 4c43f3f commit eaf2e57

File tree

11 files changed

+111
-91
lines changed

11 files changed

+111
-91
lines changed

.papi/descriptors/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
"types": "./dist/index.d.ts",
2020
"sideEffects": false,
2121
"peerDependencies": {
22-
"polkadot-api": ">=1.9.11"
22+
"polkadot-api": ">=1.11.2"
2323
}
2424
}

examples/telegram-bot/src/handlers.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,41 @@ const SYSTEM_PROMPT = `I am a Telegram bot powered by PolkadotAgentKit. I can as
99
- Checking proxies (e.g., "check proxies on westend" or "check proxies")
1010
- Transfer tokens through XCM (e.g., "transfer 1 WND to 5CSox4ZSN4SGLKUG9NYPtfVK9sByXLtxP4hmoF4UgkM4jgDJ from west to westend_asset_hub ")
1111
12-
IMPORTANT: When users mention chain names, I must convert them to the correct parameter values using this mapping:
12+
CHAIN NAME CONVERSION RULES for checking balance and transfer native tokens on specific chain : When users mention chain names in checking balance and transfer native tokens on specific chain , I must convert them to the correct parameter values using this mapping:
1313
1414
| User Input | Real Param (USE THIS IN TOOL CALLS) |
1515
|------------|-------------------------------------|
1616
| Westend | west |
1717
| Westend Asset Hub | west_asset_hub |
1818
| Polkadot | polkadot |
1919
| Kusama | kusama |
20+
| AssetHubWestend | west_asset_hub |
21+
| AssetHubPolkadot | polkadot_asset_hub |
22+
23+
24+
CHAIN NAME CONVERSION RULES for transfer tokens through XCM: When users mention chain names in transfer tokens through XCM, I must convert them to the correct parameter values using this mapping:
25+
26+
| User Input | Real Param (USE THIS IN TOOL CALLS) |
27+
|------------|-------------------------------------|
28+
| dot | polkadot |
29+
| asset hub | polkadot_asset_hub |
30+
| polkadot | polkadot |
31+
| Polkadot | polkadot |
32+
| AssetHubPolkadot | polkadot_asset_hub |
33+
| Polkadot Asset Hub | polkadot_asset_hub |
2034
21-
CHAIN NAME CONVERSION RULES:
22-
- Always use the "Real Param" values when calling tools
23-
- "Westend" → "west"
24-
- "Westend Asset Hub" → "west_asset_hub"
25-
- "Polkadot" → "polkadot"
26-
- "Kusama" → "kusama"
2735
2836
For XCM transfers, when the user says:
2937
"transfer X WND to [address] from [source_chain_user_input] to [dest_chain_user_input]"
3038
3139
I must:
32-
1. Convert source chain user input to real param (e.g., "Westend" → "west")
33-
2. Convert destination chain user input to real param (e.g., "Westend Asset Hub" → "west_asset_hub")
40+
1. Convert source chain user input to real param (e.g., "dot" → "polkadot")
41+
2. Convert destination chain user input to real param (e.g., "asset hub" → "polkadot_asset_hub")
3442
3. Use these converted values in the tool call parameters
3543
3644
Example:
37-
User: "transfer 0.1 WND to 5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr from Westend to Westend Asset Hub"
38-
Tool call should use: sourceChain: "west", destChain: "west_asset_hub"
45+
User: "transfer 0.1 WND to 5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr from dot/polkadot/DOT/Polkadot to asset hub/polkadot_asset_hub/Polkadot Asset Hub"
46+
Tool call should use: sourceChain: "polkadot", destChain: "polkadot_asset_hub"
3947
4048
When transferring tokens, please provide:
4149
1. The amount of tokens to transfer (e.g., 1)
@@ -91,7 +99,6 @@ export function setupHandlers(
9199
return;
92100
}
93101
const response = JSON.parse(toolMessage.content || "{}");
94-
console.log("response", response);
95102
if (response.error) {
96103
await ctx.reply(`Error: ${response.message}`);
97104
} else {

packages/common/src/chains/supported-chains.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const polkadotChain = createChain({
2424

2525
export const polkadotAssetHubChain = createChain({
2626
id: "polkadot_asset_hub",
27-
name: "Polkadot Asset Hub",
27+
name: "AssetHubPolkadot",
2828
specName: "asset-hub-polkadot",
2929
wsUrls: ["wss://polkadot-asset-hub-rpc.polkadot.io/"],
3030
relay: "polkadot",
@@ -53,7 +53,7 @@ export const westendChain = createChain({
5353

5454
export const westendAssetHubChain = createChain({
5555
id: "west_asset_hub",
56-
name: "Westend Asset Hub",
56+
name: "AssetHubWestend",
5757
specName: "asset-hub-westend",
5858
wsUrls: ["wss://westend-asset-hub-rpc.polkadot.io"],
5959
relay: "west",
Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,51 @@
1-
import type { Api, KnownChainId } from "@polkadot-agent-kit/common"
1+
import type { TDestination, TNodeDotKsmWithRelayChains, TPapiTransaction } from "@paraspell/sdk"
2+
import { Builder } from "@paraspell/sdk"
3+
import type { KnownChainId } from "@polkadot-agent-kit/common"
24
import { getAllSupportedChains, getChainById } from "@polkadot-agent-kit/common"
3-
import type { TxResult } from "@substrate/asset-transfer-api"
4-
import { AssetTransferApi } from "@substrate/asset-transfer-api"
5-
6-
import { constructApiPromiseWithTimeout } from "../../utils"
75

86
/**
9-
* Transfers a native asset to a destination address on a destination chain via xcm
10-
* @param api - The API instance to use for the query
11-
* @param to - The destination address
12-
* @param amount - The amount to transfer
13-
* @param destinationChain - The destination chain (RelayChain or Parachain)
14-
* @returns The transaction result
7+
* Builds an XCM transaction to transfer a native asset from one chain to another.
8+
*
9+
* This function uses the \@paraspell/sdk Builder to construct a cross-chain (XCM) transfer
10+
* of the native token from a source chain to a destination chain. It supports both relay chains
11+
* and parachains, and abstracts away the complexity of XCM message construction.
12+
*
13+
* @param srcChain - The source chain ID (relay chain or parachain) from which the asset is sent
14+
* @param destChain - The destination chain ID (relay chain or parachain) to which the asset is sent
15+
* @param from - The sender's address on the source chain
16+
* @param to - The recipient's address on the destination chain
17+
* @param amount - The amount of the native asset to transfer (as bigint, in base units)
18+
* @returns A Promise resolving to a TPapiTransaction object representing the unsigned XCM transaction
19+
*
20+
* @example
21+
* const tx = await xcmTransferNativeAsset(
22+
* 'polkadot',
23+
* 'hydra',
24+
* 'senderAddress',
25+
* 'recipientAddress',
26+
* 10000000000n
27+
* )
28+
* // tx can then be signed and submitted using the appropriate transaction handler
1529
*/
30+
1631
export const xcmTransferNativeAsset = async (
17-
api: Api<KnownChainId>,
32+
srcChain: KnownChainId,
33+
destChain: KnownChainId,
34+
from: string,
1835
to: string,
19-
amount: bigint,
20-
destinationChain: KnownChainId
21-
): Promise<TxResult<"submittable">> => {
22-
const sourceChain = getChainById(api.chainId, getAllSupportedChains())
23-
const destChain = getChainById(destinationChain, getAllSupportedChains())
24-
const {
25-
api: xcmApi,
26-
specName,
27-
safeXcmVersion
28-
} = await constructApiPromiseWithTimeout(sourceChain.wsUrls)
29-
30-
const assetApi = new AssetTransferApi(xcmApi, specName, safeXcmVersion)
31-
let callInfo: TxResult<"submittable">
32-
try {
33-
callInfo = await assetApi.createTransferTransaction(
34-
destChain.chainId?.toString() || "",
35-
to,
36-
[sourceChain.symbol],
37-
[amount.toString()],
38-
{
39-
format: "submittable",
40-
xcmVersion: safeXcmVersion
41-
}
42-
)
43-
return callInfo
44-
} catch (e) {
45-
throw Error(e as string)
46-
}
36+
amount: bigint
37+
): Promise<TPapiTransaction> => {
38+
const sourceChain = getChainById(srcChain, getAllSupportedChains())
39+
const destinationChain = getChainById(destChain, getAllSupportedChains())
40+
const url = sourceChain.wsUrls
41+
// XCM transfer native tokken
42+
const tx = await Builder(url)
43+
.from(sourceChain.name as TNodeDotKsmWithRelayChains)
44+
.senderAddress(from)
45+
.to(destinationChain.name as TDestination)
46+
.currency({ symbol: sourceChain.symbol, amount: amount })
47+
.address(to)
48+
.build()
4749

48-
return callInfo
50+
return tx
4951
}

packages/llm/src/agent/api.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { KeyringPair } from "@polkadot/keyring/types"
21
import type { PolkadotApi } from "@polkadot-agent-kit/core"
32
import type { PolkadotSigner } from "polkadot-api"
43

@@ -26,7 +25,7 @@ export interface IPolkadotAgentApi {
2625
// * Returns a tool that transfers native tokens to a specific address via xcm
2726
// * @returns A dynamic structured tool that transfers native tokens to the specified address via xcm
2827
// */
29-
xcmTransferNativeTool(signer: KeyringPair): XcmTransferNativeAssetTool
28+
xcmTransferNativeTool(signer: PolkadotSigner, sender: string): XcmTransferNativeAssetTool
3029
}
3130

3231
/**
@@ -51,7 +50,7 @@ export class PolkadotAgentApi implements IPolkadotAgentApi {
5150
return transferNativeTool(this.api.getAllApis(), signer) as TransferTool
5251
}
5352

54-
xcmTransferNativeTool(signer: KeyringPair): XcmTransferNativeAssetTool {
55-
return xcmTransferNativeTool(this.api.getAllApis(), signer) as XcmTransferNativeAssetTool
53+
xcmTransferNativeTool(signer: PolkadotSigner, sender: string): XcmTransferNativeAssetTool {
54+
return xcmTransferNativeTool(signer, sender) as XcmTransferNativeAssetTool
5655
}
5756
}

packages/llm/src/langchain/xcm.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
import { tool } from "@langchain/core/tools"
2-
import type { KeyringPair } from "@polkadot/keyring/types"
3-
import type { Api, KnownChainId } from "@polkadot-agent-kit/common"
2+
import type { KnownChainId } from "@polkadot-agent-kit/common"
43
import { getDecimalsByChainId, parseUnits } from "@polkadot-agent-kit/common"
5-
import { submitXcmTxWithKeypair, xcmTransferNativeAsset } from "@polkadot-agent-kit/core"
4+
import { submitTxWithPolkadotSigner, xcmTransferNativeAsset } from "@polkadot-agent-kit/core"
5+
import type { PolkadotSigner } from "polkadot-api/signer"
66
import type { z } from "zod"
77

88
import type { xcmTransferNativeAssetSchema, XcmTransferNativeAssetToolResult } from "../types"
99
import { ToolNames } from "../types/common"
1010
import { toolConfigXcmTransferNativeAsset } from "../types/xcm"
11-
import { executeTool, getApiForChain, validateAndFormatAddress } from "../utils"
11+
import { executeTool, validateAndFormatAddress } from "../utils"
1212

1313
/**
1414
* Returns a tool that transfers native tokens to a specific address to a destination chain via xcm
1515
* @param api - The API instance to use for the transfer
1616
* @returns A dynamic structured tool that transfers native tokens to the specified address to a destination chain via xcm
1717
*/
18-
export const xcmTransferNativeTool = (
19-
apis: Map<KnownChainId, Api<KnownChainId>>,
20-
signer: KeyringPair
21-
) => {
18+
export const xcmTransferNativeTool = (signer: PolkadotSigner, sender: string) => {
2219
return tool(
2320
async ({
2421
amount,
@@ -29,16 +26,16 @@ export const xcmTransferNativeTool = (
2926
return executeTool<XcmTransferNativeAssetToolResult>(
3027
ToolNames.XCM_TRANSFER_NATIVE_ASSET,
3128
async () => {
32-
const api = getApiForChain(apis, sourceChain)
3329
const formattedAddress = validateAndFormatAddress(to, sourceChain as KnownChainId)
3430
const parsedAmount = parseUnits(amount, getDecimalsByChainId(sourceChain))
35-
const xcmSubmittable = await xcmTransferNativeAsset(
36-
api,
31+
const xcmTx = await xcmTransferNativeAsset(
32+
sourceChain as KnownChainId,
33+
destChain as KnownChainId,
34+
sender,
3735
formattedAddress,
38-
parsedAmount,
39-
destChain as KnownChainId
36+
parsedAmount
4037
)
41-
const tx = await submitXcmTxWithKeypair(xcmSubmittable, signer)
38+
const tx = await submitTxWithPolkadotSigner(xcmTx, signer)
4239
if (tx.success) {
4340
return {
4441
success: tx.success,

packages/sdk/src/api.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ describe("PolkadotApi", () => {
284284

285285
let mockXcmTool: any
286286
let mockAgentPolkadotApi: PolkadotAgentApi
287-
const mockKeyringPair = { sign: vi.fn() } as any
287+
const mockSigner = { sign: vi.fn() } as any
288288

289289
beforeEach(() => {
290290
mockXcmTool = createMockXcmTool("xcmTransferNative", "XCM transfer native tokens")
@@ -295,7 +295,10 @@ describe("PolkadotApi", () => {
295295
})
296296

297297
it("should return the XCM transfer tool and call it with correct params", async () => {
298-
const tool = mockAgentPolkadotApi.xcmTransferNativeTool(mockKeyringPair)
298+
const tool = mockAgentPolkadotApi.xcmTransferNativeTool(
299+
mockSigner,
300+
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
301+
)
299302
expect(tool).toBeDefined()
300303
expect(tool).toBe(mockXcmTool)
301304
})

packages/sdk/src/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class PolkadotAgentKit implements IPolkadotApi, IPolkadotAgentApi {
103103
}
104104

105105
xcmTransferNativeTool(): XcmTransferNativeAssetTool {
106-
return this.agentApi.xcmTransferNativeTool(this.getKeyringPair())
106+
return this.agentApi.xcmTransferNativeTool(this.getSigner(), this.getCurrentAddress())
107107
}
108108
/**
109109
* Get Address

packages/sdk/tests/integration-tests/utils.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,47 @@ export const RECIPIENT = '5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr';
44
// Test purpose only - DOnt use in production
55
export const AGENT_PRIVATE_KEY = '0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a';
66

7-
export const SYSTEM_PROMPT = `I am a Telegram bot powered by PolkadotAgentKit. I can assist you with:
7+
const SYSTEM_PROMPT = `I am a Telegram bot powered by PolkadotAgentKit. I can assist you with:
88
- Transferring native tokens on specific chain (e.g., "transfer 1 WND to 5CSox4ZSN4SGLKUG9NYPtfVK9sByXLtxP4hmoF4UgkM4jgDJ on westend_asset_hub")
99
- Checking WND balance on Westend (e.g., "check balance")
1010
- Checking proxies (e.g., "check proxies on westend" or "check proxies")
1111
- Transfer tokens through XCM (e.g., "transfer 1 WND to 5CSox4ZSN4SGLKUG9NYPtfVK9sByXLtxP4hmoF4UgkM4jgDJ from west to westend_asset_hub ")
1212
13-
IMPORTANT: When users mention chain names, I must convert them to the correct parameter values using this mapping:
13+
CHAIN NAME CONVERSION RULES for checking balance and transfer native tokens on specific chain : When users mention chain names in checking balance and transfer native tokens on specific chain , I must convert them to the correct parameter values using this mapping:
1414
1515
| User Input | Real Param (USE THIS IN TOOL CALLS) |
1616
|------------|-------------------------------------|
1717
| Westend | west |
1818
| Westend Asset Hub | west_asset_hub |
1919
| Polkadot | polkadot |
2020
| Kusama | kusama |
21+
| AssetHubWestend | west_asset_hub |
22+
| AssetHubPolkadot | polkadot_asset_hub |
23+
24+
25+
CHAIN NAME CONVERSION RULES for transfer tokens through XCM: When users mention chain names in transfer tokens through XCM, I must convert them to the correct parameter values using this mapping:
26+
27+
| User Input | Real Param (USE THIS IN TOOL CALLS) |
28+
|------------|-------------------------------------|
29+
| dot | polkadot |
30+
| asset hub | polkadot_asset_hub |
31+
| polkadot | polkadot |
32+
| Polkadot | polkadot |
33+
| AssetHubPolkadot | polkadot_asset_hub |
34+
| Polkadot Asset Hub | polkadot_asset_hub |
2135
22-
CHAIN NAME CONVERSION RULES:
23-
- Always use the "Real Param" values when calling tools
24-
- "Westend" → "west"
25-
- "Westend Asset Hub" → "west_asset_hub"
26-
- "Polkadot" → "polkadot"
27-
- "Kusama" → "kusama"
2836
2937
For XCM transfers, when the user says:
3038
"transfer X WND to [address] from [source_chain_user_input] to [dest_chain_user_input]"
3139
3240
I must:
33-
1. Convert source chain user input to real param (e.g., "Westend" → "west")
34-
2. Convert destination chain user input to real param (e.g., "Westend Asset Hub" → "west_asset_hub")
41+
1. Convert source chain user input to real param (e.g., "dot" → "polkadot")
42+
2. Convert destination chain user input to real param (e.g., "asset hub" → "polkadot_asset_hub")
3543
3. Use these converted values in the tool call parameters
3644
3745
Example:
38-
User: "transfer 0.1 WND to 5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr from Westend to Westend Asset Hub"
39-
Tool call should use: sourceChain: "west", destChain: "west_asset_hub"
46+
User: "transfer 0.1 WND to 5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr from dot/polkadot/DOT/Polkadot to asset hub/polkadot_asset_hub/Polkadot Asset Hub"
47+
Tool call should use: sourceChain: "polkadot", destChain: "polkadot_asset_hub"
4048
4149
When transferring tokens, please provide:
4250
1. The amount of tokens to transfer (e.g., 1)
@@ -58,3 +66,6 @@ export function sleep(ms: number) {
5866
return new Promise(resolve => setTimeout(resolve, ms));
5967
}
6068

69+
70+
71+

packages/sdk/vitest.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { defineConfig } from 'vitest/config'
22

33
export default defineConfig({
44
test: {
5-
include: ['src/**/*.test.ts'],
5+
include: ['src/*.test.ts'],
66
coverage: {
77
include: ['src/**/*.ts'],
88
exclude: ['src/**/*.test.ts']
99
}
1010
}
1111
})
12+

0 commit comments

Comments
 (0)