Skip to content

Commit

Permalink
Implement 'Batch' builder for EVM Parachains (#371)
Browse files Browse the repository at this point in the history
* update mrl example

* implement Batch contract builder for sending assets and xcm message with remote execution

* fix code

* update lock file

* fix lint problem

* fix linter issues from sovereign account balance checking implementations

* fix sovereign account balance checking implementation
  • Loading branch information
mmaurello authored Oct 17, 2024
1 parent 8f058ac commit 07e1499
Show file tree
Hide file tree
Showing 22 changed files with 1,097 additions and 237 deletions.
119 changes: 112 additions & 7 deletions examples/mrl-simple/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { Mrl } from '@moonbeam-network/mrl';
import { fantomTestnet, ftm, peaqAlphanet } from '@moonbeam-network/xcm-config';
import {
agng,
dev,
fantomTestnet,
ftm,
ftmwh,
moonbaseAlpha,
moonbaseBeta,
peaqAlphanet,
peaqEvmAlphanet,
} from '@moonbeam-network/xcm-config';
import type { Asset } from '@moonbeam-network/xcm-types';
import { Keyring } from '@polkadot/api';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { http, type Address, createWalletClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { moonbaseAlpha as moonbaseAlphaViem } from 'viem/chains';

// disable unnecessary warning logs
console.warn = () => null;
Expand All @@ -20,11 +32,6 @@ if (!EVM_PRIVATE_KEY || !POLKADOT_PRIVATE_KEY) {
// EVM Signer ===========================================================

const account = privateKeyToAccount(EVM_PRIVATE_KEY as Address);
const walletClient = createWalletClient({
account,
chain: fantomTestnet.getViemChain(),
transport: http(),
});

console.log(`\nEVM address: ${account.address}`);

Expand All @@ -47,14 +54,112 @@ main()
.finally(() => process.exit());

async function main() {
// await fromFantomToPeaq(ftm, 0.011);
// await fromFantomToMoonbase(ftm, 0.01);
// await fromMoonbaseToFantom(dev, 0.01);
// await fromPeaqToFantom(ftmwh, 0.1);
await fromPeaqEvmToFantom(ftmwh, 0.12);
}

async function fromFantomToPeaq(asset: Asset, amount: number) {
const data = await Mrl()
.setSource(fantomTestnet)
.setDestination(peaqAlphanet)
.setAsset(ftm)
.setAsset(asset)
.setAddresses({
sourceAddress: account.address,
destinationAddress: pair.address,
});

console.log(data);

await data.transfer(amount, {
polkadotSigner: pair,
evmSigner: walletClient,
});
}

async function fromFantomToMoonbase(asset: Asset, amount: number) {
const walletClient = createWalletClient({
account,
chain: fantomTestnet.getViemChain(),
transport: http(),
});

const data = await Mrl()
.setSource(fantomTestnet)
.setDestination(moonbaseAlpha)
.setAsset(dev)
.setAddresses({
sourceAddress: account.address,
destinationAddress: account.address,
});

console.log(data);

await data.transfer(0.5, {
polkadotSigner: pair,
evmSigner: walletClient,
});
}

async function fromMoonbaseToFantom(asset: Asset, amount: number) {
const walletClient = createWalletClient({
account,
chain: moonbaseAlphaViem,
transport: http(),
});
const data = await Mrl()
.setSource(moonbaseAlpha)
.setDestination(fantomTestnet)
.setAsset(asset)
.setAddresses({
sourceAddress: account.address,
destinationAddress: account.address,
});

console.log(data);

await data.transfer(amount, {
polkadotSigner: pair,
evmSigner: walletClient,
});
}

async function fromPeaqToFantom(asset: Asset, amount: number) {
const data = await Mrl()
.setSource(peaqAlphanet)
.setDestination(fantomTestnet)
.setAsset(asset)
.setAddresses({
sourceAddress: pair.address,
destinationAddress: account.address,
});

console.log(data);

await data.transfer(amount, {
polkadotSigner: pair,
});
}

async function fromPeaqEvmToFantom(asset: Asset, amount: number) {
const walletClient = createWalletClient({
account,
chain: peaqEvmAlphanet.getViemChain(),
transport: http(),
});

const data = await Mrl()
.setSource(peaqEvmAlphanet)
.setDestination(fantomTestnet)
.setAsset(asset)
.setAddresses({
sourceAddress: account.address,
destinationAddress: account.address,
});

console.log(data);

await data.transfer(amount, { evmSigner: walletClient });
}
1 change: 1 addition & 0 deletions examples/mrl-simple/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"dependencies": {
"@moonbeam-network/mrl": "workspace:*",
"@moonbeam-network/xcm-config": "workspace:*",
"@moonbeam-network/xcm-types": "workspace:*",
"@moonbeam-network/xcm-utils": "workspace:*"
},
"devDependencies": {
Expand Down
29 changes: 29 additions & 0 deletions packages/builder/src/builder.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { type AnyParachain, EvmParachain } from '@moonbeam-network/xcm-types';
import { u8aToHex } from '@polkadot/util';
import { decodeAddress } from '@polkadot/util-crypto';
import type { Address } from 'viem';

export function getPrecompileDestinationInterior(
destination: AnyParachain,
address?: string,
): [Address, Address] | [Address] {
if (!address) {
return [`0x0000000${destination.parachainId.toString(16)}`];
}

/*
01: AccountId32
03: AccountKey20
https://docs.moonbeam.network/builders/interoperability/xcm/xc20/xtokens/#building-the-precompile-multilocation
*/
const accountType = EvmParachain.is(destination) ? '03' : '01';
const acc = `0x${accountType}${u8aToHex(
decodeAddress(address),
-1,
false,
)}00` as Address;

return destination.parachainId
? [`0x0000000${destination.parachainId.toString(16)}`, acc]
: [acc];
}
24 changes: 5 additions & 19 deletions packages/builder/src/contract/contracts/Xtokens/Xtokens.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { type AnyParachain, EvmParachain } from '@moonbeam-network/xcm-types';
import type { AnyParachain } from '@moonbeam-network/xcm-types';
import { formatAssetIdToERC20 } from '@moonbeam-network/xcm-utils';
import { u8aToHex } from '@polkadot/util';
import { decodeAddress, evmToAddress } from '@polkadot/util-crypto';
import { getPrecompileDestinationInterior } from '../../../builder.utils';
import { ContractConfig } from '../../../types/evm/ContractConfig';
import type { ContractConfigBuilder } from '../../ContractBuilder.interfaces';
import { XTOKENS_ABI } from './XtokensABI';
Expand Down Expand Up @@ -116,6 +117,7 @@ type DestinationMultilocation = [
),
];

// TODO test if this is needed
function getDestinationMultilocationForPrecompileDestination(
address: string,
destination: AnyParachain,
Expand Down Expand Up @@ -145,22 +147,6 @@ function getDestinationMultilocation(
address: string,
destination: AnyParachain,
): DestinationMultilocation {
/*
01: AccountId32
03: AccountKey20
https://docs.moonbeam.network/builders/interoperability/xcm/xc20/xtokens/#building-the-precompile-multilocation
*/
const accountType = EvmParachain.is(destination) ? '03' : '01';
const acc = `0x${accountType}${u8aToHex(
decodeAddress(address),
-1,
false,
)}00`;

return [
1,
destination.parachainId
? [`0x0000000${destination.parachainId.toString(16)}`, acc]
: [acc],
];
const interior = getPrecompileDestinationInterior(destination, address);
return [1, interior];
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export function polkadotXcm() {
}),
};
},
trasferAssets: () => {
transferAssets: () => {
const func = 'transferAssets';

return {
Expand Down
10 changes: 6 additions & 4 deletions packages/builder/src/extrinsic/pallets/xTokens/xTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export function xTokens() {
module: pallet,
func: 'transfer',
getArgs: (func) => {
const version = getExtrinsicArgumentVersion(func, 2);
const destIndex = 2;
const version = getExtrinsicArgumentVersion(func, destIndex);

return [
asset.getAssetId(),
Expand All @@ -29,6 +30,7 @@ export function xTokens() {
}),
transferMultiAsset: (originParachainId: number) => {
const funcName = 'transferMultiasset';
const destIndex = 1;

return {
here: (): ExtrinsicConfigBuilder => ({
Expand All @@ -37,7 +39,7 @@ export function xTokens() {
module: pallet,
func: funcName,
getArgs: (func) => {
const version = getExtrinsicArgumentVersion(func, 1);
const version = getExtrinsicArgumentVersion(func, destIndex);

return [
{
Expand Down Expand Up @@ -65,7 +67,7 @@ export function xTokens() {
module: pallet,
func: funcName,
getArgs: (func) => {
const version = getExtrinsicArgumentVersion(func, 1);
const version = getExtrinsicArgumentVersion(func, destIndex);

return [
{
Expand Down Expand Up @@ -97,7 +99,7 @@ export function xTokens() {
module: pallet,
func: funcName,
getArgs: (func) => {
const version = getExtrinsicArgumentVersion(func, 1);
const version = getExtrinsicArgumentVersion(func, destIndex);

return [
{
Expand Down
Loading

0 comments on commit 07e1499

Please sign in to comment.