Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .papi/descriptors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
"types": "./dist/index.d.ts",
"sideEffects": false,
"peerDependencies": {
"polkadot-api": ">=1.9.11"
"polkadot-api": ">=1.11.2"
}
}
31 changes: 19 additions & 12 deletions examples/telegram-bot/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,41 @@ const SYSTEM_PROMPT = `I am a Telegram bot powered by PolkadotAgentKit. I can as
- Checking proxies (e.g., "check proxies on westend" or "check proxies")
- Transfer tokens through XCM (e.g., "transfer 1 WND to 5CSox4ZSN4SGLKUG9NYPtfVK9sByXLtxP4hmoF4UgkM4jgDJ from west to westend_asset_hub ")

IMPORTANT: When users mention chain names, I must convert them to the correct parameter values using this mapping:
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:

| User Input | Real Param (USE THIS IN TOOL CALLS) |
|------------|-------------------------------------|
| Westend | west |
| Westend Asset Hub | west_asset_hub |
| Polkadot | polkadot |
| Kusama | kusama |
| AssetHubWestend | west_asset_hub |
| AssetHubPolkadot | polkadot_asset_hub |


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:

| User Input | Real Param (USE THIS IN TOOL CALLS) |
|------------|-------------------------------------|
| dot | polkadot |
| asset hub | polkadot_asset_hub |
| polkadot | polkadot |
| Polkadot | polkadot |
| AssetHubPolkadot | polkadot_asset_hub |
| Polkadot Asset Hub | polkadot_asset_hub |

CHAIN NAME CONVERSION RULES:
- Always use the "Real Param" values when calling tools
- "Westend" → "west"
- "Westend Asset Hub" → "west_asset_hub"
- "Polkadot" → "polkadot"
- "Kusama" → "kusama"

For XCM transfers, when the user says:
"transfer X WND to [address] from [source_chain_user_input] to [dest_chain_user_input]"

I must:
1. Convert source chain user input to real param (e.g., "Westend" → "west")
2. Convert destination chain user input to real param (e.g., "Westend Asset Hub" → "west_asset_hub")
1. Convert source chain user input to real param (e.g., "dot" → "polkadot")
2. Convert destination chain user input to real param (e.g., "asset hub" → "polkadot_asset_hub")
3. Use these converted values in the tool call parameters

Example:
User: "transfer 0.1 WND to 5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr from Westend to Westend Asset Hub"
Tool call should use: sourceChain: "west", destChain: "west_asset_hub"
User: "transfer 0.1 WND to 5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr from dot/polkadot/DOT/Polkadot to asset hub/polkadot_asset_hub/Polkadot Asset Hub"
Tool call should use: sourceChain: "polkadot", destChain: "polkadot_asset_hub"

When transferring tokens, please provide:
1. The amount of tokens to transfer (e.g., 1)
Expand Down Expand Up @@ -91,7 +99,6 @@ export function setupHandlers(
return;
}
const response = JSON.parse(toolMessage.content || "{}");
console.log("response", response);
if (response.error) {
await ctx.reply(`Error: ${response.message}`);
} else {
Expand Down
4 changes: 2 additions & 2 deletions packages/common/src/chains/supported-chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const polkadotChain = createChain({

export const polkadotAssetHubChain = createChain({
id: "polkadot_asset_hub",
name: "Polkadot Asset Hub",
name: "AssetHubPolkadot",
specName: "asset-hub-polkadot",
wsUrls: ["wss://polkadot-asset-hub-rpc.polkadot.io/"],
relay: "polkadot",
Expand Down Expand Up @@ -53,7 +53,7 @@ export const westendChain = createChain({

export const westendAssetHubChain = createChain({
id: "west_asset_hub",
name: "Westend Asset Hub",
name: "AssetHubWestend",
specName: "asset-hub-westend",
wsUrls: ["wss://westend-asset-hub-rpc.polkadot.io"],
relay: "west",
Expand Down
84 changes: 43 additions & 41 deletions packages/core/src/pallets/xcm/nativeAsset.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,51 @@
import type { Api, KnownChainId } from "@polkadot-agent-kit/common"
import type { TDestination, TNodeDotKsmWithRelayChains, TPapiTransaction } from "@paraspell/sdk"
import { Builder } from "@paraspell/sdk"
import type { KnownChainId } from "@polkadot-agent-kit/common"
import { getAllSupportedChains, getChainById } from "@polkadot-agent-kit/common"
import type { TxResult } from "@substrate/asset-transfer-api"
import { AssetTransferApi } from "@substrate/asset-transfer-api"

import { constructApiPromiseWithTimeout } from "../../utils"

/**
* Transfers a native asset to a destination address on a destination chain via xcm
* @param api - The API instance to use for the query
* @param to - The destination address
* @param amount - The amount to transfer
* @param destinationChain - The destination chain (RelayChain or Parachain)
* @returns The transaction result
* Builds an XCM transaction to transfer a native asset from one chain to another.
*
* This function uses the \@paraspell/sdk Builder to construct a cross-chain (XCM) transfer
* of the native token from a source chain to a destination chain. It supports both relay chains
* and parachains, and abstracts away the complexity of XCM message construction.
*
* @param srcChain - The source chain ID (relay chain or parachain) from which the asset is sent
* @param destChain - The destination chain ID (relay chain or parachain) to which the asset is sent
* @param from - The sender's address on the source chain
* @param to - The recipient's address on the destination chain
* @param amount - The amount of the native asset to transfer (as bigint, in base units)
* @returns A Promise resolving to a TPapiTransaction object representing the unsigned XCM transaction
*
* @example
* const tx = await xcmTransferNativeAsset(
* 'polkadot',
* 'hydra',
* 'senderAddress',
* 'recipientAddress',
* 10000000000n
* )
* // tx can then be signed and submitted using the appropriate transaction handler
*/

export const xcmTransferNativeAsset = async (
api: Api<KnownChainId>,
srcChain: KnownChainId,
destChain: KnownChainId,
from: string,
to: string,
amount: bigint,
destinationChain: KnownChainId
): Promise<TxResult<"submittable">> => {
const sourceChain = getChainById(api.chainId, getAllSupportedChains())
const destChain = getChainById(destinationChain, getAllSupportedChains())
const {
api: xcmApi,
specName,
safeXcmVersion
} = await constructApiPromiseWithTimeout(sourceChain.wsUrls)

const assetApi = new AssetTransferApi(xcmApi, specName, safeXcmVersion)
let callInfo: TxResult<"submittable">
try {
callInfo = await assetApi.createTransferTransaction(
destChain.chainId?.toString() || "",
to,
[sourceChain.symbol],
[amount.toString()],
{
format: "submittable",
xcmVersion: safeXcmVersion
}
)
return callInfo
} catch (e) {
throw Error(e as string)
}
amount: bigint
): Promise<TPapiTransaction> => {
const sourceChain = getChainById(srcChain, getAllSupportedChains())
const destinationChain = getChainById(destChain, getAllSupportedChains())
const url = sourceChain.wsUrls
// XCM transfer native tokken
const tx = await Builder(url)
.from(sourceChain.name as TNodeDotKsmWithRelayChains)
.senderAddress(from)
.to(destinationChain.name as TDestination)
.currency({ symbol: sourceChain.symbol, amount: amount })
.address(to)
.build()

return callInfo
return tx
}
7 changes: 3 additions & 4 deletions packages/llm/src/agent/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { KeyringPair } from "@polkadot/keyring/types"
import type { PolkadotApi } from "@polkadot-agent-kit/core"
import type { PolkadotSigner } from "polkadot-api"

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

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

xcmTransferNativeTool(signer: KeyringPair): XcmTransferNativeAssetTool {
return xcmTransferNativeTool(this.api.getAllApis(), signer) as XcmTransferNativeAssetTool
xcmTransferNativeTool(signer: PolkadotSigner, sender: string): XcmTransferNativeAssetTool {
return xcmTransferNativeTool(signer, sender) as XcmTransferNativeAssetTool
}
}
25 changes: 11 additions & 14 deletions packages/llm/src/langchain/xcm.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import { tool } from "@langchain/core/tools"
import type { KeyringPair } from "@polkadot/keyring/types"
import type { Api, KnownChainId } from "@polkadot-agent-kit/common"
import type { KnownChainId } from "@polkadot-agent-kit/common"
import { getDecimalsByChainId, parseUnits } from "@polkadot-agent-kit/common"
import { submitXcmTxWithKeypair, xcmTransferNativeAsset } from "@polkadot-agent-kit/core"
import { submitTxWithPolkadotSigner, xcmTransferNativeAsset } from "@polkadot-agent-kit/core"
import type { PolkadotSigner } from "polkadot-api/signer"
import type { z } from "zod"

import type { xcmTransferNativeAssetSchema, XcmTransferNativeAssetToolResult } from "../types"
import { ToolNames } from "../types/common"
import { toolConfigXcmTransferNativeAsset } from "../types/xcm"
import { executeTool, getApiForChain, validateAndFormatAddress } from "../utils"
import { executeTool, validateAndFormatAddress } from "../utils"

/**
* Returns a tool that transfers native tokens to a specific address to a destination chain via xcm
* @param api - The API instance to use for the transfer
* @returns A dynamic structured tool that transfers native tokens to the specified address to a destination chain via xcm
*/
export const xcmTransferNativeTool = (
apis: Map<KnownChainId, Api<KnownChainId>>,
signer: KeyringPair
) => {
export const xcmTransferNativeTool = (signer: PolkadotSigner, sender: string) => {
return tool(
async ({
amount,
Expand All @@ -29,16 +26,16 @@ export const xcmTransferNativeTool = (
return executeTool<XcmTransferNativeAssetToolResult>(
ToolNames.XCM_TRANSFER_NATIVE_ASSET,
async () => {
const api = getApiForChain(apis, sourceChain)
const formattedAddress = validateAndFormatAddress(to, sourceChain as KnownChainId)
const parsedAmount = parseUnits(amount, getDecimalsByChainId(sourceChain))
const xcmSubmittable = await xcmTransferNativeAsset(
api,
const xcmTx = await xcmTransferNativeAsset(
sourceChain as KnownChainId,
destChain as KnownChainId,
sender,
formattedAddress,
parsedAmount,
destChain as KnownChainId
parsedAmount
)
const tx = await submitXcmTxWithKeypair(xcmSubmittable, signer)
const tx = await submitTxWithPolkadotSigner(xcmTx, signer)
if (tx.success) {
return {
success: tx.success,
Expand Down
7 changes: 5 additions & 2 deletions packages/sdk/src/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ describe("PolkadotApi", () => {

let mockXcmTool: any
let mockAgentPolkadotApi: PolkadotAgentApi
const mockKeyringPair = { sign: vi.fn() } as any
const mockSigner = { sign: vi.fn() } as any

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

it("should return the XCM transfer tool and call it with correct params", async () => {
const tool = mockAgentPolkadotApi.xcmTransferNativeTool(mockKeyringPair)
const tool = mockAgentPolkadotApi.xcmTransferNativeTool(
mockSigner,
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
)
expect(tool).toBeDefined()
expect(tool).toBe(mockXcmTool)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class PolkadotAgentKit implements IPolkadotApi, IPolkadotAgentApi {
}

xcmTransferNativeTool(): XcmTransferNativeAssetTool {
return this.agentApi.xcmTransferNativeTool(this.getKeyringPair())
return this.agentApi.xcmTransferNativeTool(this.getSigner(), this.getCurrentAddress())
}
/**
* Get Address
Expand Down
35 changes: 23 additions & 12 deletions packages/sdk/tests/integration-tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,47 @@ export const RECIPIENT = '5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr';
// Test purpose only - DOnt use in production
export const AGENT_PRIVATE_KEY = '0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a';

export const SYSTEM_PROMPT = `I am a Telegram bot powered by PolkadotAgentKit. I can assist you with:
const SYSTEM_PROMPT = `I am a Telegram bot powered by PolkadotAgentKit. I can assist you with:
- Transferring native tokens on specific chain (e.g., "transfer 1 WND to 5CSox4ZSN4SGLKUG9NYPtfVK9sByXLtxP4hmoF4UgkM4jgDJ on westend_asset_hub")
- Checking WND balance on Westend (e.g., "check balance")
- Checking proxies (e.g., "check proxies on westend" or "check proxies")
- Transfer tokens through XCM (e.g., "transfer 1 WND to 5CSox4ZSN4SGLKUG9NYPtfVK9sByXLtxP4hmoF4UgkM4jgDJ from west to westend_asset_hub ")

IMPORTANT: When users mention chain names, I must convert them to the correct parameter values using this mapping:
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:

| User Input | Real Param (USE THIS IN TOOL CALLS) |
|------------|-------------------------------------|
| Westend | west |
| Westend Asset Hub | west_asset_hub |
| Polkadot | polkadot |
| Kusama | kusama |
| AssetHubWestend | west_asset_hub |
| AssetHubPolkadot | polkadot_asset_hub |


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:

| User Input | Real Param (USE THIS IN TOOL CALLS) |
|------------|-------------------------------------|
| dot | polkadot |
| asset hub | polkadot_asset_hub |
| polkadot | polkadot |
| Polkadot | polkadot |
| AssetHubPolkadot | polkadot_asset_hub |
| Polkadot Asset Hub | polkadot_asset_hub |

CHAIN NAME CONVERSION RULES:
- Always use the "Real Param" values when calling tools
- "Westend" → "west"
- "Westend Asset Hub" → "west_asset_hub"
- "Polkadot" → "polkadot"
- "Kusama" → "kusama"

For XCM transfers, when the user says:
"transfer X WND to [address] from [source_chain_user_input] to [dest_chain_user_input]"

I must:
1. Convert source chain user input to real param (e.g., "Westend" → "west")
2. Convert destination chain user input to real param (e.g., "Westend Asset Hub" → "west_asset_hub")
1. Convert source chain user input to real param (e.g., "dot" → "polkadot")
2. Convert destination chain user input to real param (e.g., "asset hub" → "polkadot_asset_hub")
3. Use these converted values in the tool call parameters

Example:
User: "transfer 0.1 WND to 5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr from Westend to Westend Asset Hub"
Tool call should use: sourceChain: "west", destChain: "west_asset_hub"
User: "transfer 0.1 WND to 5D7jcv6aYbhbYGVY8k65oemM6FVNoyBfoVkuJ5cbFvbefftr from dot/polkadot/DOT/Polkadot to asset hub/polkadot_asset_hub/Polkadot Asset Hub"
Tool call should use: sourceChain: "polkadot", destChain: "polkadot_asset_hub"

When transferring tokens, please provide:
1. The amount of tokens to transfer (e.g., 1)
Expand All @@ -58,3 +66,6 @@ export function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}




3 changes: 2 additions & 1 deletion packages/sdk/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
include: ['src/**/*.test.ts'],
include: ['src/*.test.ts'],
coverage: {
include: ['src/**/*.ts'],
exclude: ['src/**/*.test.ts']
}
}
})

2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.