diff --git a/package.json b/package.json index ceda14e3a1..fb402ea976 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "lint:code": "next lint", "lint:code:fix": "next lint --fix", "lint:formatting": "prettier src pages scripts cypress . --check", - "lint:formatting:fix": "prettier src pages scripts cypress . --write", + "lint:formatting:fix": "prettier src pages scripts cypress . --write", "lint": "yarn lint:code && yarn lint:formatting", "lint:fix": "yarn lint:code:fix && yarn lint:formatting:fix", "post-update": "echo \"codesandbox preview only, need an update\" && yarn upgrade --latest", @@ -86,7 +86,8 @@ "react-number-format": "^4.9.1", "reflect-metadata": "^0.1.13", "remark-gfm": "^3.0.1", - "subscriptions-transport-ws": "^0.11.0" + "subscriptions-transport-ws": "^0.11.0", + "web3-ledgerhq-frame-connector": "^1.0.1" }, "devDependencies": { "@babel/core": "^7.18.9", diff --git a/pages/_app.page.tsx b/pages/_app.page.tsx index 663235938e..eda6600d0d 100644 --- a/pages/_app.page.tsx +++ b/pages/_app.page.tsx @@ -29,7 +29,6 @@ import { ModalContextProvider } from 'src/hooks/useModal'; import { PermissionProvider } from 'src/hooks/usePermissions'; import { WalletModalContextProvider } from 'src/hooks/useWalletModal'; import { Web3ContextProvider } from 'src/libs/web3-data-provider/Web3Provider'; -// import { Web3ContextProvider } from 'src/libs/web3-data-provider/Web3ContextProvider'; import { TxBuilderProvider } from 'src/providers/TxBuilderProvider'; import { apolloClient } from 'src/utils/apolloClient'; diff --git a/src/libs/web3-data-provider/WalletOptions.ts b/src/libs/web3-data-provider/WalletOptions.ts index 17b8afbf8e..1986cf825e 100644 --- a/src/libs/web3-data-provider/WalletOptions.ts +++ b/src/libs/web3-data-provider/WalletOptions.ts @@ -5,6 +5,7 @@ import { WalletConnectConnector } from '@web3-react/walletconnect-connector'; import { WalletLinkConnector } from '@web3-react/walletlink-connector'; import { TorusConnector } from '@web3-react/torus-connector'; import { FrameConnector } from '@web3-react/frame-connector'; +import { LedgerHQFrameConnector } from 'web3-ledgerhq-frame-connector'; import { getNetworkConfig, getSupportedChainIds } from 'src/utils/marketsAndNetworksConfig'; import { UnsupportedChainIdError } from '@web3-react/core'; import { SafeAppConnector } from '@gnosis.pm/safe-apps-web3-react'; @@ -16,6 +17,7 @@ export enum WalletType { TORUS = 'torus', FRAME = 'frame', GNOSIS = 'gnosis', + LEDGER = 'ledger', } const APP_NAME = 'Aave'; @@ -28,6 +30,8 @@ export const getWallet = ( const supportedChainIds = getSupportedChainIds(); switch (wallet) { + case WalletType.LEDGER: + return new LedgerHQFrameConnector({}); case WalletType.INJECTED: return new InjectedConnector({}); case WalletType.WALLET_LINK: diff --git a/src/libs/web3-data-provider/Web3Provider.tsx b/src/libs/web3-data-provider/Web3Provider.tsx index da2b69cc3e..9b159ee795 100644 --- a/src/libs/web3-data-provider/Web3Provider.tsx +++ b/src/libs/web3-data-provider/Web3Provider.tsx @@ -18,6 +18,7 @@ import { API_ETH_MOCK_ADDRESS, transactionType } from '@aave/contract-helpers'; import { WalletConnectConnector } from '@web3-react/walletconnect-connector'; import { WalletLinkConnector } from '@web3-react/walletlink-connector'; import { TorusConnector } from '@web3-react/torus-connector'; +import { isLedgerDappBrowserProvider } from 'web3-ledgerhq-frame-connector'; export type ERC20TokenType = { address: string; @@ -64,9 +65,10 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil const [loading, setLoading] = useState(false); const [tried, setTried] = useState(false); const [deactivated, setDeactivated] = useState(false); - const [triedSafe, setTriedSafe] = useState(false); - const [switchNetworkError, setSwitchNetworkError] = useState(); + const [triedGnosisSafe, setTriedGnosisSafe] = useState(false); const [triedCoinbase, setTriedCoinbase] = useState(false); + const [triedLedger, setTriedLedger] = useState(false); + const [switchNetworkError, setSwitchNetworkError] = useState(); // for now we use network changed as it returns the chain string instead of hex // const handleChainChanged = (chainId: number) => { @@ -175,11 +177,24 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil return false; }; + // third, try connecting to ledger + useEffect(() => { + if (!triedLedger && triedGnosisSafe && triedCoinbase) { + // check if the DApp is hosted within Ledger iframe + const canConnectToLedger = isLedgerDappBrowserProvider(); + if (canConnectToLedger) { + connectWallet(WalletType.LEDGER).finally(() => setTriedLedger(true)); + } else { + setTriedLedger(true); + } + } + }, [connectWallet, triedGnosisSafe, triedCoinbase, triedLedger, setTriedLedger]); + // second, try connecting to coinbase useEffect(() => { if (!triedCoinbase) { // do check if condition applies to try and connect directly to coinbase - if (triedSafe) { + if (triedGnosisSafe) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const injectedProvider = (window as any)?.ethereum; if (injectedProvider?.isCoinbaseBrowser) { @@ -208,34 +223,34 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil } } } - }, [connectWallet, triedSafe, setTriedCoinbase, triedCoinbase]); + }, [connectWallet, triedGnosisSafe, setTriedCoinbase, triedCoinbase]); // first, try connecting to a gnosis safe useEffect(() => { - if (!triedSafe) { + if (!triedGnosisSafe) { const gnosisConnector = getWallet(WalletType.GNOSIS); // @ts-expect-error isSafeApp not in abstract connector type gnosisConnector.isSafeApp().then((loadedInSafe) => { if (loadedInSafe) { connectWallet(WalletType.GNOSIS) .then(() => { - setTriedSafe(true); + setTriedGnosisSafe(true); }) .catch(() => { - setTriedSafe(true); + setTriedGnosisSafe(true); }); } else { - setTriedSafe(true); + setTriedGnosisSafe(true); } }); } - }, [connectWallet, setTriedSafe, triedSafe]); + }, [connectWallet, setTriedGnosisSafe, triedGnosisSafe]); // handle logic to eagerly connect to the injected ethereum provider, // if it exists and has granted access already useEffect(() => { const lastWalletProvider = localStorage.getItem('walletProvider'); - if (!active && !deactivated && triedSafe && triedCoinbase) { + if (!active && !deactivated && triedGnosisSafe && triedCoinbase && triedLedger) { if (!!lastWalletProvider) { connectWallet(lastWalletProvider as WalletType).catch(() => { setTried(true); @@ -257,7 +272,16 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil // }); } } - }, [activate, setTried, active, connectWallet, deactivated, triedSafe, triedCoinbase]); + }, [ + activate, + setTried, + active, + connectWallet, + deactivated, + triedGnosisSafe, + triedCoinbase, + triedLedger, + ]); // if the connection worked, wait until we get confirmation of that to flip the flag useEffect(() => { diff --git a/yarn.lock b/yarn.lock index 7efbea0cd9..9813b74bf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3108,6 +3108,13 @@ resolved "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz" integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw== +"@ledgerhq/iframe-provider@0": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@ledgerhq/iframe-provider/-/iframe-provider-0.4.2.tgz#2b63892bb3ab9a0719d2b00488be18b176ad6b7e" + integrity sha512-RbdwvQow/ITLk0TLb6c3M7y8IyjopIGXhhuUEMjqTU6PZhAL9Gl7TzH8INit9x9cOeG2WCuV+ZbHQ2oWsLfJ+A== + dependencies: + eventemitter3 "^4.0.0" + "@lingui/babel-plugin-extract-messages@^3.13.2": version "3.13.2" resolved "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.13.2.tgz" @@ -4591,7 +4598,7 @@ dependencies: "@walletconnect/window-getters" "^1.0.0" -"@web3-react/abstract-connector@6.0.7", "@web3-react/abstract-connector@^6.0.7": +"@web3-react/abstract-connector@6", "@web3-react/abstract-connector@6.0.7", "@web3-react/abstract-connector@^6.0.7": version "6.0.7" resolved "https://registry.yarnpkg.com/@web3-react/abstract-connector/-/abstract-connector-6.0.7.tgz#401b3c045f1e0fab04256311be49d5144e9badc6" integrity sha512-RhQasA4Ox8CxUC0OENc1AJJm8UTybu/oOCM61Zjg6y0iF7Z0sqv1Ai1VdhC33hrQpA8qSBgoXN9PaP8jKmtdqg== @@ -4637,7 +4644,7 @@ "@web3-react/abstract-connector" "^6.0.7" "@web3-react/types" "^6.0.7" -"@web3-react/types@^6.0.7": +"@web3-react/types@6", "@web3-react/types@^6.0.7": version "6.0.7" resolved "https://registry.yarnpkg.com/@web3-react/types/-/types-6.0.7.tgz#34a6204224467eedc6123abaf55fbb6baeb2809f" integrity sha512-ofGmfDhxmNT1/P/MgVa8IKSkCStFiyvXe+U5tyZurKdrtTDFU+wJ/LxClPDtFerWpczNFPUSrKcuhfPX1sI6+A== @@ -5574,9 +5581,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001366: - version "1.0.30001399" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001399.tgz" - integrity sha512-4vQ90tMKS+FkvuVWS5/QY1+d805ODxZiKFzsU8o/RsVJz49ZSRR8EjykLJbqhzdPgadbX6wB538wOzle3JniRA== + version "1.0.30001375" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz" + integrity sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw== capital-case@^1.0.4: version "1.0.4" @@ -7451,9 +7458,9 @@ eventemitter3@4.0.4: resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz" integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== -eventemitter3@4.0.7: +eventemitter3@4.0.7, eventemitter3@^4.0.0: version "4.0.7" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== eventemitter3@^3.1.0: @@ -12680,7 +12687,7 @@ through2@~0.4.1: resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -tiny-invariant@^1.0.6: +tiny-invariant@1, tiny-invariant@^1.0.6: version "1.2.0" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== @@ -13378,6 +13385,16 @@ web3-eth-iban@1.7.0: bn.js "^4.11.9" web3-utils "1.7.0" +web3-ledgerhq-frame-connector@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/web3-ledgerhq-frame-connector/-/web3-ledgerhq-frame-connector-1.0.1.tgz#7554fb5e9d1da19e1ab24e434dbc4d0c012c0527" + integrity sha512-AnSISDK0csoi2V/dMAjcomK8ZbFAYk22KArSoG/chDKlvLgxBgXafWheQPgV7540Efd/wMbtcjo4NotY2M3nDA== + dependencies: + "@ledgerhq/iframe-provider" "0" + "@web3-react/abstract-connector" "6" + "@web3-react/types" "6" + tiny-invariant "1" + web3-providers-http@1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.7.0.tgz"