From 8536459f7a9a5c26f7f70bea41e89793181d9e30 Mon Sep 17 00:00:00 2001 From: mmaurello <93129175+mmaurello@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:56:31 +0100 Subject: [PATCH] Adjustments after implementing mrl package in dapp (#401) * -wip- adjustments after integration * add setIsAutomatic data initializer and exclude relayer fee from min if not automatic * add statusCallback param * fixes in getMoonChainData * adjust configs * more adjustments * change fantom rpc endpoint --- examples/sdk-simple/index.ts | 20 +++-------- packages/config/src/chains.ts | 7 +++- .../config/src/mrl-configs/fantomTestnet.ts | 32 +++++++++++++++++ .../config/src/mrl-configs/peaqAlphanet.ts | 2 +- .../src/getTransferData/getMoonChainData.ts | 28 +++++---------- .../mrl/src/getTransferData/getSourceData.ts | 11 ++++-- .../src/getTransferData/getTransferData.ts | 10 ++++-- packages/mrl/src/mrl.interfaces.ts | 11 ++++-- packages/mrl/src/mrl.ts | 30 +++++++++------- .../src/services/polkadot/PolkadotService.ts | 36 ++++++++++++++----- 10 files changed, 121 insertions(+), 66 deletions(-) diff --git a/examples/sdk-simple/index.ts b/examples/sdk-simple/index.ts index e2e1ee2c..32bcb54b 100644 --- a/examples/sdk-simple/index.ts +++ b/examples/sdk-simple/index.ts @@ -38,34 +38,24 @@ console.log(`Substrate address: ${pair.address}`); export function logBalances(data: TransferData): void { console.log( - `Balance on ${data.source.chain.name} ${data.source.balance.toDecimal()} ${ - data.source.balance.symbol - }`, + `Balance on ${data.source.chain.name} ${data.source.balance.toDecimal()} ${data.source.balance.getSymbol()}`, ); console.log( `Balance on ${ data.destination.chain.name - } ${data.destination.balance.toDecimal()} ${ - data.destination.balance.symbol - }`, + } ${data.destination.balance.toDecimal()} ${data.destination.balance.getSymbol()}`, ); } export function logTxDetails(data: TransferData): void { console.log( - `\nYou can send min: ${data.min.toDecimal()} ${ - data.min.symbol - } and max: ${data.max.toDecimal()} ${data.max.symbol} from ${ + `\nYou can send min: ${data.min.toDecimal()} ${data.min.getSymbol()} and max: ${data.max.toDecimal()} ${data.max.getSymbol()} from ${ data.source.chain.name } to ${ data.destination.chain.name - }. You will pay ${data.source.fee.toDecimal()} ${ - data.source.fee.symbol - } fee on ${ + }. You will pay ${data.source.fee.toDecimal()} ${data.source.fee.getSymbol()} fee on ${ data.source.chain.name - } and ${data.destination.fee.toDecimal()} ${ - data.destination.fee.symbol - } fee on ${data.destination.chain.name}.`, + } and ${data.destination.fee.toDecimal()} ${data.destination.fee.getSymbol()} fee on ${data.destination.chain.name}.`, ); } diff --git a/packages/config/src/chains.ts b/packages/config/src/chains.ts index 2762c568..9e0ad8dd 100644 --- a/packages/config/src/chains.ts +++ b/packages/config/src/chains.ts @@ -480,7 +480,7 @@ export const fantomTestnet = new EvmChain({ key: 'fantom-testnet', name: 'Fantom Testnet', nativeAsset: ftm, - rpc: 'https://rpc.testnet.fantom.network', + rpc: 'https://fantom-testnet-rpc.publicnode.com', wh: { name: 'Fantom', }, @@ -962,6 +962,7 @@ export const moonbaseAlpha = new EvmParachain({ }), ], ecosystem: Ecosystem.AlphanetRelay, + explorer: 'https://moonbase.moonscan.io', genesisHash: '0x91bc6e169807aaa54802737e1c504b2577d4fafedd5a02c10293b1cd60e39527', id: 1287, @@ -1014,6 +1015,9 @@ export const moonbaseBeta = new EvmParachain({ }), ], ecosystem: Ecosystem.AlphanetRelay, + explorer: getPolkadotAppsUrl( + 'wss://moonbase-beta.api.moonbase.moonbeam.network', + ), genesisHash: '0xeebb5d05763801e54d6a7a60a4b7998ac125c4d050dcec418dd07ea959a54464', id: 1282, @@ -1347,6 +1351,7 @@ export const moonbeam = new EvmParachain({ }), ], ecosystem: Ecosystem.Polkadot, + explorer: 'https://moonbeam.moonscan.io', genesisHash: '0xfe58ea77779b7abda7da4ec526d14db9b1e9cd40a217c34892af80a9b332b76d', id: 1284, diff --git a/packages/config/src/mrl-configs/fantomTestnet.ts b/packages/config/src/mrl-configs/fantomTestnet.ts index c8c22ee1..3059b96c 100644 --- a/packages/config/src/mrl-configs/fantomTestnet.ts +++ b/packages/config/src/mrl-configs/fantomTestnet.ts @@ -5,6 +5,7 @@ import { moonbaseAlpha, moonbaseBeta, peaqAlphanet, + peaqEvmAlphanet, } from '../chains'; import { MrlChainRoutes } from '../types/MrlChainRoutes'; @@ -42,6 +43,37 @@ export const fantomTestnetRoutes = new MrlChainRoutes({ }, }, }, + { + source: { + asset: ftm, + balance: BalanceBuilder().evm().native(), + destinationFee: { + asset: ftm, + balance: BalanceBuilder().evm().native(), + }, + }, + destination: { + asset: ftmwh, + chain: peaqEvmAlphanet, + balance: BalanceBuilder().evm().erc20(), + fee: { + asset: ftmwh, + amount: 0.01, + }, + }, + mrl: { + isAutomaticPossible: false, + transfer: MrlBuilder().wormhole().wormhole().tokenTransfer(), + moonChain: { + asset: ftmwh, + fee: { + asset: dev, + amount: 0.1, + balance: BalanceBuilder().substrate().system().account(), + }, + }, + }, + }, { source: { asset: agng, diff --git a/packages/config/src/mrl-configs/peaqAlphanet.ts b/packages/config/src/mrl-configs/peaqAlphanet.ts index a9bd5dc7..d076e6f8 100644 --- a/packages/config/src/mrl-configs/peaqAlphanet.ts +++ b/packages/config/src/mrl-configs/peaqAlphanet.ts @@ -61,7 +61,7 @@ export const peaqAlphanetRoutes = new MrlChainRoutes({ destination: { asset: agng, chain: fantomTestnet, - balance: BalanceBuilder().evm().native(), + balance: BalanceBuilder().evm().erc20(), fee: { asset: agng, amount: 0.2, diff --git a/packages/mrl/src/getTransferData/getMoonChainData.ts b/packages/mrl/src/getTransferData/getMoonChainData.ts index 010248ce..752dd9f6 100644 --- a/packages/mrl/src/getTransferData/getMoonChainData.ts +++ b/packages/mrl/src/getTransferData/getMoonChainData.ts @@ -2,19 +2,14 @@ import { type MrlAssetRoute, getMoonChain } from '@moonbeam-network/xcm-config'; import { getBalance, getDestinationFee } from '@moonbeam-network/xcm-sdk'; import { Parachain } from '@moonbeam-network/xcm-types'; import { getMultilocationDerivedAddresses } from '@moonbeam-network/xcm-utils'; -import type { - DestinationTransferData, - MoonChainTransferData, -} from '../mrl.interfaces'; +import type { MoonChainTransferData } from '../mrl.interfaces'; interface GetMoonChainDataParams { - destinationData: DestinationTransferData; route: MrlAssetRoute; sourceAddress: string; } export async function getMoonChainData({ - destinationData, route, sourceAddress, }: GetMoonChainDataParams): Promise { @@ -25,16 +20,6 @@ export async function getMoonChainData({ } const moonChain = getMoonChain(route.source.chain); - const asset = moonChain.getChainAsset(route.mrl.moonChain.asset); - const isDestinationMoonChain = route.destination.chain.isEqual(moonChain); - - if (isDestinationMoonChain) { - return { - balance: destinationData.balance, - chain: destinationData.chain, - fee: destinationData.fee, - }; - } const fee = await getDestinationFee({ address: sourceAddress, // TODO not correct @@ -46,7 +31,10 @@ export async function getMoonChainData({ let address = sourceAddress; - if (Parachain.is(route.source.chain)) { + if ( + Parachain.is(route.source.chain) && + !route.source.chain.isEqual(moonChain) + ) { const { address20 } = getMultilocationDerivedAddresses({ address: sourceAddress, paraId: route.source.chain.parachainId, @@ -56,15 +44,15 @@ export async function getMoonChainData({ address = address20; } - const balance = await getBalance({ + const feeBalance = await getBalance({ address, - asset, + asset: moonChain.getChainAsset(route.mrl.moonChain.fee.asset), builder: route.mrl.moonChain.fee.balance, chain: moonChain, }); return { - balance, + feeBalance, chain: moonChain, fee, }; diff --git a/packages/mrl/src/getTransferData/getSourceData.ts b/packages/mrl/src/getTransferData/getSourceData.ts index baeae161..9c9c79bc 100644 --- a/packages/mrl/src/getTransferData/getSourceData.ts +++ b/packages/mrl/src/getTransferData/getSourceData.ts @@ -21,6 +21,7 @@ import { type EvmChain, type EvmParachain, } from '@moonbeam-network/xcm-types'; +import { toBigInt } from '@moonbeam-network/xcm-utils'; import type { SourceTransferData } from '../mrl.interfaces'; import { WormholeService } from '../services/wormhole'; import { @@ -30,6 +31,7 @@ import { } from './getTransferData.utils'; interface GetSourceDataParams { + isAutomatic: boolean; route: MrlAssetRoute; destinationAddress: string; destinationFee: AssetAmount; @@ -37,6 +39,7 @@ interface GetSourceDataParams { } export async function getSourceData({ + isAutomatic, route, destinationAddress, destinationFee, @@ -94,7 +97,7 @@ export async function getSourceData({ asset: balance, destinationAddress, feeAsset: feeBalance, - isAutomatic: route.mrl.isAutomaticPossible, + isAutomatic, route, sourceAddress, }); @@ -114,7 +117,7 @@ export async function getSourceData({ transfer, asset: balance, feeAsset: feeBalance, - isAutomatic: route.mrl.isAutomaticPossible, + isAutomatic, destinationAddress, route, sourceAddress, @@ -239,11 +242,13 @@ async function getWormholeFee({ config, }: GetWormholeFeeParams): Promise { if (WormholeConfig.is(config)) { + const safetyAmount = toBigInt(0.000001, asset.decimals); + const wh = WormholeService.create(chain as EvmChain | EvmParachain); const fee = await wh.getFee(config); return AssetAmount.fromChainAsset(chain.getChainAsset(asset), { - amount: fee.relayFee?.amount || 0n, + amount: fee.relayFee ? fee.relayFee.amount + safetyAmount : 0n, }); } diff --git a/packages/mrl/src/getTransferData/getTransferData.ts b/packages/mrl/src/getTransferData/getTransferData.ts index 088a2935..fb675777 100644 --- a/packages/mrl/src/getTransferData/getTransferData.ts +++ b/packages/mrl/src/getTransferData/getTransferData.ts @@ -32,12 +32,14 @@ interface GetTransferDataParams { route: MrlAssetRoute; sourceAddress: string; destinationAddress: string; + isAutomatic: boolean; } export async function getTransferData({ route, sourceAddress, destinationAddress, + isAutomatic, }: GetTransferDataParams): Promise { if (!route.mrl) { throw new Error( @@ -58,6 +60,7 @@ export async function getTransferData({ }); const sourceData = await getSourceData({ + isAutomatic: route.mrl.isAutomaticPossible && isAutomatic, route, destinationAddress, destinationFee, @@ -65,7 +68,6 @@ export async function getTransferData({ }); const moonChainData = await getMoonChainData({ - destinationData, route, sourceAddress, }); @@ -87,12 +89,14 @@ export async function getTransferData({ .minus( isSameAssetPayingDestinationFee ? destinationFee.toBig() : Big(0), ) - .minus(fee); + .minus(fee) + .minus(sourceData.relayerFee?.toBig() || Big(0)); return sourceData.balance.copyWith({ amount: result.lt(0) ? 0n : BigInt(result.toFixed()), }); }, + isAutomaticPossible: route.mrl.isAutomaticPossible, max: sourceData.max, min: getMrlMin({ destinationData, @@ -105,6 +109,7 @@ export async function getTransferData({ amount, isAutomatic, { evmSigner, polkadotSigner }: Partial, + statusCallback, ): Promise { const source = route.source.chain; @@ -152,6 +157,7 @@ export async function getTransferData({ sourceAddress, transfer, polkadotSigner, + statusCallback, ); return [hash]; diff --git a/packages/mrl/src/mrl.interfaces.ts b/packages/mrl/src/mrl.interfaces.ts index a552d5ed..ac80beb0 100644 --- a/packages/mrl/src/mrl.interfaces.ts +++ b/packages/mrl/src/mrl.interfaces.ts @@ -4,7 +4,7 @@ import type { } from '@moonbeam-network/xcm-sdk'; import type { AnyChain, AssetAmount } from '@moonbeam-network/xcm-types'; import type { Signer } from '@polkadot/api/types'; -import type { IKeyringPair } from '@polkadot/types/types'; +import type { IKeyringPair, ISubmittableResult } from '@polkadot/types/types'; import type { TokenTransfer } from '@wormhole-foundation/sdk-connect'; import type { WalletClient } from 'viem'; @@ -16,6 +16,7 @@ export interface Signers { export interface TransferData { destination: DestinationTransferData; getEstimate(amount: number | string): AssetAmount; + isAutomaticPossible: boolean; max: AssetAmount; min: AssetAmount; moonChain: MoonChainTransferData; @@ -24,6 +25,7 @@ export interface TransferData { amount: bigint | number | string, isAutomatic: boolean, signers: Signers, + statusCallback?: (params: ISubmittableResult) => void, ): Promise; } @@ -37,7 +39,12 @@ export interface SourceTransferData extends SourceChainTransferData { export interface DestinationTransferData extends ChainTransferData {} -export type MoonChainTransferData = Omit; +export type MoonChainTransferData = Omit< + ChainTransferData, + 'min' | 'balance' +> & { + feeBalance: AssetAmount; +}; export interface ChainTransferData { chain: AnyChain; diff --git a/packages/mrl/src/mrl.ts b/packages/mrl/src/mrl.ts index 33c5c26e..43f1b8a0 100644 --- a/packages/mrl/src/mrl.ts +++ b/packages/mrl/src/mrl.ts @@ -39,20 +39,24 @@ export function Mrl(options?: MrlOptions) { source, destination, }); - return { - setAddresses({ - sourceAddress, - destinationAddress, - }: { - sourceAddress: string; - destinationAddress: string; - }) { - return getTransferData({ - route, - sourceAddress, - destinationAddress, - }); + setIsAutomatic(isAutomatic: boolean) { + return { + setAddresses({ + sourceAddress, + destinationAddress, + }: { + sourceAddress: string; + destinationAddress: string; + }) { + return getTransferData({ + route, + sourceAddress, + destinationAddress, + isAutomatic, + }); + }, + }; }, }; }, diff --git a/packages/sdk/src/services/polkadot/PolkadotService.ts b/packages/sdk/src/services/polkadot/PolkadotService.ts index 9d8de27b..8d55e4fb 100644 --- a/packages/sdk/src/services/polkadot/PolkadotService.ts +++ b/packages/sdk/src/services/polkadot/PolkadotService.ts @@ -90,18 +90,36 @@ export class PolkadotService { account: string, config: ExtrinsicConfig, signer: PolkadotSigner | IKeyringPair, + statusCallback?: (params: ISubmittableResult) => void, ): Promise { const extrinsic = this.getExtrinsic(config); - const hash = await extrinsic.signAndSend( - this.#isSigner(signer) ? account : signer, - { - nonce: -1, - signer: this.#isSigner(signer) ? signer : undefined, - withSignedTransaction: true, - }, - ); - return hash.toString(); + const isSigner = this.#isSigner(signer); + const signOptions = { + nonce: -1, + signer: isSigner ? signer : undefined, + withSignedTransaction: true, + }; + + const hash = await new Promise((resolve, reject) => { + extrinsic + .signAndSend(isSigner ? account : signer, signOptions, (result) => { + if (result.isError || result.dispatchError) { + reject( + new Error( + result.dispatchError?.toString() || 'Transaction failed', + ), + ); + } + if (result.txHash) { + resolve(result.txHash.toString()); + } + statusCallback?.(result); + }) + .catch(reject); + }); + + return hash; } #isSigner(signer: PolkadotSigner | IKeyringPair): signer is PolkadotSigner {