diff --git a/packages/builder/src/mrl/providers/wormhole/extrinsic/polkadotXcm/polkadotXcm.ts b/packages/builder/src/mrl/providers/wormhole/extrinsic/polkadotXcm/polkadotXcm.ts index 79839830..6df20e27 100644 --- a/packages/builder/src/mrl/providers/wormhole/extrinsic/polkadotXcm/polkadotXcm.ts +++ b/packages/builder/src/mrl/providers/wormhole/extrinsic/polkadotXcm/polkadotXcm.ts @@ -1,8 +1,18 @@ -import { type AnyParachain, AssetAmount } from '@moonbeam-network/xcm-types'; +import { + type AnyParachain, + AssetAmount, + Parachain, +} from '@moonbeam-network/xcm-types'; import { getMultilocationDerivedAddresses } from '@moonbeam-network/xcm-utils'; +import type { ApiPromise } from '@polkadot/api'; +import type { SubmittableExtrinsic } from '@polkadot/api/types'; +import type { ISubmittableResult } from '@polkadot/types/types'; import { ExtrinsicBuilder } from '../../../../../extrinsic/ExtrinsicBuilder'; import { ExtrinsicConfig } from '../../../../../types/substrate/ExtrinsicConfig'; -import type { MrlConfigBuilder } from '../../../../MrlBuilder.interfaces'; +import type { + MrlBuilderParams, + MrlConfigBuilder, +} from '../../../../MrlBuilder.interfaces'; // TODO: these have to come from the configs const BUY_EXECUTION_FEE = 100_000_000_000_000_000n; // moonChainFee @@ -14,7 +24,9 @@ export function polkadotXcm() { build: ({ asset, destination, + destinationAddress, fee, + isAutomatic, moonAsset, moonChain, moonApi, @@ -35,56 +47,31 @@ export function polkadotXcm() { throw new Error('Source API needs to be defined'); } + if (!Parachain.is(source)) { + throw new Error('Source chain needs to be a parachain'); + } + const { address20: computedOriginAccount } = getMultilocationDerivedAddresses({ address: sourceAddress, - paraId: moonChain.parachainId, + paraId: source.parachainId, isParents: true, }); - const { transfer } = sourceApi.tx.xTokens; - const builder = ExtrinsicBuilder().xTokens().transfer(); - - const assetTransferTx = transfer( - ...builder - .build({ - asset, - destination: moonChain, - destinationAddress: computedOriginAccount, - destinationApi: moonApi, - fee, - // TODO: This is a workaround. xTokens.transfer doesn't need source chain but the interfaces requires it. - // In this case we know that a source chain is not a Parachain. - source: source as AnyParachain, - sourceAddress, - sourceApi, - }) - .getArgs(transfer), - ); - /* - * TODO: Can we move it to AssetRoute and receive it in build params? - * In the future we could also check if wallet already has necessary DEV/GLMR to pay execution fees on Moonbase/Moonbeam. - * Also we need to move fees to AssetRoute. - */ - const feeAssetTransferTx = transfer( - ...builder - .build({ - asset: AssetAmount.fromChainAsset( - source.getChainAsset(moonAsset), - { - amount: CROSS_CHAIN_FEE + BUY_EXECUTION_FEE, - }, - ), - destination: moonChain, - destinationAddress: computedOriginAccount, - destinationApi: moonApi, - fee, - source: source as AnyParachain, - sourceAddress, - sourceApi, - }) - .getArgs(transfer), - ); + const assetTransferTxs = getAssetTransferTxs({ + asset, + computedOriginAccount, + destination, + destinationAddress, + fee, + isAutomatic, + moonApi, + moonAsset, + moonChain, + source, + sourceAddress, + sourceApi, + }); const send = sourceApi.tx.polkadotXcm.send( { @@ -166,9 +153,95 @@ export function polkadotXcm() { return new ExtrinsicConfig({ module: 'utility', func: 'batchAll', - getArgs: () => [[assetTransferTx, feeAssetTransferTx, send]], + getArgs: () => [[...assetTransferTxs, send]], }); }, }), }; } + +interface GetAssetTransferTxsParams extends MrlBuilderParams { + computedOriginAccount: string; + source: AnyParachain; + sourceApi: ApiPromise; +} + +function getAssetTransferTxs({ + asset, + computedOriginAccount, + fee, + moonApi, + moonAsset, + moonChain, + source, + sourceAddress, + sourceApi, +}: GetAssetTransferTxsParams): SubmittableExtrinsic< + 'promise', + ISubmittableResult +>[] { + const { transfer, transferMulticurrencies } = sourceApi.tx.xTokens; + const transferBuilder = ExtrinsicBuilder().xTokens().transfer(); + const transferMulticurrenciesBuilder = ExtrinsicBuilder() + .xTokens() + .transferMultiCurrencies(); + /** + * TODO here we should compare the asset with the cross chain fee asset. + * For example, FTM cannot pay for fees in Moonbase while AGNG can, so for FTM we have to send a transferMulticurrencies + * This "if" is a workaround, change when we implement properly the concept of cross-chain fee (different from moonChainFee) + */ + if (asset.isSame(fee)) { + const assetTransferTx = transfer( + ...transferBuilder + .build({ + asset: asset.copyWith({ + // TODO for the moment this is only applicable to peaq, AGNG pays for fees and we have to include the cross chain fee + // for this we have to add a new concept in the config (Cross Chain Fee), and get the value from there + amount: asset.amount + 10n * CROSS_CHAIN_FEE, + }), + destination: moonChain, + destinationAddress: computedOriginAccount, + destinationApi: moonApi, + fee, + source: source, + sourceAddress, + sourceApi, + }) + .getArgs(transfer), + ); + const feeAssetTransferTx = transfer( + ...transferBuilder + .build({ + asset: AssetAmount.fromChainAsset(source.getChainAsset(moonAsset), { + amount: CROSS_CHAIN_FEE + BUY_EXECUTION_FEE, + }), + destination: moonChain, + destinationAddress: computedOriginAccount, + destinationApi: moonApi, + fee, + source: source, + sourceAddress, + sourceApi, + }) + .getArgs(transfer), + ); + return [assetTransferTx, feeAssetTransferTx]; + } + const multiCurrenciesTransferTx = transferMulticurrencies( + ...transferMulticurrenciesBuilder + .build({ + asset, + destination: moonChain, + destinationAddress: computedOriginAccount, + destinationApi: moonApi, + fee: AssetAmount.fromChainAsset(source.getChainAsset(moonAsset), { + amount: CROSS_CHAIN_FEE + BUY_EXECUTION_FEE, + }), + source: source as AnyParachain, + sourceAddress, + sourceApi, + }) + .getArgs(transferMulticurrencies), + ); + return [multiCurrenciesTransferTx]; +} diff --git a/packages/builder/src/mrl/providers/wormhole/wormhole/wormhole.ts b/packages/builder/src/mrl/providers/wormhole/wormhole/wormhole.ts index 4575ade5..24960128 100644 --- a/packages/builder/src/mrl/providers/wormhole/wormhole/wormhole.ts +++ b/packages/builder/src/mrl/providers/wormhole/wormhole/wormhole.ts @@ -1,4 +1,4 @@ -import { EvmChain, EvmParachain } from '@moonbeam-network/xcm-types'; +import { EvmChain, EvmParachain, Parachain } from '@moonbeam-network/xcm-types'; import { getMultilocationDerivedAddresses } from '@moonbeam-network/xcm-utils'; import { evmToAddress } from '@polkadot/util-crypto/address'; import { Wormhole } from '@wormhole-foundation/sdk-connect'; @@ -26,6 +26,7 @@ export function wormhole() { source, sourceAddress, }): WormholeConfig => { + const isSourceParachain = Parachain.is(source); const isDestinationMoonChain = destination.isEqual(moonChain); const isDestinationEvmChain = EvmChain.is(destination); const isNativeAsset = asset.isSame( @@ -40,7 +41,7 @@ export function wormhole() { const { address20: computedOriginAccount } = getMultilocationDerivedAddresses({ address: sourceAddress, - paraId: moonChain.parachainId, + paraId: isSourceParachain ? source.parachainId : undefined, isParents: true, }); diff --git a/packages/config/src/mrl-configs/fantomTestnet.ts b/packages/config/src/mrl-configs/fantomTestnet.ts index 1f9f8739..fe6ceac8 100644 --- a/packages/config/src/mrl-configs/fantomTestnet.ts +++ b/packages/config/src/mrl-configs/fantomTestnet.ts @@ -92,7 +92,7 @@ export const fantomTestnetRoutes = new MrlChainRoutes({ }, }, mrl: { - isAutomaticPossible: true, + isAutomaticPossible: false, // TODO transfer: MrlBuilder().wormhole().wormhole().tokenTransfer(), moonChain: { asset: ftmwh, diff --git a/packages/config/src/mrl-configs/moonbaseAlpha.ts b/packages/config/src/mrl-configs/moonbaseAlpha.ts index 9538185e..7a03c035 100644 --- a/packages/config/src/mrl-configs/moonbaseAlpha.ts +++ b/packages/config/src/mrl-configs/moonbaseAlpha.ts @@ -29,7 +29,7 @@ export const moonbaseAlphaRoutes = new MrlChainRoutes({ }, }, mrl: { - isAutomaticPossible: true, + isAutomaticPossible: false, // TODO transfer: MrlBuilder().wormhole().wormhole().tokenTransfer(), moonChain: { asset: ftmwh, diff --git a/packages/mrl/src/getTransferData/getMoonChainData.ts b/packages/mrl/src/getTransferData/getMoonChainData.ts index a93d3a38..98c47eb1 100644 --- a/packages/mrl/src/getTransferData/getMoonChainData.ts +++ b/packages/mrl/src/getTransferData/getMoonChainData.ts @@ -49,7 +49,7 @@ export async function getMoonChainData({ if (Parachain.is(route.source.chain)) { const { address20 } = getMultilocationDerivedAddresses({ address: sourceAddress, - paraId: moonChain.parachainId, + paraId: route.source.chain.parachainId, isParents: true, }); diff --git a/packages/mrl/src/getTransferData/getSourceData.ts b/packages/mrl/src/getTransferData/getSourceData.ts index 7d1d1407..2f53318e 100644 --- a/packages/mrl/src/getTransferData/getSourceData.ts +++ b/packages/mrl/src/getTransferData/getSourceData.ts @@ -93,7 +93,7 @@ export async function getSourceData({ const transfer = await buildTransfer({ asset: balance, destinationAddress, - destinationFee, + feeAsset: feeBalance, route, sourceAddress, }); @@ -112,8 +112,8 @@ export async function getSourceData({ chain: source, transfer, asset: balance, + feeAsset: feeBalance, destinationAddress, - destinationFee, route, sourceAddress, }); @@ -201,7 +201,7 @@ export async function getRelayerFee({ asset, chain, destinationAddress, - destinationFee, + feeAsset, route, sourceAddress, transfer, @@ -214,7 +214,7 @@ export async function getRelayerFee({ const builderParams = await getMrlBuilderParams({ asset, destinationAddress, - destinationFee, + feeAsset, route, sourceAddress, }); diff --git a/packages/mrl/src/getTransferData/getTransferData.ts b/packages/mrl/src/getTransferData/getTransferData.ts index 3c5189ed..fd281b36 100644 --- a/packages/mrl/src/getTransferData/getTransferData.ts +++ b/packages/mrl/src/getTransferData/getTransferData.ts @@ -112,11 +112,16 @@ export async function getTransferData({ route.source.chain.getChainAsset(route.source.asset), { amount: bigintAmount }, ); - + const feeAsset = AssetAmount.fromChainAsset( + route.source.chain.getChainAsset( + route.source.fee?.asset || route.source.asset, + ), + { amount: sourceData.fee.amount }, + ); const transfer = await buildTransfer({ asset, destinationAddress, - destinationFee, + feeAsset, route, sourceAddress, }); diff --git a/packages/mrl/src/getTransferData/getTransferData.utils.ts b/packages/mrl/src/getTransferData/getTransferData.utils.ts index a7730c3d..f96da7cf 100644 --- a/packages/mrl/src/getTransferData/getTransferData.utils.ts +++ b/packages/mrl/src/getTransferData/getTransferData.utils.ts @@ -99,7 +99,7 @@ export function getMrlMin({ export interface BuildTransferParams { asset: AssetAmount; destinationAddress: string; - destinationFee: AssetAmount; + feeAsset: AssetAmount; route: AssetRoute; sourceAddress: string; } @@ -124,7 +124,7 @@ export async function buildTransfer(params: BuildTransferParams) { export async function getMrlBuilderParams({ asset, destinationAddress, - destinationFee, + feeAsset, route, sourceAddress, }: BuildTransferParams): Promise { @@ -150,7 +150,7 @@ export async function getMrlBuilderParams({ destination, destinationAddress, destinationApi, - fee: destinationFee, + fee: feeAsset, isAutomatic: route.mrl.isAutomaticPossible, moonApi, moonAsset: moonChain.nativeAsset, diff --git a/packages/mrl/src/services/wormhole/WormholeWagmiSigner.ts b/packages/mrl/src/services/wormhole/WormholeWagmiSigner.ts index 1c536c9e..cb4109f5 100644 --- a/packages/mrl/src/services/wormhole/WormholeWagmiSigner.ts +++ b/packages/mrl/src/services/wormhole/WormholeWagmiSigner.ts @@ -1,4 +1,3 @@ -import { wormholeFactory } from '@moonbeam-network/xcm-builder'; import type { EvmSigner } from '@moonbeam-network/xcm-sdk'; import type { EvmChain, EvmParachain } from '@moonbeam-network/xcm-types'; import type { @@ -7,7 +6,6 @@ import type { SignAndSendSigner, SignedTx, UnsignedTransaction, - Wormhole, } from '@wormhole-foundation/sdk-connect'; import { http, @@ -28,8 +26,6 @@ export class WormholeWagmiSigner< readonly #publicClient: PublicClient; - readonly #wh: Wormhole; - constructor(chain: EvmChain | EvmParachain, signer: EvmSigner) { this.#chain = chain; this.#signer = signer; @@ -37,12 +33,11 @@ export class WormholeWagmiSigner< chain: chain.getViemChain(), transport: http(), }); - this.#wh = wormholeFactory(chain) as Wormhole; } chain(): C { // biome-ignore lint/suspicious/noExplicitAny: need to fix types - return this.#wh.getChain(this.#chain.getWormholeName()) as any as C; + return this.#chain.getWormholeName() as any as C; } address(): Address { @@ -61,13 +56,17 @@ export class WormholeWagmiSigner< transactions: UnsignedTransaction[], ): Promise { const signed = []; + const signerChainID = await this.#signer.getChainId(); let nonce = await this.#publicClient.getTransactionCount({ address: this.address(), }); for (const tx of transactions) { - const { transaction, parallelizable } = tx; + const { transaction, description, parallelizable } = tx; + console.log( + `Signing: ${description} for ${this.address()}, chainId: ${signerChainID}`, + ); const signedTx = await this.#signer.sendTransaction({ ...transaction,