Skip to content

Commit

Permalink
Add standalone redeeming capability (#381)
Browse files Browse the repository at this point in the history
* add AGNG route from Fantom to Peaq

* Add redeeming functionalities for Womrhole

* imporve dev mode

* try to fix dev

* Revert "try to fix dev"

This reverts commit 10a6c38.

* Revert "imporve dev mode"

This reverts commit 9d135c8.

* change u8 to hex calculation

* change getRedeemdData builder

---------

Co-authored-by: Richard Kenigs <[email protected]>
  • Loading branch information
mmaurello and Rihyx authored Oct 29, 2024
1 parent fc4e393 commit 4385bbe
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 15 deletions.
56 changes: 47 additions & 9 deletions examples/mrl-simple/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ 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';
import type { EvmSigner } from '../../packages/sdk/build';

// disable unnecessary warning logs
console.warn = () => null;
Expand Down Expand Up @@ -54,12 +55,46 @@ main()
.finally(() => process.exit());

async function main() {
const isAutomatic = true;
// await fromFantomToPeaq(ftm, 0.011, isAutomatic);
// await fromFantomToMoonbase(ftm, 0.01, isAutomatic);
const isAutomatic = false;

// await fromFantomToPeaq(agng, 10, isAutomatic);
// await fromFantomToMoonbase(dev, 1.23, isAutomatic);
// await fromMoonbaseToFantom(ftmwh, 0.01, isAutomatic);
await fromPeaqToFantom(agng, 20, isAutomatic);
// await fromPeaqToFantom(ftmwh, 0.0121, isAutomatic);
// await fromPeaqEvmToFantom(ftmwh, 1.5, isAutomatic);

await redeemInMoonbaseAlpha();
// await redeemInFantomTestnet();
}

async function redeemInMoonbaseAlpha() {
const txId =
'0x59e70ad73c57bce44cbb3e3308fc6a31d29ff0dcbb2957055b05025969545bed';
const walletClient = createWalletClient({
account,
chain: moonbaseAlphaViem,
transport: http(),
});

const data = await Mrl().getRedeemData({ txId, chain: moonbaseAlpha });
console.log('data', data);

await data.redeem(walletClient as EvmSigner);
}

async function redeemInFantomTestnet() {
const txId =
'0xa0032ff270885f7278a42d4d974fceab9e4feb039263db35b09beafe57bd6683';
const walletClient = createWalletClient({
account,
chain: fantomTestnet.getViemChain(),
transport: http(),
});

const data = await Mrl().getRedeemData({ txId, chain: fantomTestnet });
console.log('data', data);

await data.redeem(walletClient as EvmSigner);
}

async function fromFantomToPeaq(
Expand All @@ -86,7 +121,7 @@ async function fromFantomToPeaq(

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

Expand All @@ -112,10 +147,11 @@ async function fromFantomToMoonbase(

console.log(data);

await data.transfer(amount, isAutomatic, {
const hash = await data.transfer(amount, isAutomatic, {
polkadotSigner: pair,
evmSigner: walletClient,
evmSigner: walletClient as EvmSigner,
});
console.log('hash', hash);
}

async function fromMoonbaseToFantom(
Expand All @@ -141,7 +177,7 @@ async function fromMoonbaseToFantom(

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

Expand Down Expand Up @@ -188,5 +224,7 @@ async function fromPeaqEvmToFantom(

console.log(data);

await data.transfer(amount, isAutomatic, { evmSigner: walletClient });
await data.transfer(amount, isAutomatic, {
evmSigner: walletClient as EvmSigner,
});
}
9 changes: 9 additions & 0 deletions packages/builder/src/mrl/MrlBuilder.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export type MrlConfigBuilder = ConfigBuilder<
MrlBuilderParams
>;

export type MrlRedeemConfigBuilder = ConfigBuilder<
ContractConfig,
MrlRedeemBuilderParams
>;

export interface MrlBuilderParams extends BuilderParams<AnyChain> {
isAutomatic: boolean;
moonApi: ApiPromise;
Expand All @@ -24,6 +29,10 @@ export interface MrlBuilderParams extends BuilderParams<AnyChain> {
transact?: Transact;
}

export interface MrlRedeemBuilderParams {
bytes?: Uint8Array;
}

export interface Transact {
call: HexString;
txWeight: {
Expand Down
27 changes: 27 additions & 0 deletions packages/builder/src/mrl/providers/wormhole/contract/Gmp/Gmp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { u8aToHex } from '@polkadot/util';
import { ContractConfig } from '../../../../../contract';
import type { MrlRedeemConfigBuilder } from '../../../../MrlBuilder.interfaces';
import { GMP_ABI } from './GmpAbi';

const module = 'GMP';

export const GMP_CONTRACT_ADDRESS =
'0x0000000000000000000000000000000000000816';

export function Gmp() {
return {
wormholeTransferERC20: (): MrlRedeemConfigBuilder => ({
build: ({ bytes }) => {
const hex = u8aToHex(bytes);

return new ContractConfig({
address: GMP_CONTRACT_ADDRESS,
abi: GMP_ABI,
args: [hex],
func: 'wormholeTransferERC20',
module,
});
},
}),
};
}
15 changes: 15 additions & 0 deletions packages/builder/src/mrl/providers/wormhole/contract/Gmp/GmpAbi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const GMP_ABI = [
{
inputs: [
{
internalType: 'bytes',
name: 'vaa',
type: 'bytes',
},
],
name: 'wormholeTransferERC20',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
] as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Gmp';
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Batch } from './Batch';
import { Gmp } from './Gmp';
import { TokenBridge } from './TokenBridge';
import { TokenBridgeRelayer } from './TokenBridgeRelayer';

export function contract() {
return { Batch, TokenBridge, TokenBridgeRelayer };
return { Batch, Gmp, TokenBridge, TokenBridgeRelayer };
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import type {
MrlBuilderParams,
MrlConfigBuilder,
} from '../../../MrlBuilder.interfaces';
import { GMP_CONTRACT_ADDRESS } from '../contract/Gmp';
import { WormholeConfig } from './WormholeConfig';
import { wormholeFactory } from './wormholeFactory';

export const GMP_CONTRACT_ADDRESS =
'0x0000000000000000000000000000000000000816';

export function wormhole() {
return {
tokenTransfer: (): MrlConfigBuilder => ({
Expand Down
33 changes: 32 additions & 1 deletion packages/config/src/mrl-configs/fantomTestnet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BalanceBuilder, MrlBuilder } from '@moonbeam-network/xcm-builder';
import { dev, ftm, ftmwh } from '../assets';
import { agng, dev, ftm, ftmwh } from '../assets';
import {
fantomTestnet,
moonbaseAlpha,
Expand Down Expand Up @@ -42,6 +42,37 @@ export const fantomTestnetRoutes = new MrlChainRoutes({
},
},
},
{
source: {
asset: agng,
balance: BalanceBuilder().evm().erc20(),
destinationFee: {
asset: agng,
balance: BalanceBuilder().evm().erc20(),
},
},
destination: {
asset: agng,
chain: peaqAlphanet,
balance: BalanceBuilder().substrate().system().account(),
fee: {
asset: agng,
amount: 1,
},
},
mrl: {
isAutomaticPossible: false,
transfer: MrlBuilder().wormhole().wormhole().tokenTransfer(),
moonChain: {
asset: agng,
fee: {
asset: dev,
amount: 0.1,
balance: BalanceBuilder().substrate().system().account(),
},
},
},
},
{
source: {
asset: ftm,
Expand Down
49 changes: 49 additions & 0 deletions packages/mrl/src/getTransferData/getRedeemData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { type ContractConfig, MrlBuilder } from '@moonbeam-network/xcm-builder';
import { EvmService, type EvmSigner } from '@moonbeam-network/xcm-sdk';
import type { EvmChain, EvmParachain } from '@moonbeam-network/xcm-types';
import { WormholeService } from '../services/wormhole';

export interface WormholeRedeemParams {
txId: string;
chain: EvmChain | EvmParachain;
}

export async function getRedeemData({ txId, chain }: WormholeRedeemParams) {
// TODO this is just for wormhole
const wh = WormholeService.create(chain);

const vaa = await wh.getVaa(txId);
const tokenTransfer = await wh.getTokenTransfer(vaa, txId);

const isXcm = vaa.payloadName === 'TransferWithPayload';

return {
vaa,
tokenTransfer,
async redeem(signer: EvmSigner) {
const isComplete = await wh.isComplete(vaa, tokenTransfer);

if (isComplete) {
throw new Error('This transaction is already finalized in Wormhole');
}

if (isXcm) {
const bytes = await wh.getVaaBytes(vaa);

const contract = MrlBuilder()
.wormhole()
.contract()
.Gmp()
.wormholeTransferERC20()
.build({ bytes }) as ContractConfig;

const evm = EvmService.create(chain);
const hash = await evm.transfer(signer, contract);

return hash;
}

return await wh.completeTransfer(tokenTransfer, signer);
},
};
}
13 changes: 12 additions & 1 deletion packages/mrl/src/mrl.interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { SourceChainTransferData } from '@moonbeam-network/xcm-sdk';
import type {
EvmSigner,
SourceChainTransferData,
} 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 { TokenTransfer } from '@wormhole-foundation/sdk-connect';
import type { WalletClient } from 'viem';

export interface Signers {
Expand Down Expand Up @@ -41,3 +45,10 @@ export interface ChainTransferData {
fee: AssetAmount;
min: AssetAmount;
}

// TODO this is just for Wormhole
export type RedeemData = {
vaa: TokenTransfer.VAA;
tokenTransfer: TokenTransfer;
transfer(signer: EvmSigner): Promise<string[]>;
};
7 changes: 7 additions & 0 deletions packages/mrl/src/mrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import type {
AnyChain,
Ecosystem,
} from '@moonbeam-network/xcm-types';
import {
type WormholeRedeemParams,
getRedeemData,
} from './getTransferData/getRedeemData';
import { getTransferData } from './getTransferData/getTransferData';

const DEFAULT_SERVICE = new ConfigService({ routes: mrlRoutesMap });
Expand Down Expand Up @@ -56,5 +60,8 @@ export function Mrl(options?: MrlOptions) {
},
};
},
getRedeemData({ txId, chain }: WormholeRedeemParams) {
return getRedeemData({ txId, chain });
},
};
}
40 changes: 40 additions & 0 deletions packages/mrl/src/services/wormhole/WormholeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,44 @@ export class WormholeService {

return xfer.initiateTransfer(new WormholeWagmiSigner(this.chain, signer));
}

async getVaa(txId: string) {
return await TokenTransfer.getTransferVaa(this.#wh, txId);
}

async getVaaBytes(vaa: TokenTransfer.VAA) {
return (
(await this.#wh.getVaaBytes({
chain: vaa.emitterChain,
emitter: vaa.emitterAddress,
sequence: vaa.sequence,
})) || undefined
);
}

async getTokenTransfer(vaa: TokenTransfer.VAA, txId: string) {
return await TokenTransfer.from(this.#wh, {
chain: vaa.emitterChain,
txid: txId,
});
}

async isComplete(vaa: TokenTransfer.VAA, tokenTransfer: TokenTransfer) {
const isComplete = await TokenTransfer.isTransferComplete(
tokenTransfer.toChain,
vaa,
);
return isComplete;
}

async completeTransfer(
tokenTransfer: TokenTransfer,
signer: EvmSigner,
): Promise<string> {
const txIds = await tokenTransfer.completeTransfer(
new WormholeWagmiSigner(this.chain, signer),
);

return txIds[0];
}
}

0 comments on commit 4385bbe

Please sign in to comment.