diff --git a/package.json b/package.json index bca2713f2f6..6b3e316f8f8 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@rabby-wallet/rabby-action": "0.1.4", "@rabby-wallet/rabby-api": "0.8.4", "@rabby-wallet/rabby-security-engine": "2.0.7", - "@rabby-wallet/rabby-swap": "0.0.40", + "@rabby-wallet/rabby-swap": "0.0.42", "@rabby-wallet/widgets": "1.0.9", "@rematch/core": "2.2.0", "@rematch/select": "3.1.2", diff --git a/src/background/controller/provider/controller.ts b/src/background/controller/provider/controller.ts index e08e490b4b3..6f6cfa2bd07 100644 --- a/src/background/controller/provider/controller.ts +++ b/src/background/controller/provider/controller.ts @@ -473,13 +473,25 @@ class ProviderController extends BaseController { reported: false, }; + let signedTx; try { - const signedTx = await keyringService.signTransaction( + signedTx = await keyringService.signTransaction( keyring, tx, txParams.from, opts ); + } catch (e) { + const errObj = + typeof e === 'object' + ? { message: e.message } + : ({ message: e } as any); + errObj.method = EVENTS.COMMON_HARDWARE.REJECTED; + + throw errObj; + } + + try { if ( currentAccount.type === KEYRING_TYPE.GnosisKeyring || currentAccount.type === KEYRING_TYPE.CoboArgusKeyring diff --git a/src/background/controller/provider/rpcFlow.ts b/src/background/controller/provider/rpcFlow.ts index 65171dc605d..74dae649a2b 100644 --- a/src/background/controller/provider/rpcFlow.ts +++ b/src/background/controller/provider/rpcFlow.ts @@ -263,15 +263,21 @@ const flowContext = flow }) .then(resolve) .catch((e: any) => { + const payload = { + method: EVENTS.SIGN_FINISHED, + params: { + success: false, + errorMsg: e?.message || JSON.stringify(e), + }, + }; + if (e.method) { + payload.method = e.method; + payload.params = e.message; + } + Sentry.captureException(e); if (isSignApproval(approvalType)) { - eventBus.emit(EVENTS.broadcastToUI, { - method: EVENTS.SIGN_FINISHED, - params: { - success: false, - errorMsg: e?.message || JSON.stringify(e), - }, - }); + eventBus.emit(EVENTS.broadcastToUI, payload); } }) ); diff --git a/src/background/service/keyring/eth-ledger-keyring.ts b/src/background/service/keyring/eth-ledger-keyring.ts index a6aa30d6996..c7d4b178e3c 100644 --- a/src/background/service/keyring/eth-ledger-keyring.ts +++ b/src/background/service/keyring/eth-ledger-keyring.ts @@ -159,10 +159,19 @@ class LedgerBridgeKeyring { const path = hdPath ? this._toLedgerPath(hdPath) : this.hdPath; await this.makeApp(); - const res = await this.app!.getAddress(path, false, true); - const { address } = res; + let res: { address: string }; + try { + res = await this.app!.getAddress(path, false, true); + } catch (e) { + if (e.name === 'DisconnectedDeviceDuringOperation') { + await this.cleanUp(); + return this.unlock(hdPath, force); + } else { + throw e; + } + } - return address; + return res?.address; } addAccounts(n = 1) { @@ -235,7 +244,6 @@ class LedgerBridgeKeyring { signTransaction(address, tx) { return this.signHelper.invoke(async () => { // make sure the previous transaction is cleaned up - await this._reconnect(); // transactions built with older versions of ethereumjs-tx have a // getChainId method that newer versions do not. Older versions are mutable @@ -296,20 +304,6 @@ class LedgerBridgeKeyring { }); } - async _reconnect() { - await this.cleanUp(); - - let count = 0; - // wait connect the WebHID - while (!this.app) { - await this.makeApp(); - await new Promise((resolve) => setTimeout(resolve, 100)); - if (count++ > 50) { - throw new Error('Ledger: Failed to connect to Ledger'); - } - } - } - async _signTransaction(address, rawTxHex, handleSigning) { const hdPath = await this.unlockAccountByAddress(address); await this.makeApp(true); @@ -326,8 +320,6 @@ class LedgerBridgeKeyring { throw new Error( err.toString() || 'Ledger: Unknown error while signing transaction' ); - } finally { - this.cleanUp(); } } @@ -338,7 +330,6 @@ class LedgerBridgeKeyring { // For personal_sign, we need to prefix the message: async signPersonalMessage(withAccount, message) { return this.signHelper.invoke(async () => { - await this._reconnect(); try { await this.makeApp(true); const hdPath = await this.unlockAccountByAddress(withAccount); @@ -369,8 +360,6 @@ class LedgerBridgeKeyring { throw new Error( e.toString() || 'Ledger: Unknown error while signing message' ); - } finally { - this.cleanUp(); } }); } @@ -397,7 +386,6 @@ class LedgerBridgeKeyring { async signTypedData(withAccount, data, options: any = {}) { return this.signHelper.invoke(async () => { - await this._reconnect(); const isV4 = options.version === 'V4'; if (!isV4) { throw new Error( @@ -471,8 +459,6 @@ class LedgerBridgeKeyring { throw new Error( e.toString() || 'Ledger: Unknown error while signing message' ); - } finally { - this.cleanUp(); } }); } diff --git a/src/background/service/keyring/helper.ts b/src/background/service/keyring/helper.ts index 6b126bbf56f..158d062fdc7 100644 --- a/src/background/service/keyring/helper.ts +++ b/src/background/service/keyring/helper.ts @@ -7,9 +7,7 @@ export const throwError = (error, method = EVENTS.COMMON_HARDWARE.REJECTED) => { method, params: error, }); - throw new Error(error); }; - export class SignHelper { signFn: any; errorEventName: string; @@ -27,18 +25,7 @@ export class SignHelper { } async invoke(fn: () => Promise) { - return new Promise((resolve) => { - this.signFn = async () => { - try { - const result = await fn(); - resolve(result); - } catch (e) { - Sentry.captureException(e); - throwError(e?.message ?? e, this.errorEventName); - } - }; - this.signFn(); - }); + return fn(); } } diff --git a/src/constant/index.ts b/src/constant/index.ts index 2a0fe6f872e..eb673d2b050 100644 --- a/src/constant/index.ts +++ b/src/constant/index.ts @@ -1480,6 +1480,12 @@ export const DEX = { name: 'Odos', chains: DEX_SUPPORT_CHAINS[DEX_ENUM.ODOS], }, + [DEX_ENUM.ZEROXAPIV2]: { + id: DEX_ENUM.ZEROXAPIV2, + logo: Logo0X, + name: '0x', + chains: DEX_SUPPORT_CHAINS[DEX_ENUM.ZEROXAPIV2], + }, }; export const DEX_WITH_WRAP = { diff --git a/src/ui/utils/hooks.ts b/src/ui/utils/hooks.ts index c34b6d68d4e..16c0555d134 100644 --- a/src/ui/utils/hooks.ts +++ b/src/ui/utils/hooks.ts @@ -25,7 +25,7 @@ export const useApproval = () => { approvalId?: string ) => { // handle connect - if (!(await deviceConnect(data?.type))) { + if (!(await deviceConnect(data))) { return; } diff --git a/src/ui/utils/sendTransaction.ts b/src/ui/utils/sendTransaction.ts index 9fb9afdab74..11c24cd3884 100644 --- a/src/ui/utils/sendTransaction.ts +++ b/src/ui/utils/sendTransaction.ts @@ -445,44 +445,45 @@ export const sendTransaction = async ({ } }; + wallet.reportStats('signTransaction', { + type: currentAccount.brandName, + category: KEYRING_CATEGORY_MAP[currentAccount.type], + chainId: chain.serverId, + createdBy: ga ? 'rabby' : 'dapp', + source: ga?.source || '', + trigger: ga?.trigger || '', + networkType: chain?.isTestnet ? 'Custom Network' : 'Integrated Network', + }); + // submit tx let hash = ''; try { - hash = await Promise.race([ - wallet.ethSendTransaction({ - data: { - $ctx: { - ga, - }, - params: [transaction], - }, - session: INTERNAL_REQUEST_SESSION, - approvalRes: { - ...transaction, - signingTxId, - logId: logId, - lowGasDeadline, - isGasLess, - isGasAccount: autoUseGasAccount ? canUseGasAccount : isGasAccount, - pushType, + hash = await wallet.ethSendTransaction({ + data: { + $ctx: { + ga, }, - pushed: false, - result: undefined, - }), - new Promise((_, reject) => { - eventBus.once(EVENTS.LEDGER.REJECTED, async (data) => { - if (signingTxId != null) { - wallet.removeSigningTx(signingTxId); - } - reject(new Error(data)); - }); - }), - ]); + params: [transaction], + }, + session: INTERNAL_REQUEST_SESSION, + approvalRes: { + ...transaction, + signingTxId, + logId: logId, + lowGasDeadline, + isGasLess, + isGasAccount: autoUseGasAccount ? canUseGasAccount : isGasAccount, + pushType, + }, + pushed: false, + result: undefined, + }); await handleSendAfter(); } catch (e) { await handleSendAfter(); const err = new Error(e.message); err.name = FailedCode.SubmitTxFailed; + eventBus.emit(EVENTS.COMMON_HARDWARE.REJECTED, e.message); throw err; } diff --git a/src/ui/utils/useDeviceConnect.ts b/src/ui/utils/useDeviceConnect.ts index b8fefc33fd4..0b424caa45a 100644 --- a/src/ui/utils/useDeviceConnect.ts +++ b/src/ui/utils/useDeviceConnect.ts @@ -1,8 +1,7 @@ import { KEYRING_CLASS } from '@/constant'; import React from 'react'; import { useLedgerStatus } from '../component/ConnectStatus/useLedgerStatus'; -import { useCommonPopupView } from './WalletContext'; -import { useSessionStatus } from '../component/WalletConnect/useSessionStatus'; +import { useCommonPopupView, useWallet } from './WalletContext'; import { useCurrentAccount } from '../hooks/backgroundState/useAccount'; import { useImKeyStatus } from '../component/ConnectStatus/useImKeyStatus'; @@ -12,29 +11,35 @@ import { useImKeyStatus } from '../component/ConnectStatus/useImKeyStatus'; */ export const useDeviceConnect = () => { const ledgerStatus = useLedgerStatus(); - const account = useCurrentAccount(); - const walletConnectStatus = useSessionStatus(account!); const imKeyStatus = useImKeyStatus(); const { activePopup, setAccount } = useCommonPopupView(); + const wallet = useWallet(); + const currentAccount = useCurrentAccount(); /** * @returns {boolean} true if connected, false if not connected and popup is shown */ const connect = React.useCallback( - async (type: string) => { + async (data: any) => { + if (!data) return; + const { type, account, isGnosis } = data; + if (type === KEYRING_CLASS.HARDWARE.LEDGER) { if (ledgerStatus.status === 'DISCONNECTED') { activePopup('Ledger'); return false; } } else if (type === KEYRING_CLASS.WALLETCONNECT) { - if ( - !walletConnectStatus.status || - walletConnectStatus.status === 'DISCONNECTED' - ) { - if (account) { + const acc = isGnosis ? account : currentAccount; + const status = await wallet.getWalletConnectSessionStatus( + acc.address, + acc.brandName + ); + + if (!status || status === 'DISCONNECTED') { + if (acc) { setAccount({ - ...account, + ...acc, type, }); } @@ -50,7 +55,7 @@ export const useDeviceConnect = () => { return true; }, - [ledgerStatus, walletConnectStatus, imKeyStatus, account] + [ledgerStatus, imKeyStatus] ); return connect; diff --git a/src/ui/views/Approval/components/LedgerHardwareWaiting.tsx b/src/ui/views/Approval/components/LedgerHardwareWaiting.tsx index b33f4ba7c00..e61b6794125 100644 --- a/src/ui/views/Approval/components/LedgerHardwareWaiting.tsx +++ b/src/ui/views/Approval/components/LedgerHardwareWaiting.tsx @@ -152,10 +152,7 @@ const LedgerHardwareWaiting = ({ params }: { params: ApprovalParams }) => { method: params?.extra?.signTextMethod, }); } - eventBus.addEventListener(EVENTS.LEDGER.REJECT_APPROVAL, (data) => { - rejectApproval(data, false, true); - }); - eventBus.addEventListener(EVENTS.LEDGER.REJECTED, async (data) => { + eventBus.addEventListener(EVENTS.COMMON_HARDWARE.REJECTED, async (data) => { setErrorMessage(data); if (/DisconnectedDeviceDuringOperation/i.test(data)) { await rejectApproval('User rejected the request.'); diff --git a/src/ui/views/Approval/components/MiniSignTx/MiniLedgerAction.tsx b/src/ui/views/Approval/components/MiniSignTx/MiniLedgerAction.tsx index 9a0a1644b14..6f7cef6ff18 100644 --- a/src/ui/views/Approval/components/MiniSignTx/MiniLedgerAction.tsx +++ b/src/ui/views/Approval/components/MiniSignTx/MiniLedgerAction.tsx @@ -89,10 +89,10 @@ export const MiniLedgerAction: React.FC = ({ } }; - eventBus.addEventListener(EVENTS.LEDGER.REJECTED, listener); + eventBus.addEventListener(EVENTS.COMMON_HARDWARE.REJECTED, listener); return () => { - eventBus.removeEventListener(EVENTS.LEDGER.REJECTED, listener); + eventBus.removeEventListener(EVENTS.COMMON_HARDWARE.REJECTED, listener); }; }, []); @@ -106,7 +106,7 @@ export const MiniLedgerAction: React.FC = ({ React.useEffect(() => { if (task.status === 'active' && status === 'DISCONNECTED') { - eventBus.emit(EVENTS.LEDGER.REJECTED, 'DISCONNECTED'); + eventBus.emit(EVENTS.COMMON_HARDWARE.REJECTED, 'DISCONNECTED'); } }, [task.status, status]); const { t } = useTranslation(); diff --git a/src/ui/views/Approval/components/TxComponents/BalanceChangeWrapper.tsx b/src/ui/views/Approval/components/TxComponents/BalanceChangeWrapper.tsx index 56b4391d8ad..d3907cf95b7 100644 --- a/src/ui/views/Approval/components/TxComponents/BalanceChangeWrapper.tsx +++ b/src/ui/views/Approval/components/TxComponents/BalanceChangeWrapper.tsx @@ -44,7 +44,7 @@ export const BalanceChangeWrapper: React.FC = ({ balanceChange.receive_nft_list.length + balanceChange.receive_token_list.length + balanceChange.send_nft_list.length + - balanceChange.send_nft_list.length <= + balanceChange.send_token_list.length <= 0 ) { return true; diff --git a/src/ui/views/ApprovalManagePage/components/BatchRevoke/RevokeActionLedgerButton.tsx b/src/ui/views/ApprovalManagePage/components/BatchRevoke/RevokeActionLedgerButton.tsx index 20b76a38d8e..7813fc17817 100644 --- a/src/ui/views/ApprovalManagePage/components/BatchRevoke/RevokeActionLedgerButton.tsx +++ b/src/ui/views/ApprovalManagePage/components/BatchRevoke/RevokeActionLedgerButton.tsx @@ -59,16 +59,16 @@ export const RevokeActionLedgerButton: React.FC<{ } }; - eventBus.addEventListener(EVENTS.LEDGER.REJECTED, listener); + eventBus.addEventListener(EVENTS.COMMON_HARDWARE.REJECTED, listener); return () => { - eventBus.removeEventListener(EVENTS.LEDGER.REJECTED, listener); + eventBus.removeEventListener(EVENTS.COMMON_HARDWARE.REJECTED, listener); }; }, [task.addRevokeTask]); React.useEffect(() => { if (task.status === 'active' && status === 'DISCONNECTED') { - eventBus.emit(EVENTS.LEDGER.REJECTED, 'DISCONNECTED'); + eventBus.emit(EVENTS.COMMON_HARDWARE.REJECTED, 'DISCONNECTED'); } }, [task.status, status]); diff --git a/src/ui/views/HDManager/QRCodeManager.tsx b/src/ui/views/HDManager/QRCodeManager.tsx index ee97e0bd59b..e81351c8467 100644 --- a/src/ui/views/HDManager/QRCodeManager.tsx +++ b/src/ui/views/HDManager/QRCodeManager.tsx @@ -104,19 +104,25 @@ export const QRCodeManager: React.FC = ({ brand }) => { try { setLoading(true); - if (type !== setting.type) { + if (setting.type && type !== setting.type) { /** * This code is written to be consistent with the behavior of importing wallets via QR Code. */ await removeAddressAndForgetDevice(false); } - await wallet.requestKeyring( - KEYSTONE_TYPE, - 'getAddressesViaUSB', - keyringId, - type - ); + try { + await wallet.requestKeyring( + KEYSTONE_TYPE, + 'getAddressesViaUSB', + keyringId, + type + ); + } catch (e) { + // ignore + console.error(e); + } + await getCurrentAccounts(); setLoading(false); } catch (error) { diff --git a/src/ui/views/Swap/Component/History.tsx b/src/ui/views/Swap/Component/History.tsx index c393ad24798..f9a53597b0b 100644 --- a/src/ui/views/Swap/Component/History.tsx +++ b/src/ui/views/Swap/Component/History.tsx @@ -2,7 +2,6 @@ import { Popup, TokenWithChain } from '@/ui/component'; import React, { forwardRef, useMemo } from 'react'; import { useSwapHistory } from '../hooks'; import { SwapItem, TokenItem } from '@rabby-wallet/rabby-api/dist/types'; -import { CHAINS_LIST } from '@debank/common'; import { formatAmount, formatUsdValue, openInTab, sinceTime } from '@/ui/utils'; import { getTokenSymbol } from '@/ui/utils/token'; import { TooltipWithMagnetArrow } from '@/ui/component/Tooltip/TooltipWithMagnetArrow'; @@ -81,7 +80,10 @@ const Transaction = forwardRef( const isPending = data.status === 'Pending'; const isCompleted = data?.status === 'Completed'; const time = data?.finished_at || data?.create_at; - const targetDex = DEX?.[data?.dex_id]?.name || data?.dex_id || ''; + const targetDex = + data?.dex_id === '0xV2' + ? '0x' + : DEX?.[data?.dex_id]?.name || data?.dex_id || ''; const txId = data?.tx_id; const chainItem = useMemo( () => diff --git a/src/ui/views/Swap/Component/Main.tsx b/src/ui/views/Swap/Component/Main.tsx index 2d7f55a4fe2..e10cac955f9 100644 --- a/src/ui/views/Swap/Component/Main.tsx +++ b/src/ui/views/Swap/Component/Main.tsx @@ -292,7 +292,7 @@ export const Main = () => { .toNumber(), slippage: new BigNumber(slippage).div(100).toNumber(), }, - dex_id: activeProvider?.name.replace('API', '') || 'WrapToken', + dex_id: activeProvider?.name || 'WrapToken', }, }, { @@ -359,7 +359,7 @@ export const Main = () => { .toNumber(), slippage: new BigNumber(slippage).div(100).toNumber(), }, - dex_id: activeProvider?.name.replace('API', '') || 'WrapToken', + dex_id: activeProvider?.name || 'WrapToken', }, }, { diff --git a/src/ui/views/Swap/hooks/quote.tsx b/src/ui/views/Swap/hooks/quote.tsx index bf9ee9420d7..8b9bf6de1ae 100644 --- a/src/ui/views/Swap/hooks/quote.tsx +++ b/src/ui/views/Swap/hooks/quote.tsx @@ -88,8 +88,7 @@ export const useQuoteMethods = () => { .toNumber(), slippage: new BigNumber(slippage).div(100).toNumber(), }, - // 0xAPI => 0x - dex_id: dexId.replace('API', ''), + dex_id: dexId, tx_id: txId, tx, }), diff --git a/yarn.lock b/yarn.lock index 155cc5a09fd..78130aa34e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4808,10 +4808,10 @@ sha256-uint8array "^0.10.3" url-parse "^1.5.1" -"@rabby-wallet/rabby-swap@0.0.40": - version "0.0.40" - resolved "https://registry.yarnpkg.com/@rabby-wallet/rabby-swap/-/rabby-swap-0.0.40.tgz#cabca6192b500789450d53d75b134004c752f022" - integrity sha512-OLBlek+xW6qNJzeU8XVv+6jDkNN2QN89kG66nKS6TtN/IQmdDOSmPfAqNFPpI6UptLG/BUyO8Fcv7LLDLwXvEg== +"@rabby-wallet/rabby-swap@0.0.42": + version "0.0.42" + resolved "https://registry.yarnpkg.com/@rabby-wallet/rabby-swap/-/rabby-swap-0.0.42.tgz#b0fc3c156874e0566df94c892752d01f0e1df1fb" + integrity sha512-k1yhqs+UROMCBseQBrirZ/qwa0iEC7wozDmeOTSJQEKdEXSifhL0W4HLoL1Zh2mZQytv3b86TN8TvoI7Yboh0Q== dependencies: "@ethersproject/abi" "^5.7.0" axios "^0.27.2"