From 6d289640d3f0f45228b0dd7df1248db4a7ba99e9 Mon Sep 17 00:00:00 2001 From: Jay Date: Wed, 2 Aug 2023 16:11:50 +0800 Subject: [PATCH] Support transfer from Goerli to ArbitrumGoerli through LnBridge (#499) * Config network * Support transfer from Goerli to ArbitrumGoerli with LnBridge * Fix unit tests --- packages/apps/__tests__/bridge.spec.ts | 2 +- packages/apps/__tests__/network.spec.ts | 14 +- packages/apps/bridges/bridges.ts | 2 + packages/apps/bridges/index.ts | 2 + .../l1tol2/ethereum-arbitrum/config/bridge.ts | 2 +- .../Arbitrum2EthereumLnBridge.tsx | 10 + .../Ethereum2ArbitrumLnBridge.tsx | 10 + .../config/abi/lnArbitrumL1Issuing.json | 671 ++++++++++++++++++ .../ethereum-arbitrum/config/bridge.ts | 28 + .../ethereum-arbitrum/config/index.ts | 1 + .../lnbridge/ethereum-arbitrum/index.ts | 2 + .../ethereum-arbitrum/model/bridge.ts | 21 + .../lnbridge/ethereum-arbitrum/model/index.ts | 1 + .../ethereum-arbitrum/utils/bridge.ts | 133 ++++ .../lnbridge/ethereum-arbitrum/utils/index.ts | 1 + .../abi/{lnbridge.json => arb-eth.json} | 0 .../lnbridge/lnbridge/config/abi/eth-arb.json | 463 ++++++++++++ .../bridges/lnbridge/lnbridge/utils/bridge.ts | 27 +- packages/apps/components/CrossChain.tsx | 14 +- .../form-control/BridgeSelector.tsx | 2 +- .../apps/components/tx/TransferConfirm.tsx | 3 +- packages/apps/config/bridge.ts | 11 +- packages/apps/config/gql.ts | 1 + packages/apps/model/gql.ts | 1 + packages/apps/model/tx.ts | 1 + packages/apps/utils/bridge/bridge.ts | 2 +- packages/apps/utils/bridge/predicates.ts | 19 +- packages/shared/config/network/goerli.ts | 5 + packages/shared/core/bridge.ts | 2 +- packages/shared/model/bridge/bridge.ts | 9 +- 30 files changed, 1426 insertions(+), 34 deletions(-) create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/Arbitrum2EthereumLnBridge.tsx create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/Ethereum2ArbitrumLnBridge.tsx create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/config/abi/lnArbitrumL1Issuing.json create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/config/bridge.ts create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/config/index.ts create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/index.ts create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/model/bridge.ts create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/model/index.ts create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/utils/bridge.ts create mode 100644 packages/apps/bridges/lnbridge/ethereum-arbitrum/utils/index.ts rename packages/apps/bridges/lnbridge/lnbridge/config/abi/{lnbridge.json => arb-eth.json} (100%) create mode 100644 packages/apps/bridges/lnbridge/lnbridge/config/abi/eth-arb.json diff --git a/packages/apps/__tests__/bridge.spec.ts b/packages/apps/__tests__/bridge.spec.ts index 1db2283f3..c516dd93f 100644 --- a/packages/apps/__tests__/bridge.spec.ts +++ b/packages/apps/__tests__/bridge.spec.ts @@ -52,7 +52,7 @@ describe('bridge utils', () => { }); it('should support transfer count: ', () => { - expect(allDirections).toHaveLength(114); + expect(allDirections).toHaveLength(118); }); it('Should correct bridge category name', () => { diff --git a/packages/apps/__tests__/network.spec.ts b/packages/apps/__tests__/network.spec.ts index e369cddfd..a1fe3db3d 100644 --- a/packages/apps/__tests__/network.spec.ts +++ b/packages/apps/__tests__/network.spec.ts @@ -73,6 +73,7 @@ describe('network utils', () => { 'arbitrum', 'arbitrum', 'arbitrum', + 'arbitrum', 'astar', 'avalanche', 'optimism', @@ -101,7 +102,18 @@ describe('network utils', () => { expect(group).not.toEqual(undefined); expect(sort(group![1])).toEqual( - sort(['astar', 'avalanche', 'optimism', 'bsc', 'ethereum', 'ethereum', 'ethereum', 'ethereum', 'polygon']) + sort([ + 'astar', + 'avalanche', + 'optimism', + 'bsc', + 'ethereum', + 'ethereum', + 'ethereum', + 'ethereum', + 'ethereum', + 'polygon', + ]) ); }); diff --git a/packages/apps/bridges/bridges.ts b/packages/apps/bridges/bridges.ts index 2a27e36ba..209e04ba4 100644 --- a/packages/apps/bridges/bridges.ts +++ b/packages/apps/bridges/bridges.ts @@ -25,6 +25,7 @@ import { ArbitrumEthereumBridge } from './helixlp/arbitrum-ethereum/utils'; import { EthereumArbitrumBridgeL2 } from './l1tol2/ethereum-arbitrum/utils'; import { HelixLpBridgeBridge } from './helixlp/helixLpBridge/utils'; import { ArbitrumEthereumBridge as ArbitrumEthereumBridgeLnBridge } from './lnbridge/arbitrum-ethereum/utils'; +import { EthereumArbitrumBridge as EthereumArbitrumBridgeLnBridge } from './lnbridge/ethereum-arbitrum/utils'; export const bridgeConstructors = [ CBridgeBridge, @@ -53,6 +54,7 @@ export const bridgeConstructors = [ ArbitrumEthereumBridge, EthereumArbitrumBridgeL2, ArbitrumEthereumBridgeLnBridge, + EthereumArbitrumBridgeLnBridge, ]; export function bridgeFactory( diff --git a/packages/apps/bridges/index.ts b/packages/apps/bridges/index.ts index 753f9f3a4..845856866 100644 --- a/packages/apps/bridges/index.ts +++ b/packages/apps/bridges/index.ts @@ -47,3 +47,5 @@ export { Arbitrum2EthereumLn, Ethereum2ArbitrumLn } from './helixlp/arbitrum-eth export { Ethereum2ArbitrumL2, Arbitrum2EthereumL2 } from './l1tol2/ethereum-arbitrum'; export { Arbitrum2EthereumLnBridge } from './lnbridge/arbitrum-ethereum'; + +export { Ethereum2ArbitrumLnBridge } from './lnbridge/ethereum-arbitrum'; diff --git a/packages/apps/bridges/l1tol2/ethereum-arbitrum/config/bridge.ts b/packages/apps/bridges/l1tol2/ethereum-arbitrum/config/bridge.ts index 689628d30..f403eb379 100644 --- a/packages/apps/bridges/l1tol2/ethereum-arbitrum/config/bridge.ts +++ b/packages/apps/bridges/l1tol2/ethereum-arbitrum/config/bridge.ts @@ -26,7 +26,7 @@ export const ethereumArbitrumL2 = new BridgeBase(ethereumConfig, arbitrumConfig, category: 'l1tol2', }); -export const arbitrumGoerliL2 = new BridgeBase(goerliConfig, arbitrumGoerliConfig, arbitrumGoerliGoerliConfig, { +export const goerliArbitrumL2 = new BridgeBase(goerliConfig, arbitrumGoerliConfig, arbitrumGoerliGoerliConfig, { name: 'ethereum-arbitrum', category: 'l1tol2', }); diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/Arbitrum2EthereumLnBridge.tsx b/packages/apps/bridges/lnbridge/ethereum-arbitrum/Arbitrum2EthereumLnBridge.tsx new file mode 100644 index 000000000..2f9b34296 --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/Arbitrum2EthereumLnBridge.tsx @@ -0,0 +1,10 @@ +import { CrossToken, DVMChainConfig } from 'shared/model'; +import { Bridge } from '../../../components/bridge/Bridge'; +import { CrossChainComponentProps } from '../../../model/component'; +import { EthereumArbitrumBridge } from './utils/bridge'; + +export function Arbitrum2EthereumLnBridge( + props: CrossChainComponentProps, CrossToken> +) { + return ; +} diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/Ethereum2ArbitrumLnBridge.tsx b/packages/apps/bridges/lnbridge/ethereum-arbitrum/Ethereum2ArbitrumLnBridge.tsx new file mode 100644 index 000000000..713b4056b --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/Ethereum2ArbitrumLnBridge.tsx @@ -0,0 +1,10 @@ +import { CrossToken, DVMChainConfig } from 'shared/model'; +import { Bridge } from '../../../components/bridge/Bridge'; +import { CrossChainComponentProps } from '../../../model/component'; +import { EthereumArbitrumBridge } from './utils/bridge'; + +export function Ethereum2ArbitrumLnBridge( + props: CrossChainComponentProps, CrossToken> +) { + return ; +} diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/abi/lnArbitrumL1Issuing.json b/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/abi/lnArbitrumL1Issuing.json new file mode 100644 index 000000000..411e41bcd --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/abi/lnArbitrumL1Issuing.json @@ -0,0 +1,671 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "transferId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "TransferCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "transferId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "relayer", + "type": "address" + } + ], + "name": "TransferRelayed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "DAO_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPERATOR_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "transferIds", + "type": "bytes32[]" + }, + { + "internalType": "bool", + "name": "withdrawNative", + "type": "bool" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "_encodeWithdrawLiquidity", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inbox", + "outputs": [ + { + "internalType": "contract IInbox", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_dao", + "type": "address" + }, + { + "internalType": "address", + "name": "_inbox", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "issuedMessages", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint112", + "name": "amount", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "sourceChainId", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "issuingNative", + "type": "bool" + } + ], + "name": "relay", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "remoteBridge", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "issuingNative", + "type": "bool" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint112", + "name": "amount", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "sourceChainId", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "withdrawNative", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + } + ], + "name": "requestCancelIssuing", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "transferIds", + "type": "bytes32[]" + }, + { + "internalType": "bool", + "name": "withdrawNative", + "type": "bool" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "requestWithdrawLiquidity", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_remoteBridge", + "type": "address" + } + ], + "name": "setRemoteBridge", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "baseFee", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "transferIds", + "type": "bytes32[]" + }, + { + "internalType": "bool", + "name": "withdrawNative", + "type": "bool" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "percentIncrease", + "type": "uint256" + } + ], + "name": "submissionFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/bridge.ts b/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/bridge.ts new file mode 100644 index 000000000..cb24b8446 --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/bridge.ts @@ -0,0 +1,28 @@ +import { arbitrumConfig, ethereumConfig } from 'shared/config/network'; +import { arbitrumGoerliConfig, goerliConfig } from 'shared/config/network'; +import { BridgeBase } from 'shared/core/bridge'; +import { EthereumArbitrumBridgeConfig } from '../model'; + +const ethereumArbitrumConfig: EthereumArbitrumBridgeConfig = { + contracts: { + backing: '0xD1B10B114f1975d8BCc6cb6FC43519160e2AA978', + issuing: '0xeAb1F01a8f4A2687023B159c2063639Adad5304E', + }, +}; + +const goerliArbitrumGoerliConfig: EthereumArbitrumBridgeConfig = { + contracts: { + backing: '0xcD86cf37a4Dc6f78B4899232E7dD1b5c8130EFDA', + issuing: '0x4112c9d474951246fBC2B4D868D247e714698aE1', + }, +}; + +export const ethereumArbitrumLnBridge = new BridgeBase(ethereumConfig, arbitrumConfig, ethereumArbitrumConfig, { + name: 'ethereum-arbitrum', + category: 'lnbridgev20-default', +}); + +export const goerliArbitrumLnBridge = new BridgeBase(goerliConfig, arbitrumGoerliConfig, goerliArbitrumGoerliConfig, { + name: 'ethereum-arbitrum', + category: 'lnbridgev20-default', +}); diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/index.ts b/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/index.ts new file mode 100644 index 000000000..292563932 --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/config/index.ts @@ -0,0 +1 @@ +export * from './bridge'; diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/index.ts b/packages/apps/bridges/lnbridge/ethereum-arbitrum/index.ts new file mode 100644 index 000000000..3c9e9fec0 --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/index.ts @@ -0,0 +1,2 @@ +export * from './Arbitrum2EthereumLnBridge'; +export * from './Ethereum2ArbitrumLnBridge'; diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/model/bridge.ts b/packages/apps/bridges/lnbridge/ethereum-arbitrum/model/bridge.ts new file mode 100644 index 000000000..6ce1b1e30 --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/model/bridge.ts @@ -0,0 +1,21 @@ +import { BridgeConfig } from 'shared/model'; +import { ContractConfig } from 'shared/model'; +import { CrossToken, DVMChainConfig } from 'shared/model'; +import { Bridge } from '../../../../core/bridge'; +import { CrossChainPayload } from '../../../../model/tx'; + +type EthereumArbitrumContractConfig = ContractConfig; + +export type EthereumArbitrumBridgeConfig = Required>; + +export type IssuingPayload = CrossChainPayload< + Bridge, + CrossToken, + CrossToken +>; + +export type RedeemPayload = CrossChainPayload< + Bridge, + CrossToken, + CrossToken +>; diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/model/index.ts b/packages/apps/bridges/lnbridge/ethereum-arbitrum/model/index.ts new file mode 100644 index 000000000..292563932 --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/model/index.ts @@ -0,0 +1 @@ +export * from './bridge'; diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/utils/bridge.ts b/packages/apps/bridges/lnbridge/ethereum-arbitrum/utils/bridge.ts new file mode 100644 index 000000000..c4f36cef7 --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/utils/bridge.ts @@ -0,0 +1,133 @@ +import { BN } from '@polkadot/util'; +import { Contract } from 'ethers/lib/ethers'; +import { message } from 'antd'; +import { ChainConfig, DVMChainConfig, CrossChainDirection, CrossToken, Tx, HelixHistoryRecord } from 'shared/model'; +import { entrance, isMetamaskChainConsistent } from 'shared/utils/connection'; +import { sendTransactionFromContract } from 'shared/utils/tx'; +import { EMPTY } from 'rxjs/internal/observable/empty'; +import { from, Observable, switchMap } from 'rxjs'; +import { IssuingPayload, RedeemPayload, EthereumArbitrumBridgeConfig } from '../model'; +import { LnBridgeBridge } from '../../lnbridge/utils/bridge'; +import lpArbitrumL1IssuingAbi from '../config/abi/lnArbitrumL1Issuing.json'; + +interface RefundParams { + maxSubmissionCost: BN; + gasPrice: BN; + deposit: BN; +} + +export class EthereumArbitrumBridge extends LnBridgeBridge< + EthereumArbitrumBridgeConfig, + DVMChainConfig, + DVMChainConfig +> { + static readonly alias: string = 'EthereumArbitrumLnBridge'; + private l2GasLimit = '100000'; + private feeScaler = '1.1'; + + back(payload: IssuingPayload, fee: BN): Observable { + return this.send(payload, fee); + } + + burn(payload: RedeemPayload, fee: BN): Observable { + return this.send(payload, fee); + } + + refund(record: HelixHistoryRecord): Observable { + const { sender, recipient, sendAmount, fromChain, toChain } = record; + const splitRecords = record.id.split('-'); + const [, sourceChainId, , , transferId] = splitRecords; + const sendToken = this.getTokenConfigFromHelixRecord(record, 'sendToken'); + const recvToken = this.getTokenConfigFromHelixRecord(record, 'recvToken'); + + const { contractAddress, departure, arrival } = this.isIssue(fromChain, toChain) + ? { + contractAddress: this.config.contracts!.issuing, + departure: this.departure, + arrival: this.arrival, + } + : { + contractAddress: this.config.contracts!.backing, + departure: this.arrival, + arrival: this.departure, + }; + + const direction = { from: { meta: arrival }, to: { meta: departure } } as CrossChainDirection< + CrossToken, + CrossToken + >; + + return isMetamaskChainConsistent(this.getChainConfig(toChain)).pipe( + switchMap(() => from(this.getBridgeFee(direction, transferId, false, recipient))), + switchMap((fee) => { + if (fee === null) { + message.error('Some error occurred while getting bridge fee!'); + return EMPTY; + } + const args = [ + record.messageNonce, + recvToken.type === 'native', + record.recvTokenAddress, + sender, + record.recipient, + sendAmount, + sourceChainId, + sendToken.type === 'native', + fee.maxSubmissionCost.toString(), + this.l2GasLimit, + fee.gasPrice.toString(), + ]; + return sendTransactionFromContract( + contractAddress, + (contract) => contract.requestCancelIssuing(...args, { value: fee?.deposit.toString() }), + lpArbitrumL1IssuingAbi + ); + }) + ); + } + + async getBridgeFee( + direction: CrossChainDirection, CrossToken>, + transferId: string, + withdrawNative: boolean, + receiver: string + ): Promise { + const { + from: { meta: departure }, + to: { meta: arrival }, + } = direction; + + const l1Provider = entrance.web3.getInstance(departure.provider.https); + const l2Provider = entrance.web3.getInstance(arrival.provider.https); + + try { + const l1BaseFee = (await l1Provider.getBlock('latest')).baseFeePerGas; + const l2GasPrice = await l2Provider.getGasPrice(); + const feeScaler = Number(this.feeScaler); + const scaleL1BaseFee = (Number(l1BaseFee) * feeScaler).toFixed(); + const scaleL2GasPrice = (Number(l2GasPrice) * feeScaler).toFixed(); + + const address = this.isIssue(departure, arrival) + ? this.config.contracts?.backing + : this.config.contracts?.issuing; + const contract = new Contract(address as string, lpArbitrumL1IssuingAbi, l1Provider); + const maxSubmissionCost = await contract.submissionFee( + scaleL1BaseFee.toString(), + [transferId], + withdrawNative, + receiver, + 10 + ); + const deposit = Number(this.l2GasLimit) * Number(scaleL2GasPrice) + Number(maxSubmissionCost); + + return { + maxSubmissionCost: new BN(maxSubmissionCost.toString()), + gasPrice: new BN(scaleL2GasPrice.toString()), + deposit: new BN(deposit.toString()), + }; + } catch (err) { + console.log(err); + return null; + } + } +} diff --git a/packages/apps/bridges/lnbridge/ethereum-arbitrum/utils/index.ts b/packages/apps/bridges/lnbridge/ethereum-arbitrum/utils/index.ts new file mode 100644 index 000000000..292563932 --- /dev/null +++ b/packages/apps/bridges/lnbridge/ethereum-arbitrum/utils/index.ts @@ -0,0 +1 @@ +export * from './bridge'; diff --git a/packages/apps/bridges/lnbridge/lnbridge/config/abi/lnbridge.json b/packages/apps/bridges/lnbridge/lnbridge/config/abi/arb-eth.json similarity index 100% rename from packages/apps/bridges/lnbridge/lnbridge/config/abi/lnbridge.json rename to packages/apps/bridges/lnbridge/lnbridge/config/abi/arb-eth.json diff --git a/packages/apps/bridges/lnbridge/lnbridge/config/abi/eth-arb.json b/packages/apps/bridges/lnbridge/lnbridge/config/abi/eth-arb.json new file mode 100644 index 000000000..92afe1b73 --- /dev/null +++ b/packages/apps/bridges/lnbridge/lnbridge/config/abi/eth-arb.json @@ -0,0 +1,463 @@ +[ + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "provider", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "sourceToken", "type": "address" }, + { "indexed": false, "internalType": "uint112", "name": "baseFee", "type": "uint112" }, + { "indexed": false, "internalType": "uint8", "name": "liquidityfeeRate", "type": "uint8" } + ], + "name": "LnProviderUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "bytes32", "name": "previousAdminRole", "type": "bytes32" }, + { "indexed": true, "internalType": "bytes32", "name": "newAdminRole", "type": "bytes32" } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bytes32", "name": "transferId", "type": "bytes32" }, + { "indexed": false, "internalType": "address", "name": "provider", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "sourceToken", "type": "address" }, + { "indexed": false, "internalType": "uint112", "name": "amount", "type": "uint112" }, + { "indexed": false, "internalType": "uint112", "name": "fee", "type": "uint112" }, + { "indexed": false, "internalType": "address", "name": "receiver", "type": "address" } + ], + "name": "TokenLocked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "sourceToken", "type": "address" }, + { "indexed": false, "internalType": "uint112", "name": "amount", "type": "uint112" } + ], + "name": "WithdrawMargin", + "type": "event" + }, + { + "inputs": [], + "name": "DAO_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INIT_SLASH_TRANSFER_ID", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LIQUIDITY_FEE_RATE_BASE", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_TRANSFER_AMOUNT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_SLASH_TIMESTAMP", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPERATOR_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "sourceToken", "type": "address" }, + { "internalType": "address", "name": "targetToken", "type": "address" } + ], + "name": "getDefaultProviderKey", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "sourceToken", "type": "address" } + ], + "name": "getProviderKey", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "getRoleMember", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }], + "name": "getRoleMemberCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inbox", + "outputs": [{ "internalType": "contract IInbox", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_dao", "type": "address" }, + { "internalType": "address", "name": "_inbox", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "name": "lnProviders", + "outputs": [ + { + "components": [ + { "internalType": "uint112", "name": "baseFee", "type": "uint112" }, + { "internalType": "uint8", "name": "liquidityFeeRate", "type": "uint8" } + ], + "internalType": "struct LnDefaultBridgeSource.LnProviderFee", + "name": "fee", + "type": "tuple" + }, + { "internalType": "uint64", "name": "withdrawNonce", "type": "uint64" }, + { "internalType": "bytes32", "name": "lastTransferId", "type": "bytes32" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "name": "lockInfos", + "outputs": [ + { "internalType": "uint112", "name": "fee", "type": "uint112" }, + { "internalType": "uint112", "name": "penalty", "type": "uint112" }, + { "internalType": "bool", "name": "isLocked", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "pause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFeeReceiver", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "remoteBridge", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sourceToken", "type": "address" }, + { "internalType": "uint112", "name": "amount", "type": "uint112" }, + { "internalType": "uint256", "name": "maxSubmissionCost", "type": "uint256" }, + { "internalType": "uint256", "name": "maxGas", "type": "uint256" }, + { "internalType": "uint256", "name": "gasPriceBid", "type": "uint256" } + ], + "name": "requestWithdrawMargin", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sourceToken", "type": "address" }, + { "internalType": "uint112", "name": "baseFee", "type": "uint112" }, + { "internalType": "uint8", "name": "liquidityFeeRate", "type": "uint8" } + ], + "name": "setProviderFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_remoteBridge", "type": "address" }], + "name": "setRemoteBridge", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_sourceToken", "type": "address" }, + { "internalType": "address", "name": "_targetToken", "type": "address" }, + { "internalType": "uint112", "name": "_protocolFee", "type": "uint112" }, + { "internalType": "uint112", "name": "_penaltyLnCollateral", "type": "uint112" }, + { "internalType": "uint8", "name": "_sourceDecimals", "type": "uint8" }, + { "internalType": "uint8", "name": "_targetDecimals", "type": "uint8" } + ], + "name": "setTokenInfo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "bytes32", "name": "previousTransferId", "type": "bytes32" }, + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "sourceToken", "type": "address" }, + { "internalType": "address", "name": "targetToken", "type": "address" }, + { "internalType": "uint112", "name": "amount", "type": "uint112" }, + { "internalType": "uint64", "name": "timestamp", "type": "uint64" }, + { "internalType": "address", "name": "receiver", "type": "address" } + ], + "internalType": "struct LnBridgeHelper.TransferParameter", + "name": "params", + "type": "tuple" + }, + { "internalType": "bytes32", "name": "expectedTransferId", "type": "bytes32" }, + { "internalType": "uint256", "name": "maxSubmissionCost", "type": "uint256" }, + { "internalType": "uint256", "name": "maxGas", "type": "uint256" }, + { "internalType": "uint256", "name": "gasPriceBid", "type": "uint256" } + ], + "name": "slashAndRemoteRelease", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "baseFee", "type": "uint256" }, + { + "components": [ + { "internalType": "bytes32", "name": "previousTransferId", "type": "bytes32" }, + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "sourceToken", "type": "address" }, + { "internalType": "address", "name": "targetToken", "type": "address" }, + { "internalType": "uint112", "name": "amount", "type": "uint112" }, + { "internalType": "uint64", "name": "timestamp", "type": "uint64" }, + { "internalType": "address", "name": "receiver", "type": "address" } + ], + "internalType": "struct LnBridgeHelper.TransferParameter", + "name": "params", + "type": "tuple" + }, + { "internalType": "address", "name": "slasher", "type": "address" }, + { "internalType": "uint112", "name": "fee", "type": "uint112" }, + { "internalType": "uint112", "name": "penalty", "type": "uint112" }, + { "internalType": "uint256", "name": "percentIncrease", "type": "uint256" } + ], + "name": "submissionSlashFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "baseFee", "type": "uint256" }, + { "internalType": "bytes32", "name": "lastTransferId", "type": "bytes32" }, + { "internalType": "uint64", "name": "withdrawNonce", "type": "uint64" }, + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "sourceToken", "type": "address" }, + { "internalType": "address", "name": "targetToken", "type": "address" }, + { "internalType": "uint112", "name": "amount", "type": "uint112" }, + { "internalType": "uint256", "name": "percentIncrease", "type": "uint256" } + ], + "name": "submissionWithdrawFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "tokenInfos", + "outputs": [ + { "internalType": "address", "name": "targetToken", "type": "address" }, + { "internalType": "uint112", "name": "protocolFee", "type": "uint112" }, + { "internalType": "uint112", "name": "penaltyLnCollateral", "type": "uint112" }, + { "internalType": "uint8", "name": "sourceDecimals", "type": "uint8" }, + { "internalType": "uint8", "name": "targetDecimals", "type": "uint8" }, + { "internalType": "bool", "name": "isRegistered", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "sourceToken", "type": "address" }, + { "internalType": "uint112", "name": "amount", "type": "uint112" } + ], + "name": "totalFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "sourceToken", "type": "address" }, + { "internalType": "bytes32", "name": "transferId", "type": "bytes32" }, + { "internalType": "uint112", "name": "totalFee", "type": "uint112" }, + { "internalType": "uint64", "name": "withdrawNonce", "type": "uint64" } + ], + "internalType": "struct LnDefaultBridgeSource.Snapshot", + "name": "snapshot", + "type": "tuple" + }, + { "internalType": "uint112", "name": "amount", "type": "uint112" }, + { "internalType": "address", "name": "receiver", "type": "address" } + ], + "name": "transferAndLockMargin", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { "inputs": [], "name": "unpause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "_receiver", "type": "address" }], + "name": "updateFeeReceiver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/packages/apps/bridges/lnbridge/lnbridge/utils/bridge.ts b/packages/apps/bridges/lnbridge/lnbridge/utils/bridge.ts index 75edba1e9..bf08fda0a 100644 --- a/packages/apps/bridges/lnbridge/lnbridge/utils/bridge.ts +++ b/packages/apps/bridges/lnbridge/lnbridge/utils/bridge.ts @@ -17,7 +17,8 @@ import { sendTransactionFromContract } from 'shared/utils/tx'; import { getOverview } from 'utils/bridge'; import { Bridge, TokenWithAmount } from '../../../../core/bridge'; import { AllowancePayload } from '../../../../model/allowance'; -import lnBridgeAbi from '../config/abi/lnbridge.json'; +import arbEthAbi from '../config/abi/arb-eth.json'; +import ethArbAbi from '../config/abi/eth-arb.json'; import { CrossChainPayload } from '../../../../model/tx'; export abstract class LnBridgeBridge< @@ -57,6 +58,7 @@ export abstract class LnBridgeBridge< const transferAmount = utils.parseUnits(amount.toString(), decimals); const { contracts } = bridge.config; const contractAddress = bridge.isIssue(fromChain, to.meta) ? contracts!.backing : contracts!.issuing; + const contractAbi = bridge.category === 'lnbridgev20-default' ? ethArbAbi : arbEthAbi; return sendTransactionFromContract( contractAddress, @@ -74,16 +76,15 @@ export abstract class LnBridgeBridge< } ); } else if (snapshot) { - const { relayer, sourceToken, transferId, depositedMargin, totalFee } = snapshot; - return contract.transferAndLockMargin( - [relayer, sourceToken, transferId, depositedMargin, totalFee], - transferAmount, - recipient, - { gasLimit: 1000000 } - ); + const { relayer, sourceToken, transferId, depositedMargin, totalFee, withdrawNonce } = snapshot; + const snapshotArgs = + bridge.category === 'lnbridgev20-default' + ? [relayer, sourceToken, transferId, totalFee, withdrawNonce] + : [relayer, sourceToken, transferId, depositedMargin, totalFee]; + return contract.transferAndLockMargin(snapshotArgs, transferAmount, recipient, { gasLimit: 1000000 }); } }, - lnBridgeAbi + contractAbi ); } @@ -117,7 +118,7 @@ export abstract class LnBridgeBridge< }); } }, - lnBridgeAbi + arbEthAbi ); }) ); @@ -152,13 +153,13 @@ export abstract class LnBridgeBridge< if (options) { totalFee = utils .parseUnits(amount.toString(), direction.from.decimals) - .mul(BigNumber.from(options.liquidityFeeRate)) + .mul(options.liquidityFeeRate) // eslint-disable-next-line no-magic-numbers - .div(BigNumber.from(100000)) + .div(100000) .add(BigNumber.from(options.baseFee)) .toString(); } else { - const overview = getOverview(direction, 'lnbridgev20-opposite'); + const overview = getOverview(direction, 'lnbridgev20-default'); // basefee + amount * 0.1% const baseFee = overview!.basefee! + amount * Number(this.feePercent); totalFee = toWei({ value: baseFee, decimals: direction.from.decimals }); diff --git a/packages/apps/components/CrossChain.tsx b/packages/apps/components/CrossChain.tsx index 84bbb58c6..5ce6263c7 100644 --- a/packages/apps/components/CrossChain.tsx +++ b/packages/apps/components/CrossChain.tsx @@ -120,7 +120,7 @@ export function CrossChain() { nameWithSuffix = name + 'Ln'; } else if (bridge.category === 'l1tol2') { nameWithSuffix = name + 'L2'; - } else if (bridge.category === 'lnbridgev20-opposite') { + } else if (bridge.category === 'lnbridgev20-opposite' || bridge.category === 'lnbridgev20-default') { nameWithSuffix = name + 'LnBridge'; } @@ -204,7 +204,7 @@ export function CrossChain() { } let sub$$: Subscription; - if (bridge.category === 'lnbridgev20-opposite') { + if (bridge.category === 'lnbridgev20-opposite' || bridge.category === 'lnbridgev20-default') { const amount = Number.isNaN(Number(direction.from.amount)) ? 0 : Number(direction.from.amount); sub$$ = from( fetchRelayersInfo({ @@ -530,8 +530,12 @@ export function CrossChain() { patchPayload, (value: CrossChainPayload | null) => value && { ...value, wallet: departureConnection.wallet as SupportedWallet }, + // eslint-disable-next-line complexity async (value) => { - if (value?.bridge.category === 'lnbridgev20-opposite') { + if ( + value?.bridge.category === 'lnbridgev20-opposite' || + value?.bridge.category === 'lnbridgev20-default' + ) { _relayerCount = 0; try { const amount = utils.parseUnits( @@ -558,6 +562,9 @@ export function CrossChain() { const baseFee = BigNumber.from(relayersInfo.sortedLnv20RelayInfos[0].baseFee); const liquidityFeeRate = relayersInfo.sortedLnv20RelayInfos[0].liquidityFeeRate; const transferId = relayersInfo.sortedLnv20RelayInfos[0].lastTransferId; + const withdrawNonce = BigNumber.from( + relayersInfo.sortedLnv20RelayInfos[0].withdrawNonce + ).add(1); const totalFee = BigNumber.from(liquidityFeeRate) .mul(amount) @@ -573,6 +580,7 @@ export function CrossChain() { transferId, depositedMargin, totalFee, + withdrawNonce, }, }; } diff --git a/packages/apps/components/form-control/BridgeSelector.tsx b/packages/apps/components/form-control/BridgeSelector.tsx index dd49d95fd..1f4db74f3 100644 --- a/packages/apps/components/form-control/BridgeSelector.tsx +++ b/packages/apps/components/form-control/BridgeSelector.tsx @@ -73,7 +73,7 @@ export function BridgeSelector({ direction, value, onChange }: BridgeSelectorPro ? 'helix(Legacy)' : item.category === 'helixLpBridge' ? 'helix(Fusion)' - : item.category === 'lnbridgev20-opposite' + : item.category === 'lnbridgev20-opposite' || item.category === 'lnbridgev20-default' ? 'Helix LnBridge' : item.category} diff --git a/packages/apps/components/tx/TransferConfirm.tsx b/packages/apps/components/tx/TransferConfirm.tsx index 62a805690..822c1f8a1 100644 --- a/packages/apps/components/tx/TransferConfirm.tsx +++ b/packages/apps/components/tx/TransferConfirm.tsx @@ -110,7 +110,8 @@ export function TransferConfirm({ estimate: value.bridge.category === 'cBridge' || value.bridge.category === 'helix' ? '5-20' - : value.bridge.category === 'lnbridgev20-opposite' + : value.bridge.category === 'lnbridgev20-opposite' || + value.bridge.category === 'lnbridgev20-default' ? '1-30' : '1-3', })} diff --git a/packages/apps/config/bridge.ts b/packages/apps/config/bridge.ts index c0f99d04a..da373f9e1 100644 --- a/packages/apps/config/bridge.ts +++ b/packages/apps/config/bridge.ts @@ -51,8 +51,9 @@ import { khalaMoonriver } from '../bridges/xcm/khala-moonriver/config'; import { crabDarwinia } from '../bridges/helixlp/crab-darwinia/config'; import { darwiniaEthereum } from '../bridges/helixlp/darwinia-ethereum/config'; import { arbitrumGoerli, arbitrumEthereum } from '../bridges/helixlp/arbitrum-ethereum/config'; -import { arbitrumGoerliL2, ethereumArbitrumL2 } from '../bridges/l1tol2/ethereum-arbitrum/config'; +import { goerliArbitrumL2, ethereumArbitrumL2 } from '../bridges/l1tol2/ethereum-arbitrum/config'; import { arbitrumGoerliLnBridge, arbitrumEthereumLnBridge } from '../bridges/lnbridge/arbitrum-ethereum/config'; +import { goerliArbitrumLnBridge, ethereumArbitrumLnBridge } from '../bridges/lnbridge/ethereum-arbitrum/config'; const formalBridges = [ arbitrumAstar, @@ -79,7 +80,6 @@ const formalBridges = [ darwiniaDVMCrabDVM, darwiniaDVMDarwiniaDVM, darwiniaDVMEthereum, - ethereumArbitrum, ethereumAstar, ethereumAvalanche, ethereumBSC, @@ -104,8 +104,10 @@ const formalBridges = [ crabDarwinia, darwiniaEthereum, arbitrumEthereum, - ethereumArbitrumL2, arbitrumEthereumLnBridge, + ethereumArbitrumLnBridge, + ethereumArbitrumL2, + ethereumArbitrum, ]; const testBridges = [ @@ -113,8 +115,9 @@ const testBridges = [ pangoroDVMPangolinDVM, pangoroDVMPangoroDVM, arbitrumGoerli, - arbitrumGoerliL2, arbitrumGoerliLnBridge, + goerliArbitrumLnBridge, + goerliArbitrumL2, ]; export const BRIDGES = (() => { diff --git a/packages/apps/config/gql.ts b/packages/apps/config/gql.ts index b180bbc86..68deb0fb0 100644 --- a/packages/apps/config/gql.ts +++ b/packages/apps/config/gql.ts @@ -99,6 +99,7 @@ export const GET_RELAYERS_INFO = ` baseFee liquidityFeeRate lastTransferId + withdrawNonce } } `; diff --git a/packages/apps/model/gql.ts b/packages/apps/model/gql.ts index 49fd5d822..398124fa8 100644 --- a/packages/apps/model/gql.ts +++ b/packages/apps/model/gql.ts @@ -15,5 +15,6 @@ export interface RelayersInfoRes { baseFee: string; liquidityFeeRate: number; // 0 ~ 256 lastTransferId: string; + withdrawNonce: string; }[]; } diff --git a/packages/apps/model/tx.ts b/packages/apps/model/tx.ts index bb9485c84..fd4223899 100644 --- a/packages/apps/model/tx.ts +++ b/packages/apps/model/tx.ts @@ -10,6 +10,7 @@ interface Snapshot { transferId: string; depositedMargin: BigNumber; totalFee: BigNumber; + withdrawNonce: BigNumber; } interface CrossChainParty { diff --git a/packages/apps/utils/bridge/bridge.ts b/packages/apps/utils/bridge/bridge.ts index fd9374b5d..676fe3da1 100644 --- a/packages/apps/utils/bridge/bridge.ts +++ b/packages/apps/utils/bridge/bridge.ts @@ -69,7 +69,7 @@ export function getBridges(source: CrossChainPureDirection): BridgeBase[] { export function bridgeCategoryDisplay(category: BridgeCategory) { if (category === 'l1tol2') { return 'L2Bridge'; - } else if (category === 'lnbridgev20-opposite') { + } else if (category === 'lnbridgev20-opposite' || category === 'lnbridgev20-default') { return 'Helix LnBridge'; } return /^[a-z]+$/.test(category) ? upperFirst(category) : category; diff --git a/packages/apps/utils/bridge/predicates.ts b/packages/apps/utils/bridge/predicates.ts index 960f1c8fe..1684eae4e 100644 --- a/packages/apps/utils/bridge/predicates.ts +++ b/packages/apps/utils/bridge/predicates.ts @@ -14,11 +14,18 @@ const or: (...fns: BridgePredicateFn[]) => (dep: Network, arr: Network) => boole (departure, arrival) => fns.some((fn) => fn(departure, arrival)); -const isBridge = (category: BridgeCategory) => (direction: CrossChainPureDirection) => { - const bridge = getBridge(direction, category); - - return bridge.category === category; -}; +const isBridge = + (...categories: BridgeCategory[]) => + (direction: CrossChainPureDirection) => { + for (const category of categories) { + const bridge = getBridge(direction, category); + if (bridge.category === category) { + return true; + } + } + + return false; + }; export const isCBridge = isBridge('cBridge'); @@ -28,7 +35,7 @@ export const isLpBridge = isBridge('helixLpBridge'); export const isL2Bridge = isBridge('l1tol2'); -export const isLnBridge = isBridge('lnbridgev20-opposite'); +export const isLnBridge = isBridge('lnbridgev20-opposite', 'lnbridgev20-default'); export const isTransferBetween = (network1: Network, network2: Network) => { const isBacking = predicate(network1, network2); diff --git a/packages/shared/config/network/goerli.ts b/packages/shared/config/network/goerli.ts index 860c070d1..d6c2c2228 100644 --- a/packages/shared/config/network/goerli.ts +++ b/packages/shared/config/network/goerli.ts @@ -54,6 +54,11 @@ export const goerliConfig: EthereumChainConfig = { name: 'RING', decimals: 18, cross: [ + { + category: 'lnbridgev20-default', + bridge: 'ethereum-arbitrum', + partner: { name: 'arbitrum-goerli', role: 'issuing', symbol: 'RING' }, + }, { category: 'l1tol2', bridge: 'ethereum-arbitrum', diff --git a/packages/shared/core/bridge.ts b/packages/shared/core/bridge.ts index 0ee0ee8a1..0b64002c6 100644 --- a/packages/shared/core/bridge.ts +++ b/packages/shared/core/bridge.ts @@ -113,7 +113,7 @@ export class BridgeBase