From 80dc781700f432c25bfeb5a45279e9fcd458595f Mon Sep 17 00:00:00 2001 From: JayJay1024 Date: Mon, 25 Dec 2023 18:54:04 +0800 Subject: [PATCH] Msgline margin withdraw --- src/bridges/lnbridge-default.ts | 63 ++++++++++++++++++- .../modals/relayer-manage-modal.tsx | 31 ++++++--- src/config/gql.ts | 2 + src/providers/relayer-provider.tsx | 16 ++++- src/types/bridge.ts | 10 +++ src/types/graphql.ts | 2 + 6 files changed, 112 insertions(+), 12 deletions(-) diff --git a/src/bridges/lnbridge-default.ts b/src/bridges/lnbridge-default.ts index cfa5a7bbc..210d8148d 100644 --- a/src/bridges/lnbridge-default.ts +++ b/src/bridges/lnbridge-default.ts @@ -1,8 +1,9 @@ -import { Address, TransactionReceipt, bytesToHex } from "viem"; +import { Address, TransactionReceipt, bytesToHex, encodeFunctionData } from "viem"; import { LnBridgeBase } from "./lnbridge-base"; import { ChainID } from "@/types/chain"; import { isProduction } from "@/utils/env"; -import { BridgeConstructorArgs, TransferOptions } from "@/types/bridge"; +import { BridgeConstructorArgs, GetWithdrawFeeArgs, TransferOptions } from "@/types/bridge"; +import { fetchMsglineFeeAndParams } from "@/utils"; export class LnBridgeDefault extends LnBridgeBase { constructor(args: BridgeConstructorArgs) { @@ -145,7 +146,7 @@ export class LnBridgeDefault extends LnBridgeBase { } } - async getWithdrawFee() { + private async _getLayerzeroWithdrawFee(_args: GetWithdrawFeeArgs) { if (this.contract && this.sourceNativeToken && this.targetChain && this.sourcePublicClient) { const bridgeAbi = (await import(`../abi/lnbridgev20-default`)).default; const accessAbi = (await import(`../abi/lnaccess-controller`)).default; @@ -168,6 +169,62 @@ export class LnBridgeDefault extends LnBridgeBase { } } + private async _getMsglineWithdrawFee(args: GetWithdrawFeeArgs) { + const sourceMessager = this.sourceChain?.messager?.msgline; + const targetMessager = this.targetChain?.messager?.msgline; + + if ( + sourceMessager && + targetMessager && + this.sourceNativeToken && + this.sourceToken && + this.targetToken && + args.transferId && + args.withdrawNonce && + args.sender && + args.relayer + ) { + const message = encodeFunctionData({ + abi: (await import(`../abi/lnbridgev20-default`)).default, + functionName: "withdraw", + args: [ + BigInt(this.sourceChain.id), + args.transferId, + BigInt(args.withdrawNonce), + args.relayer, + this.sourceToken.address, + this.targetToken.address, + args.amount, + ], + }); + + const payload = encodeFunctionData({ + abi: (await import("@/abi/msgline-messager")).default, + functionName: "receiveMessage", + args: [BigInt(this.sourceChain.id), sourceMessager, targetMessager, message], + }); + + const feeAndParams = await fetchMsglineFeeAndParams( + this.sourceChain.id, + this.targetChain.id, + sourceMessager, + targetMessager, + args.sender, + payload, + ); + + return feeAndParams && { value: feeAndParams.fee, token: this.sourceNativeToken }; + } + } + + async getWithdrawFee(args: GetWithdrawFeeArgs) { + if (args.messageChannel === "layerzero") { + return this._getLayerzeroWithdrawFee(args); + } else if (args.messageChannel === "msgline") { + return this._getMsglineWithdrawFee(args); + } + } + async withdrawMargin(recipient: Address, amount: bigint, fee: bigint) { await this.validateNetwork("source"); diff --git a/src/components/modals/relayer-manage-modal.tsx b/src/components/modals/relayer-manage-modal.tsx index 60f6bc8de..25d2c1999 100644 --- a/src/components/modals/relayer-manage-modal.tsx +++ b/src/components/modals/relayer-manage-modal.tsx @@ -5,7 +5,7 @@ import SegmentedTabs, { SegmentedTabsProps } from "@/ui/segmented-tabs"; import { formatBalance, formatFeeRate, getChainConfig, notifyError } from "@/utils"; import { useApolloClient } from "@apollo/client"; import dynamic from "next/dynamic"; -import { PropsWithChildren, useEffect, useMemo, useState } from "react"; +import { PropsWithChildren, useDeferredValue, useEffect, useMemo, useState } from "react"; import { useAccount, useNetwork, useSwitchNetwork } from "wagmi"; import { Subscription, from } from "rxjs"; import { TransactionReceipt } from "viem"; @@ -40,6 +40,7 @@ export default function RelayerManageModal({ relayerInfo, isOpen, onClose, onSuc margin, baseFee, feeRate, + withdrawAmount, setMargin, setBaseFee, setFeeRate, @@ -48,6 +49,7 @@ export default function RelayerManageModal({ relayerInfo, isOpen, onClose, onSuc setSourceToken, setBridgeCategory, setFeeAndRate, + setWithdrawAmount, depositMargin, updateFeeAndMargin, withdrawMargin, @@ -59,11 +61,10 @@ export default function RelayerManageModal({ relayerInfo, isOpen, onClose, onSuc const [height, setHeight] = useState(); const [busy, setBusy] = useState(false); const [withdrawFee, setWithdrawFee] = useState<{ value: bigint; token: Token }>(); - const [depositAmount, setDepositAmount] = useState>({ input: "", valid: true, value: 0n }); - const [withdrawAmount, setWithdrawAmount] = useState>({ input: "", valid: true, value: 0n }); const [baseFeeInput, setBaseFeeInput] = useState>({ input: "", valid: true, value: 0n }); const [feeRateInput, setFeeRateInput] = useState>({ input: "", valid: true, value: 0 }); + const deferredWithdrawAmount = useDeferredValue(withdrawAmount); const { switchNetwork } = useSwitchNetwork(); const { chain } = useNetwork(); @@ -157,12 +158,22 @@ export default function RelayerManageModal({ relayerInfo, isOpen, onClose, onSuc setSourceChain, setTargetChain, setSourceToken, + setWithdrawAmount, ]); useEffect(() => { let sub$$: Subscription | undefined; - if (defaultBridge && relayerInfo?.messageChannel === "layerzero") { - sub$$ = from(defaultBridge.getWithdrawFee()).subscribe({ + if (defaultBridge && (relayerInfo?.messageChannel === "layerzero" || relayerInfo?.messageChannel === "msgline")) { + sub$$ = from( + defaultBridge.getWithdrawFee({ + amount: deferredWithdrawAmount.value, + sender: address, + relayer: relayerInfo.relayer, + transferId: relayerInfo.lastTransferId, + withdrawNonce: relayerInfo.withdrawNonce, + messageChannel: relayerInfo.messageChannel, + }), + ).subscribe({ next: setWithdrawFee, error: (err) => { console.error(err); @@ -173,7 +184,7 @@ export default function RelayerManageModal({ relayerInfo, isOpen, onClose, onSuc setWithdrawFee(undefined); } return () => sub$$?.unsubscribe(); - }, [defaultBridge, relayerInfo]); + }, [defaultBridge, relayerInfo, address, deferredWithdrawAmount]); return ( - Powered by LayerZero & Helix + + {relayerInfo?.messageChannel === "layerzero" + ? "Powered by LayerZero & Helix" + : "Powered by Msgport & Helix"} + ) : (
@@ -388,7 +403,7 @@ export default function RelayerManageModal({ relayerInfo, isOpen, onClose, onSuc
), - disabled: relayerInfo?.messageChannel !== "layerzero", + disabled: !(relayerInfo?.messageChannel === "layerzero" || relayerInfo?.messageChannel === "msgline"), }, ]} activeKey={activeKey} diff --git a/src/config/gql.ts b/src/config/gql.ts index 061fd11f0..42d9c0b63 100644 --- a/src/config/gql.ts +++ b/src/config/gql.ts @@ -130,6 +130,8 @@ export const GQL_QUERY_LNV20_RELAY_INFOS = gql` profit heartbeatTimestamp messageChannel + lastTransferId + withdrawNonce } } } diff --git a/src/providers/relayer-provider.tsx b/src/providers/relayer-provider.tsx index 3aac33bee..befeafc29 100644 --- a/src/providers/relayer-provider.tsx +++ b/src/providers/relayer-provider.tsx @@ -1,7 +1,14 @@ "use client"; import { BaseBridge, LnBridgeDefault, LnBridgeOpposite } from "@/bridges"; -import { BridgeCategory, ChainConfig, CheckLnBridgeExistReqParams, CheckLnBridgeExistResData, Token } from "@/types"; +import { + BridgeCategory, + ChainConfig, + CheckLnBridgeExistReqParams, + CheckLnBridgeExistResData, + InputValue, + Token, +} from "@/types"; import { getAvailableTargetTokens, notifyError, notifyTransaction } from "@/utils"; import { Dispatch, @@ -34,6 +41,7 @@ interface RelayerCtx { bridgeCategory: BridgeCategory | undefined; defaultBridge: LnBridgeDefault | undefined; oppositeBridge: LnBridgeOpposite | undefined; + withdrawAmount: InputValue; setMargin: Dispatch>; setBaseFee: Dispatch>; @@ -46,6 +54,7 @@ interface RelayerCtx { setTargetChain: Dispatch>; setSourceToken: Dispatch>; setBridgeCategory: Dispatch>; + setWithdrawAmount: Dispatch>>; sourceApprove: ( owner: Address, @@ -110,6 +119,7 @@ const defaultValue: RelayerCtx = { bridgeCategory: undefined, defaultBridge: undefined, oppositeBridge: undefined, + withdrawAmount: { input: "", valid: true, value: 0n }, setMargin: () => undefined, setBaseFee: () => undefined, @@ -122,6 +132,7 @@ const defaultValue: RelayerCtx = { setTargetChain: () => undefined, setSourceToken: () => undefined, setBridgeCategory: () => undefined, + setWithdrawAmount: () => undefined, sourceApprove: async () => undefined, targetApprove: async () => undefined, @@ -146,6 +157,7 @@ export default function RelayerProvider({ children }: PropsWithChildren const [targetChain, setTargetChain] = useState(defaultValue.targetChain); const [sourceToken, setSourceToken] = useState(defaultValue.sourceToken); const [bridgeCategory, setBridgeCategory] = useState(defaultValue.bridgeCategory); + const [withdrawAmount, setWithdrawAmount] = useState(defaultValue.withdrawAmount); const { data: walletClient } = useWalletClient(); const publicClient = usePublicClient(); @@ -363,6 +375,7 @@ export default function RelayerProvider({ children }: PropsWithChildren bridgeCategory, defaultBridge, oppositeBridge, + withdrawAmount, setMargin, setBaseFee, @@ -375,6 +388,7 @@ export default function RelayerProvider({ children }: PropsWithChildren setTargetChain, setSourceToken, setBridgeCategory, + setWithdrawAmount, sourceApprove, targetApprove, diff --git a/src/types/bridge.ts b/src/types/bridge.ts index ea791de2c..bc2a1bcc3 100644 --- a/src/types/bridge.ts +++ b/src/types/bridge.ts @@ -2,6 +2,7 @@ import { Address, Hex } from "viem"; import { PublicClient, WalletClient } from "wagmi"; import { ChainConfig } from "./chain"; import { Token } from "./token"; +import { MessageChannel } from "."; /** * lpbridge-darwinia-dvm etc. are named from graphql indexer. @@ -60,3 +61,12 @@ export interface TransferOptions { withdrawNonce?: bigint; depositedMargin?: bigint; } + +export interface GetWithdrawFeeArgs { + amount: bigint; + sender?: Address; + relayer?: Address; + transferId?: Hex | null; + withdrawNonce?: string | null; + messageChannel?: MessageChannel | null; +} diff --git a/src/types/graphql.ts b/src/types/graphql.ts index be8db47e2..4cc377b73 100644 --- a/src/types/graphql.ts +++ b/src/types/graphql.ts @@ -133,6 +133,8 @@ export type Lnv20RelayerOverview = Pick< | "profit" | "heartbeatTimestamp" | "messageChannel" + | "lastTransferId" + | "withdrawNonce" >; export interface QueryLnV20RelayInfosReqParams {