From 75314611a7d0ba575a337acbedf58399007d10fa Mon Sep 17 00:00:00 2001 From: mmaurello <93129175+mmaurello@users.noreply.github.com> Date: Thu, 3 Oct 2024 12:36:21 +0200 Subject: [PATCH] Allow Parachains/Moonbase to Evm Routes (#361) * add moonbase to fantom routes and fix configuration accordingly to consider moonChain as source * add peaqAlphanet routes * fix moonAsset retrieval * fix asset in relayer fee * remove comment * udpate snap --- .vscode/settings.json | 1 + cspell.config.cjs | 1 + mkdocs/docs/reference/interfaces.md | 2 +- .../__snapshots__/polkadotXcm.test.ts.snap | 8 +- .../extrinsic/polkadotXcm/polkadotXcm.ts | 56 +++++++------- .../providers/wormhole/wormhole/wormhole.ts | 19 +++-- .../config/src/mrl-configs/fantomTestnet.ts | 2 +- packages/config/src/mrl-configs/index.ts | 8 +- .../config/src/mrl-configs/moonbaseAlpha.ts | 72 ++++++++++++++++++ .../config/src/mrl-configs/peaqAlphanet.ts | 76 +++++++++++++++++++ .../src/getTransferData/getMoonChainData.ts | 4 +- .../mrl/src/getTransferData/getSourceData.ts | 20 ++--- .../getTransferData/getTransferData.utils.ts | 5 +- 13 files changed, 217 insertions(+), 57 deletions(-) create mode 100644 packages/config/src/mrl-configs/moonbaseAlpha.ts create mode 100644 packages/config/src/mrl-configs/peaqAlphanet.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 2cafdf41..09a2de14 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,6 +17,7 @@ "darwiniacrab", "derivated", "dwellir", + "extrinsics", "foucoco", "ftmwh", "fullnode", diff --git a/cspell.config.cjs b/cspell.config.cjs index 4942df20..38176103 100644 --- a/cspell.config.cjs +++ b/cspell.config.cjs @@ -11,6 +11,7 @@ module.exports = { '**/*.ts.snap', '**/*.sol', 'pnpm-lock.yaml', + 'CHANGELOG.md', ], words: vscodeConfig['cSpell.words'], }; diff --git a/mkdocs/docs/reference/interfaces.md b/mkdocs/docs/reference/interfaces.md index 518addb0..5bd5d6e9 100644 --- a/mkdocs/docs/reference/interfaces.md +++ b/mkdocs/docs/reference/interfaces.md @@ -829,7 +829,7 @@ Defines the fees for a particular asset on the destination chain. ```js title="Example" -// The Desintation Fee Asset Config object +// The Destination Fee Asset Config object // For configuring USDT to be sent from // the Polkadot Asset Hub to Moonbeam { diff --git a/packages/builder/src/mrl/providers/wormhole/extrinsic/polkadotXcm/__snapshots__/polkadotXcm.test.ts.snap b/packages/builder/src/mrl/providers/wormhole/extrinsic/polkadotXcm/__snapshots__/polkadotXcm.test.ts.snap index 0b1f33a1..acd4285e 100644 --- a/packages/builder/src/mrl/providers/wormhole/extrinsic/polkadotXcm/__snapshots__/polkadotXcm.test.ts.snap +++ b/packages/builder/src/mrl/providers/wormhole/extrinsic/polkadotXcm/__snapshots__/polkadotXcm.test.ts.snap @@ -10,9 +10,11 @@ ExtrinsicConfig { exports[`polkadotXcm > send > should get correct arguments 1`] = ` [ - "xTokens.transfer => RESULT", - "xTokens.transfer => RESULT", - "polkadotXcm.send => RESULT", + [ + "xTokens.transfer => RESULT", + "xTokens.transfer => RESULT", + "polkadotXcm.send => RESULT", + ], ] `; 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 c1945ad0..a7e7cee7 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,4 @@ -import { - type AnyParachain, - AssetAmount, - EvmParachain, -} from '@moonbeam-network/xcm-types'; +import { type AnyParachain, AssetAmount } from '@moonbeam-network/xcm-types'; import { getMultilocationDerivedAddresses } from '@moonbeam-network/xcm-utils'; import { ExtrinsicBuilder } from '../../../../../extrinsic/ExtrinsicBuilder'; import { ExtrinsicConfig } from '../../../../../types/substrate/ExtrinsicConfig'; @@ -23,6 +19,7 @@ export function polkadotXcm() { fee, moonAsset, moonChain, + moonApi, source, sourceAddress, sourceApi, @@ -32,12 +29,6 @@ export function polkadotXcm() { throw new Error('Destination chain does not have a wormhole name'); } - if (!EvmParachain.isAnyParachain(destination)) { - throw new Error( - `Destination ${destination.name} is not a Parachain or EvmParachain`, - ); - } - if (!transact) { throw new Error('Transact params are required'); } @@ -46,16 +37,23 @@ export function polkadotXcm() { throw new Error('Source API needs to be defined'); } + const { address20: computedOriginAccount } = + getMultilocationDerivedAddresses({ + address: sourceAddress, + paraId: moonChain.parachainId, + isParents: true, + }); + const { transfer } = sourceApi.tx.xTokens; const builder = ExtrinsicBuilder().xTokens().transfer(); const assetTransferTx = transfer( - builder + ...builder .build({ asset, - destination, - destinationAddress, - destinationApi, + 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. @@ -71,14 +69,17 @@ export function polkadotXcm() { * Also we need to move fees to AssetRoute. */ const feeAssetTransferTx = transfer( - builder + ...builder .build({ - asset: AssetAmount.fromChainAsset(moonAsset, { - amount: CROSS_CHAIN_FEE + BUY_EXECUTION_FEE, - }), - destination, - destinationAddress, - destinationApi, + 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, @@ -87,12 +88,6 @@ export function polkadotXcm() { .getArgs(transfer), ); - const { address20 } = getMultilocationDerivedAddresses({ - address: sourceAddress, - paraId: moonChain.parachainId, - isParents: true, - }); - const send = sourceApi.tx.polkadotXcm.send( { V3: { @@ -158,7 +153,7 @@ export function polkadotXcm() { beneficiary: { parents: 0, interior: { - X1: { AccountKey20: { key: address20 } }, + X1: { AccountKey20: { key: computedOriginAccount } }, }, }, }, @@ -169,10 +164,11 @@ export function polkadotXcm() { }, ); + // TODO add here ability to only send the remote execution (only `send`) return new ExtrinsicConfig({ module: 'utility', func: 'batchAll', - getArgs: () => [assetTransferTx, feeAssetTransferTx, send], + getArgs: () => [[assetTransferTx, feeAssetTransferTx, send]], }); }, }), diff --git a/packages/builder/src/mrl/providers/wormhole/wormhole/wormhole.ts b/packages/builder/src/mrl/providers/wormhole/wormhole/wormhole.ts index ca156dd9..c574982c 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 { EvmParachain } from '@moonbeam-network/xcm-types'; +import { EvmChain, EvmParachain } from '@moonbeam-network/xcm-types'; import { evmToAddress } from '@polkadot/util-crypto/address'; import { Wormhole } from '@wormhole-foundation/sdk-connect'; import { getExtrinsicAccount } from '../../../../extrinsic/ExtrinsicBuilder.utils'; @@ -27,6 +27,7 @@ export function wormhole() { }) => { const isNativeAsset = asset.isSame(source.nativeAsset); const isDestinationMoonChain = destination.isEqual(moonChain); + const isDestinationEvmChain = EvmChain.is(destination); const tokenAddress = isNativeAsset ? 'native' : asset.address; if (!tokenAddress) { @@ -35,15 +36,19 @@ export function wormhole() { const wh = wormholeFactory(source); const whSource = wh.getChain(source.getWormholeName()); - const whMoonChain = wh.getChain(moonChain.getWormholeName()); + const whDestination = isDestinationEvmChain + ? wh.getChain(destination.getWormholeName()) + : wh.getChain(moonChain.getWormholeName()); const whAsset = Wormhole.tokenId(whSource.chain, tokenAddress); const whSourceAddress = Wormhole.chainAddress( whSource.chain, sourceAddress, ); - const whMoonChainAddress = Wormhole.chainAddress( - whMoonChain.chain, - isDestinationMoonChain ? destinationAddress : GMP_CONTRACT_ADDRESS, + const whDestinationAddress = Wormhole.chainAddress( + whDestination.chain, + isDestinationMoonChain || isDestinationEvmChain + ? destinationAddress + : GMP_CONTRACT_ADDRESS, ); return new WormholeConfig({ @@ -51,9 +56,9 @@ export function wormhole() { whAsset, asset.amount, whSourceAddress, - whMoonChainAddress, + whDestinationAddress, isAutomatic, - isDestinationMoonChain + isDestinationMoonChain || isDestinationEvmChain ? undefined : getPayload({ destination, destinationAddress, moonApi }), ], diff --git a/packages/config/src/mrl-configs/fantomTestnet.ts b/packages/config/src/mrl-configs/fantomTestnet.ts index 803b26ff..4269db98 100644 --- a/packages/config/src/mrl-configs/fantomTestnet.ts +++ b/packages/config/src/mrl-configs/fantomTestnet.ts @@ -56,7 +56,7 @@ export const fantomTestnetRoutes = new ChainRoutes({ }, }, mrl: { - isAutomatic: false, + isAutomatic: true, transfer: MrlBuilder().wormhole().wormhole().tokenTransfer(), moonChain: { asset: ftmwh, diff --git a/packages/config/src/mrl-configs/index.ts b/packages/config/src/mrl-configs/index.ts index b7f3a12b..4636267a 100644 --- a/packages/config/src/mrl-configs/index.ts +++ b/packages/config/src/mrl-configs/index.ts @@ -1,7 +1,13 @@ import type { ChainRoutes } from '../types/ChainRoutes'; import { fantomTestnetRoutes } from './fantomTestnet'; +import { moonbaseAlphaRoutes } from './moonbaseAlpha'; +import { peaqAlphanetRoutes } from './peaqAlphanet'; -export const mrlRoutesList: ChainRoutes[] = [fantomTestnetRoutes]; +export const mrlRoutesList: ChainRoutes[] = [ + fantomTestnetRoutes, + moonbaseAlphaRoutes, + peaqAlphanetRoutes, +]; export const mrlRoutesMap = new Map( mrlRoutesList.map((config) => [config.chain.key, config]), diff --git a/packages/config/src/mrl-configs/moonbaseAlpha.ts b/packages/config/src/mrl-configs/moonbaseAlpha.ts new file mode 100644 index 00000000..ac238499 --- /dev/null +++ b/packages/config/src/mrl-configs/moonbaseAlpha.ts @@ -0,0 +1,72 @@ +import { BalanceBuilder, MrlBuilder } from '@moonbeam-network/xcm-builder'; +import { dev, ftm, ftmwh } from '../assets'; +import { fantomTestnet, moonbaseAlpha } from '../chains'; +import { ChainRoutes } from '../types/ChainRoutes'; + +export const moonbaseAlphaRoutes = new ChainRoutes({ + chain: moonbaseAlpha, + routes: [ + { + source: { + asset: ftmwh, + balance: BalanceBuilder().evm().erc20(), + destinationFee: { + asset: ftmwh, + balance: BalanceBuilder().evm().erc20(), + }, + }, + destination: { + asset: ftm, + chain: fantomTestnet, + balance: BalanceBuilder().evm().native(), + fee: { + asset: ftm, + amount: 0, + }, + }, + mrl: { + isAutomatic: true, + transfer: MrlBuilder().wormhole().wormhole().tokenTransfer(), + moonChain: { + asset: ftmwh, + fee: { + asset: dev, + amount: 0.1, // TODO not really, it would be the source fee as source is moonChain + balance: BalanceBuilder().substrate().system().account(), + }, + }, + }, + }, + { + source: { + asset: dev, + balance: BalanceBuilder().substrate().system().account(), + destinationFee: { + asset: dev, + balance: BalanceBuilder().substrate().system().account(), + }, + }, + destination: { + asset: dev, + chain: fantomTestnet, + balance: BalanceBuilder().evm().erc20(), + fee: { + asset: dev, + amount: 0, + }, + }, + mrl: { + isAutomatic: true, + transfer: MrlBuilder().wormhole().wormhole().tokenTransfer(), + moonChain: { + asset: dev, + fee: { + asset: dev, + amount: 0.1, + balance: BalanceBuilder().substrate().system().account(), + }, + }, + }, + }, + ], +}); diff --git a/packages/config/src/mrl-configs/peaqAlphanet.ts b/packages/config/src/mrl-configs/peaqAlphanet.ts new file mode 100644 index 00000000..4328fd21 --- /dev/null +++ b/packages/config/src/mrl-configs/peaqAlphanet.ts @@ -0,0 +1,76 @@ +import { BalanceBuilder, MrlBuilder } from '@moonbeam-network/xcm-builder'; +import { agng, dev, ftm, ftmwh } from '../assets'; +import { fantomTestnet, peaqAlphanet } from '../chains'; +import { ChainRoutes } from '../types/ChainRoutes'; + +export const peaqAlphanetRoutes = new ChainRoutes({ + chain: peaqAlphanet, + routes: [ + { + source: { + asset: ftmwh, + balance: BalanceBuilder().substrate().assets().account(), + destinationFee: { + asset: ftmwh, + balance: BalanceBuilder().substrate().assets().account(), + }, + fee: { + asset: agng, + balance: BalanceBuilder().substrate().system().account(), + }, + }, + destination: { + asset: ftm, + chain: fantomTestnet, + balance: BalanceBuilder().evm().native(), + fee: { + asset: ftm, + amount: 0, + }, + }, + mrl: { + isAutomatic: true, + transfer: MrlBuilder().wormhole().extrinsic().polkadotXcm().send(), + moonChain: { + asset: ftmwh, + fee: { + asset: dev, + amount: 0.1, + balance: BalanceBuilder().substrate().system().account(), + }, + }, + }, + }, + { + source: { + asset: agng, + balance: BalanceBuilder().substrate().system().account(), + destinationFee: { + asset: ftmwh, + balance: BalanceBuilder().substrate().assets().account(), + }, + }, + destination: { + asset: ftm, + chain: fantomTestnet, + balance: BalanceBuilder().evm().native(), + fee: { + asset: ftm, + amount: 0, + }, + }, + mrl: { + isAutomatic: true, + transfer: MrlBuilder().wormhole().extrinsic().polkadotXcm().send(), + moonChain: { + asset: ftmwh, + fee: { + asset: dev, + amount: 0.1, + balance: BalanceBuilder().substrate().system().account(), + }, + }, + }, + }, + ], +}); diff --git a/packages/mrl/src/getTransferData/getMoonChainData.ts b/packages/mrl/src/getTransferData/getMoonChainData.ts index 529258a8..0bcc37cc 100644 --- a/packages/mrl/src/getTransferData/getMoonChainData.ts +++ b/packages/mrl/src/getTransferData/getMoonChainData.ts @@ -38,8 +38,8 @@ export async function getMoonChainData({ const fee = await getDestinationFee({ address: sourceAddress, // TODO not correct - asset: route.destination.fee.asset, - chain: moonChain, + asset: route.mrl.moonChain.fee.asset, + destination: moonChain, fee: route.mrl.moonChain.fee.amount, transferAsset: route.source.asset, }); diff --git a/packages/mrl/src/getTransferData/getSourceData.ts b/packages/mrl/src/getTransferData/getSourceData.ts index 4c7f4160..e2dc3bfc 100644 --- a/packages/mrl/src/getTransferData/getSourceData.ts +++ b/packages/mrl/src/getTransferData/getSourceData.ts @@ -14,12 +14,12 @@ import { getExtrinsicFee, getMax, } from '@moonbeam-network/xcm-sdk'; -import type { - AnyChain, - AnyParachain, +import { + type AnyChain, + type AnyParachain, AssetAmount, - EvmChain, - EvmParachain, + type EvmChain, + type EvmParachain, } from '@moonbeam-network/xcm-types'; import { WormholeService } from '../services/wormhole'; import { buildTransfer } from './getTransferData.utils'; @@ -137,11 +137,13 @@ export async function getFee({ const wh = WormholeService.create(chain as EvmChain | EvmParachain); const fee = await wh.getFee(transfer); - console.log('fee', fee); + console.log('fee in getFee.WormholeConfig', fee); - // TODO: finish - // biome-ignore lint/suspicious/noExplicitAny: - return {} as any; + // TODO technically this is not the fee on source chain, it's relayer fee + // source fee should be the fee paid to send the message in polkadot to eth or to sign the transaction in eth to polkadot + return AssetAmount.fromChainAsset(chain.getChainAsset(balance), { + amount: fee.relayFee?.amount || 0n, + }); } if (ContractConfig.is(transfer)) { diff --git a/packages/mrl/src/getTransferData/getTransferData.utils.ts b/packages/mrl/src/getTransferData/getTransferData.utils.ts index a4568260..a76bed5b 100644 --- a/packages/mrl/src/getTransferData/getTransferData.utils.ts +++ b/packages/mrl/src/getTransferData/getTransferData.utils.ts @@ -36,7 +36,7 @@ import { import type { MoonChainTransferData } from '../mrl.interfaces'; const MOON_CHAIN_AUTOMATIC_GAS_ESTIMATION = { - [moonbeam.key]: 657226n, + [moonbeam.key]: 1273110n, [moonbaseAlpha.key]: 1271922n, }; @@ -58,7 +58,6 @@ export function getMoonChainFeeValueOnSource({ const isSameAssetPayingMoonChainFee = sourceData.balance.isSame( moonChainData.fee, ); - return !isDestinationMoonChain && isSourceParachain && isSameAssetPayingMoonChainFee @@ -125,7 +124,7 @@ export async function buildTransfer({ destinationAddress, destinationApi, fee: destinationFee, - isAutomatic: route.mrl.isAutomatic, + isAutomatic: route.mrl.isAutomatic, // TODO moonApi, moonAsset: moonChain.nativeAsset, moonChain,