mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 22:57:55 +00:00
8f2b5c7136
Features: - Add XCMTeleportModal for cross-chain HEZ transfers - Support Asset Hub and People Chain teleports - Add "Fund Fees" button with user-friendly tooltips - Use correct XCM V3 format with teyrchain junction Fixes: - Fix PEZ transfer to use Asset Hub API - Silence unnecessary pallet availability warnings - Fix transaction loading performance (10 blocks limit) - Remove Supabase admin_roles dependency CI/CD: - Add auto-deploy to VPS on main branch push - Add version bumping on deploy - Upload build artifacts for deployment
725 lines
20 KiB
TypeScript
725 lines
20 KiB
TypeScript
/**
|
|
* XCM Configuration Wizard Backend Functions
|
|
*
|
|
* Handles parachain registration, HRMP channels, foreign asset registration,
|
|
* and XCM transfer testing for PezkuwiChain.
|
|
*/
|
|
|
|
import type { ApiPromise } from '@pezkuwi/api';
|
|
import type { InjectedAccountWithMeta } from '@pezkuwi/extension-inject/types';
|
|
|
|
// ========================================
|
|
// TYPES
|
|
// ========================================
|
|
|
|
export type RelayChain = 'westend' | 'rococo' | 'polkadot';
|
|
|
|
export interface ChainArtifacts {
|
|
genesisPath: string;
|
|
genesisSize: number;
|
|
wasmPath: string;
|
|
wasmSize: number;
|
|
}
|
|
|
|
export interface HRMPChannel {
|
|
sender: number;
|
|
receiver: number;
|
|
channelId: string;
|
|
}
|
|
|
|
export interface AssetMetadata {
|
|
name: string;
|
|
symbol: string;
|
|
decimals: number;
|
|
minBalance: string;
|
|
}
|
|
|
|
export interface ForeignAsset {
|
|
symbol: string;
|
|
location: {
|
|
parents: number;
|
|
interior: any; // XCM Location interior
|
|
};
|
|
metadata: AssetMetadata;
|
|
}
|
|
|
|
export interface RegisteredAsset {
|
|
assetId: number;
|
|
symbol: string;
|
|
}
|
|
|
|
export interface XCMTestResult {
|
|
txHash: string;
|
|
success: boolean;
|
|
balance: string;
|
|
error?: string;
|
|
}
|
|
|
|
// ========================================
|
|
// STEP 1: RESERVE PARAID
|
|
// ========================================
|
|
|
|
/**
|
|
* Reserve a ParaId on the relay chain
|
|
*
|
|
* @param api - Polkadot.js API instance (connected to relay chain)
|
|
* @param relayChain - Target relay chain (westend/rococo/polkadot)
|
|
* @param account - Account to sign the transaction
|
|
* @returns Reserved ParaId number
|
|
*/
|
|
export async function reserveParaId(
|
|
api: ApiPromise,
|
|
relayChain: RelayChain,
|
|
account: InjectedAccountWithMeta
|
|
): Promise<number> {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
const injector = await (window as any).injectedWeb3[account.meta.source]?.enable?.('PezkuwiChain');
|
|
if (!injector) {
|
|
throw new Error('Failed to get injector from wallet extension');
|
|
}
|
|
|
|
const signer = injector.signer;
|
|
|
|
// Call registrar.reserve() on relay chain
|
|
const tx = api.tx.registrar.reserve();
|
|
|
|
let unsub: () => void;
|
|
|
|
await tx.signAndSend(account.address, { signer }, ({ status, events, dispatchError }) => {
|
|
if (dispatchError) {
|
|
if (dispatchError.isModule) {
|
|
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
|
reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
|
|
} else {
|
|
reject(new Error(dispatchError.toString()));
|
|
}
|
|
if (unsub) unsub();
|
|
return;
|
|
}
|
|
|
|
if (status.isInBlock) {
|
|
// Extract ParaId from events
|
|
const reservedEvent = events.find(({ event }) =>
|
|
api.events.registrar.Reserved.is(event)
|
|
);
|
|
|
|
if (reservedEvent) {
|
|
const paraId = reservedEvent.event.data[0].toNumber();
|
|
resolve(paraId);
|
|
if (unsub) unsub();
|
|
} else {
|
|
reject(new Error('ParaId reservation failed: No Reserved event found'));
|
|
if (unsub) unsub();
|
|
}
|
|
}
|
|
}).then(unsubscribe => { unsub = unsubscribe; });
|
|
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========================================
|
|
// STEP 2: GENERATE CHAIN ARTIFACTS
|
|
// ========================================
|
|
|
|
/**
|
|
* Generate genesis state and runtime WASM for parachain
|
|
*
|
|
* Note: This is a simplified version. In production, you'd call
|
|
* your blockchain node CLI to generate these artifacts.
|
|
*
|
|
* @param chainName - Name of the parachain
|
|
* @returns Paths to generated artifacts
|
|
*/
|
|
export async function generateChainArtifacts(
|
|
chainName: string
|
|
): Promise<ChainArtifacts> {
|
|
// In a real implementation, this would:
|
|
// 1. Call: ./target/release/pezkuwi export-genesis-state --chain=<chain-spec> > genesis-head.hex
|
|
// 2. Call: ./target/release/pezkuwi export-genesis-wasm --chain=<chain-spec> > runtime.wasm
|
|
// 3. Return the file paths and sizes
|
|
|
|
// For now, we'll return placeholder paths
|
|
// The actual implementation should use Node.js child_process or a backend API
|
|
|
|
return {
|
|
genesisPath: `/tmp/pezkuwi-${chainName}-genesis.hex`,
|
|
genesisSize: 0, // Would be actual file size
|
|
wasmPath: `/tmp/pezkuwi-${chainName}-runtime.wasm`,
|
|
wasmSize: 0, // Would be actual file size
|
|
};
|
|
}
|
|
|
|
// ========================================
|
|
// STEP 3: REGISTER PARACHAIN
|
|
// ========================================
|
|
|
|
/**
|
|
* Register parachain on relay chain with genesis and WASM
|
|
*
|
|
* @param api - Polkadot.js API instance (relay chain)
|
|
* @param paraId - Reserved ParaId
|
|
* @param genesisFile - Genesis state file
|
|
* @param wasmFile - Runtime WASM file
|
|
* @param account - Account to sign transaction
|
|
* @returns Transaction hash
|
|
*/
|
|
export async function registerParachain(
|
|
api: ApiPromise,
|
|
paraId: number,
|
|
genesisFile: File,
|
|
wasmFile: File,
|
|
account: InjectedAccountWithMeta
|
|
): Promise<string> {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
const injector = await (window as any).injectedWeb3[account.meta.source]?.enable?.('PezkuwiChain');
|
|
if (!injector) {
|
|
throw new Error('Failed to get injector from wallet extension');
|
|
}
|
|
|
|
const signer = injector.signer;
|
|
|
|
// Read files as hex strings
|
|
const genesisHex = await readFileAsHex(genesisFile);
|
|
const wasmHex = await readFileAsHex(wasmFile);
|
|
|
|
// Call registrar.register() with paraId, genesis, and wasm
|
|
const tx = api.tx.registrar.register(paraId, genesisHex, wasmHex);
|
|
|
|
let unsub: () => void;
|
|
|
|
await tx.signAndSend(account.address, { signer }, ({ status, dispatchError }) => {
|
|
if (dispatchError) {
|
|
if (dispatchError.isModule) {
|
|
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
|
reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
|
|
} else {
|
|
reject(new Error(dispatchError.toString()));
|
|
}
|
|
if (unsub) unsub();
|
|
return;
|
|
}
|
|
|
|
if (status.isInBlock) {
|
|
resolve(status.asInBlock.toString());
|
|
if (unsub) unsub();
|
|
}
|
|
}).then(unsubscribe => { unsub = unsubscribe; });
|
|
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Helper: Read File as hex string
|
|
*/
|
|
async function readFileAsHex(file: File): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
const arrayBuffer = reader.result as ArrayBuffer;
|
|
const uint8Array = new Uint8Array(arrayBuffer);
|
|
const hex = '0x' + Array.from(uint8Array)
|
|
.map(b => b.toString(16).padStart(2, '0'))
|
|
.join('');
|
|
resolve(hex);
|
|
};
|
|
reader.onerror = () => reject(new Error('Failed to read file'));
|
|
reader.readAsArrayBuffer(file);
|
|
});
|
|
}
|
|
|
|
// ========================================
|
|
// STEP 4: OPEN HRMP CHANNELS
|
|
// ========================================
|
|
|
|
/**
|
|
* Open bidirectional HRMP channels with target parachains
|
|
*
|
|
* @param api - Polkadot.js API instance (relay chain)
|
|
* @param paraId - Our ParaId
|
|
* @param targetParas - List of target ParaIds (e.g., [1000] for Asset Hub)
|
|
* @param account - Account to sign transactions
|
|
* @returns Array of opened channels
|
|
*/
|
|
export async function openHRMPChannels(
|
|
api: ApiPromise,
|
|
paraId: number,
|
|
targetParas: number[],
|
|
account: InjectedAccountWithMeta
|
|
): Promise<HRMPChannel[]> {
|
|
const channels: HRMPChannel[] = [];
|
|
|
|
for (const targetParaId of targetParas) {
|
|
// Open channel: paraId → targetParaId
|
|
const outgoingChannel = await openHRMPChannel(api, paraId, targetParaId, account);
|
|
channels.push(outgoingChannel);
|
|
|
|
// Open channel: targetParaId → paraId (requires governance or target's approval)
|
|
// Note: In practice, this requires the target parachain to initiate
|
|
// For Asset Hub and system chains, this is usually done via governance
|
|
}
|
|
|
|
return channels;
|
|
}
|
|
|
|
/**
|
|
* Open a single HRMP channel
|
|
*/
|
|
async function openHRMPChannel(
|
|
api: ApiPromise,
|
|
sender: number,
|
|
receiver: number,
|
|
account: InjectedAccountWithMeta
|
|
): Promise<HRMPChannel> {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
const injector = await (window as any).injectedWeb3[account.meta.source]?.enable?.('PezkuwiChain');
|
|
if (!injector) {
|
|
throw new Error('Failed to get injector from wallet extension');
|
|
}
|
|
|
|
const signer = injector.signer;
|
|
|
|
// Call hrmp.hrmpInitOpenChannel(recipient, proposedMaxCapacity, proposedMaxMessageSize)
|
|
const maxCapacity = 1000;
|
|
const maxMessageSize = 102400; // 100 KB
|
|
|
|
const tx = api.tx.hrmp.hrmpInitOpenChannel(receiver, maxCapacity, maxMessageSize);
|
|
|
|
let unsub: () => void;
|
|
|
|
await tx.signAndSend(account.address, { signer }, ({ status, events, dispatchError }) => {
|
|
if (dispatchError) {
|
|
if (dispatchError.isModule) {
|
|
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
|
reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
|
|
} else {
|
|
reject(new Error(dispatchError.toString()));
|
|
}
|
|
if (unsub) unsub();
|
|
return;
|
|
}
|
|
|
|
if (status.isInBlock) {
|
|
const channelId = status.asInBlock.toString();
|
|
resolve({ sender, receiver, channelId });
|
|
if (unsub) unsub();
|
|
}
|
|
}).then(unsubscribe => { unsub = unsubscribe; });
|
|
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========================================
|
|
// STEP 5: REGISTER FOREIGN ASSETS
|
|
// ========================================
|
|
|
|
/**
|
|
* Register foreign assets from other chains (via XCM)
|
|
*
|
|
* @param api - Polkadot.js API instance (our parachain)
|
|
* @param assets - List of foreign assets to register
|
|
* @param account - Account to sign transactions
|
|
* @returns List of registered assets with Asset IDs
|
|
*/
|
|
export async function registerForeignAssets(
|
|
api: ApiPromise,
|
|
assets: ForeignAsset[],
|
|
account: InjectedAccountWithMeta
|
|
): Promise<RegisteredAsset[]> {
|
|
const registered: RegisteredAsset[] = [];
|
|
|
|
for (const asset of assets) {
|
|
const registeredAsset = await registerSingleAsset(api, asset, account);
|
|
registered.push(registeredAsset);
|
|
}
|
|
|
|
return registered;
|
|
}
|
|
|
|
/**
|
|
* Register a single foreign asset
|
|
*/
|
|
async function registerSingleAsset(
|
|
api: ApiPromise,
|
|
asset: ForeignAsset,
|
|
account: InjectedAccountWithMeta
|
|
): Promise<RegisteredAsset> {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
const injector = await (window as any).injectedWeb3[account.meta.source]?.enable?.('PezkuwiChain');
|
|
if (!injector) {
|
|
throw new Error('Failed to get injector from wallet extension');
|
|
}
|
|
|
|
const signer = injector.signer;
|
|
|
|
// Get next available asset ID
|
|
const nextAssetId = await getNextAssetId(api);
|
|
|
|
// Create asset with metadata
|
|
// Note: Adjust based on your pallet configuration
|
|
const createTx = api.tx.assets.create(
|
|
nextAssetId,
|
|
account.address, // Admin
|
|
asset.metadata.minBalance
|
|
);
|
|
|
|
const setMetadataTx = api.tx.assets.setMetadata(
|
|
nextAssetId,
|
|
asset.metadata.name,
|
|
asset.metadata.symbol,
|
|
asset.metadata.decimals
|
|
);
|
|
|
|
// Batch both transactions
|
|
const tx = api.tx.utility.batchAll([createTx, setMetadataTx]);
|
|
|
|
let unsub: () => void;
|
|
|
|
await tx.signAndSend(account.address, { signer }, ({ status, dispatchError }) => {
|
|
if (dispatchError) {
|
|
if (dispatchError.isModule) {
|
|
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
|
reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
|
|
} else {
|
|
reject(new Error(dispatchError.toString()));
|
|
}
|
|
if (unsub) unsub();
|
|
return;
|
|
}
|
|
|
|
if (status.isInBlock) {
|
|
resolve({
|
|
assetId: nextAssetId,
|
|
symbol: asset.metadata.symbol,
|
|
});
|
|
if (unsub) unsub();
|
|
}
|
|
}).then(unsubscribe => { unsub = unsubscribe; });
|
|
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get next available Asset ID
|
|
*/
|
|
async function getNextAssetId(api: ApiPromise): Promise<number> {
|
|
// Query existing assets and find the next ID
|
|
// This is a simplified version - adjust based on your implementation
|
|
const assets = await api.query.assets.asset.entries();
|
|
|
|
if (assets.length === 0) {
|
|
return 1000; // Start from 1000 for foreign assets
|
|
}
|
|
|
|
const maxId = Math.max(...assets.map(([key]) => {
|
|
const assetId = key.args[0].toNumber();
|
|
return assetId;
|
|
}));
|
|
|
|
return maxId + 1;
|
|
}
|
|
|
|
// ========================================
|
|
// STEP 6: TEST XCM TRANSFER
|
|
// ========================================
|
|
|
|
/**
|
|
* Test XCM transfer from Asset Hub USDT to our wUSDT
|
|
*
|
|
* @param api - Polkadot.js API instance (our parachain)
|
|
* @param amount - Amount to transfer (in smallest unit)
|
|
* @param account - Account to receive the transfer
|
|
* @returns Test result with transaction hash and balance
|
|
*/
|
|
export async function testXCMTransfer(
|
|
api: ApiPromise,
|
|
amount: string,
|
|
account: InjectedAccountWithMeta
|
|
): Promise<XCMTestResult> {
|
|
try {
|
|
// This is a placeholder for XCM testing
|
|
// In reality, you'd need to:
|
|
// 1. Connect to Asset Hub
|
|
// 2. Send limitedReserveTransferAssets() to our parachain
|
|
// 3. Monitor for AssetReceived event on our side
|
|
|
|
// For now, return a mock success result
|
|
return {
|
|
txHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
success: false,
|
|
balance: '0',
|
|
error: 'XCM testing requires connection to relay chain and Asset Hub',
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
txHash: '',
|
|
success: false,
|
|
balance: '0',
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
};
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// XCM TELEPORT: RELAY CHAIN → ASSET HUB
|
|
// ========================================
|
|
|
|
/**
|
|
* Teleport HEZ from Relay Chain to Asset Hub
|
|
* This is needed to pay fees on Asset Hub for PEZ transfers
|
|
*
|
|
* @param relayApi - Polkadot.js API instance (connected to relay chain)
|
|
* @param amount - Amount in smallest unit (e.g., 100000000000 for 0.1 HEZ with 12 decimals)
|
|
* @param account - Account to sign and receive on Asset Hub
|
|
* @param assetHubParaId - Asset Hub parachain ID (default: 1000)
|
|
* @returns Transaction hash
|
|
*/
|
|
export async function teleportToAssetHub(
|
|
relayApi: ApiPromise,
|
|
amount: string | bigint,
|
|
account: InjectedAccountWithMeta,
|
|
assetHubParaId: number = 1000
|
|
): Promise<string> {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
const injector = await (window as any).injectedWeb3[account.meta.source]?.enable?.('PezkuwiChain');
|
|
if (!injector) {
|
|
throw new Error('Failed to get injector from wallet extension');
|
|
}
|
|
|
|
const signer = injector.signer;
|
|
|
|
// Destination: Asset Hub parachain
|
|
const dest = {
|
|
V3: {
|
|
parents: 0,
|
|
interior: {
|
|
X1: { Parachain: assetHubParaId }
|
|
}
|
|
}
|
|
};
|
|
|
|
// Beneficiary: Same account on Asset Hub
|
|
const beneficiary = {
|
|
V3: {
|
|
parents: 0,
|
|
interior: {
|
|
X1: {
|
|
AccountId32: {
|
|
network: null,
|
|
id: relayApi.createType('AccountId32', account.address).toHex()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Assets: Native token (HEZ)
|
|
const assets = {
|
|
V3: [{
|
|
id: {
|
|
Concrete: {
|
|
parents: 0,
|
|
interior: 'Here'
|
|
}
|
|
},
|
|
fun: {
|
|
Fungible: amount.toString()
|
|
}
|
|
}]
|
|
};
|
|
|
|
// Fee asset item (index 0 = first asset)
|
|
const feeAssetItem = 0;
|
|
|
|
// Weight limit: Unlimited
|
|
const weightLimit = 'Unlimited';
|
|
|
|
// Create teleport transaction
|
|
const tx = relayApi.tx.xcmPallet.limitedTeleportAssets(
|
|
dest,
|
|
beneficiary,
|
|
assets,
|
|
feeAssetItem,
|
|
weightLimit
|
|
);
|
|
|
|
let unsub: () => void;
|
|
|
|
await tx.signAndSend(account.address, { signer }, ({ status, events, dispatchError }) => {
|
|
if (dispatchError) {
|
|
if (dispatchError.isModule) {
|
|
const decoded = relayApi.registry.findMetaError(dispatchError.asModule);
|
|
reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
|
|
} else {
|
|
reject(new Error(dispatchError.toString()));
|
|
}
|
|
if (unsub) unsub();
|
|
return;
|
|
}
|
|
|
|
if (status.isInBlock) {
|
|
console.log(`✅ XCM Teleport included in block: ${status.asInBlock}`);
|
|
|
|
// Check for XCM events
|
|
const xcmSent = events.find(({ event }) =>
|
|
event.section === 'xcmPallet' && event.method === 'Sent'
|
|
);
|
|
|
|
if (xcmSent) {
|
|
console.log('✅ XCM message sent successfully');
|
|
}
|
|
|
|
resolve(status.asInBlock.toString());
|
|
if (unsub) unsub();
|
|
}
|
|
}).then(unsubscribe => { unsub = unsubscribe; });
|
|
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Teleport HEZ from Asset Hub back to Relay Chain
|
|
*
|
|
* @param assetHubApi - Polkadot.js API instance (connected to Asset Hub)
|
|
* @param amount - Amount in smallest unit
|
|
* @param account - Account to sign and receive on relay chain
|
|
* @returns Transaction hash
|
|
*/
|
|
export async function teleportToRelayChain(
|
|
assetHubApi: ApiPromise,
|
|
amount: string | bigint,
|
|
account: InjectedAccountWithMeta
|
|
): Promise<string> {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
const injector = await (window as any).injectedWeb3[account.meta.source]?.enable?.('PezkuwiChain');
|
|
if (!injector) {
|
|
throw new Error('Failed to get injector from wallet extension');
|
|
}
|
|
|
|
const signer = injector.signer;
|
|
|
|
// Destination: Relay chain (parent)
|
|
const dest = {
|
|
V3: {
|
|
parents: 1,
|
|
interior: 'Here'
|
|
}
|
|
};
|
|
|
|
// Beneficiary: Same account on relay chain
|
|
const beneficiary = {
|
|
V3: {
|
|
parents: 0,
|
|
interior: {
|
|
X1: {
|
|
AccountId32: {
|
|
network: null,
|
|
id: assetHubApi.createType('AccountId32', account.address).toHex()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Assets: Native token
|
|
const assets = {
|
|
V3: [{
|
|
id: {
|
|
Concrete: {
|
|
parents: 1,
|
|
interior: 'Here'
|
|
}
|
|
},
|
|
fun: {
|
|
Fungible: amount.toString()
|
|
}
|
|
}]
|
|
};
|
|
|
|
const feeAssetItem = 0;
|
|
const weightLimit = 'Unlimited';
|
|
|
|
const tx = assetHubApi.tx.polkadotXcm.limitedTeleportAssets(
|
|
dest,
|
|
beneficiary,
|
|
assets,
|
|
feeAssetItem,
|
|
weightLimit
|
|
);
|
|
|
|
let unsub: () => void;
|
|
|
|
await tx.signAndSend(account.address, { signer }, ({ status, dispatchError }) => {
|
|
if (dispatchError) {
|
|
if (dispatchError.isModule) {
|
|
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
|
reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
|
|
} else {
|
|
reject(new Error(dispatchError.toString()));
|
|
}
|
|
if (unsub) unsub();
|
|
return;
|
|
}
|
|
|
|
if (status.isInBlock) {
|
|
resolve(status.asInBlock.toString());
|
|
if (unsub) unsub();
|
|
}
|
|
}).then(unsubscribe => { unsub = unsubscribe; });
|
|
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========================================
|
|
// UTILITY FUNCTIONS
|
|
// ========================================
|
|
|
|
/**
|
|
* Get relay chain endpoint based on network selection
|
|
*/
|
|
export function getRelayChainEndpoint(relayChain: RelayChain): string {
|
|
const endpoints = {
|
|
westend: 'wss://westend-rpc.polkadot.io',
|
|
rococo: 'wss://rococo-rpc.polkadot.io',
|
|
polkadot: 'wss://rpc.polkadot.io',
|
|
};
|
|
|
|
return endpoints[relayChain];
|
|
}
|
|
|
|
/**
|
|
* Asset Hub ParaId by relay chain
|
|
*/
|
|
export function getAssetHubParaId(relayChain: RelayChain): number {
|
|
const paraIds = {
|
|
westend: 1000, // Westend Asset Hub
|
|
rococo: 1000, // Rococo Asset Hub
|
|
polkadot: 1000, // Polkadot Asset Hub (Statemint)
|
|
};
|
|
|
|
return paraIds[relayChain];
|
|
}
|