diff --git a/docs/flashbots-protect/overview.mdx b/docs/flashbots-protect/overview.mdx index 07575bbd6..b14aa63cf 100644 --- a/docs/flashbots-protect/overview.mdx +++ b/docs/flashbots-protect/overview.mdx @@ -2,7 +2,7 @@ title: Overview --- -import ProtectButton from "protect-button" +import AddRpcConfigurator from "@site/src/components/AddRpcConfigurator" Flashbots Protect makes it easy for everyday users and developers to use Flashbots for frontrunning protection. We abstract away the complexity of submitting bundles to the Flashbots Auction and make integrating as simple as adding a URL to MetaMask. @@ -14,7 +14,7 @@ At a high level these are some of the major benefits of integrating Flashbots Pr - **No failed transactions:** transactions will only be included if it doesn't include any reverts, so users don't pay for failed transactions. Note: transactions could be included in uncled blocks, emitted to the mempool, and then included on-chain. - **Etherscan integration:** users can see the status of their transactions on Etherscan. -Connect Wallet to Protect +

diff --git a/package.json b/package.json index e237e1bb7..b7aea60dc 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "docusaurus-plugin-sass": "0.2.1", "docusaurus2-dotenv": "^1.4.0", "dotenv": "^8.2.0", + "metamask-react": "^2.6.0", "protect-button": "0.2", "react": "^17.0.1", "react-dom": "^17.0.1", diff --git a/src/components/AddRpcConfigurator/index.tsx b/src/components/AddRpcConfigurator/index.tsx new file mode 100644 index 000000000..92c5ddad2 --- /dev/null +++ b/src/components/AddRpcConfigurator/index.tsx @@ -0,0 +1,158 @@ +import React, { ReactNode, useCallback, useEffect, useState } from "react" +import styles from './styles.module.scss'; +import Button from "../Button/Button"; +import { useMetaMask } from 'metamask-react' +import { StyledCheckbox } from "../StyledCheckbox"; +import clsx from "clsx"; + +interface IAddRpcConfigurator { +} + +const RPCS = { + 1: "https://rpc.flashbots.net", + 5: "https://rpc-goerli.flashbots.net", + 11155111: "https://rpc-sepolia.flashbots.net" +} + +interface IHintSettings { + calldata: boolean + contractAddress: boolean + functionSelector: boolean + logs: boolean +} + +const defaults = { + experimental: { + calldata: true, + contractAddress: true, + functionSelector: true, + logs: true, + }, + stable: { + calldata: false, + contractAddress: true, + functionSelector: true, + logs: true, + }, + private: { + calldata: false, + contractAddress: false, + functionSelector: false, + logs: false, + } +} + +const AddRpcConfigurator = ({ }: IAddRpcConfigurator) => { + const { status, connect, addChain } = useMetaMask() + + const [targetChain, setTargetChain] = useState<1 | 5 | 11155111>(1) + + const [hintSettings, setHints] = useState({ + calldata: false, + contractAddress: true, + functionSelector: true, + logs: true, + }) + + const [advanced, setAdvanced] = useState(false) + + const addProtectRpc = useCallback(async () => { + if (!RPCS[targetChain]) return + + const addChainParams = { + chainId: `0x${targetChain.toString(16)}`, + chainName: `Flashbots Protect ${ + targetChain === 1 ? "(Mainnet)" : + targetChain === 5 ? "(Goerli)" : + targetChain === 11155111 ? "(Sepolia)" : + ` on chain ${targetChain}`}`, + iconUrls: ["https://docs.flashbots.net/img/logo.png"], + nativeCurrency: { + name: "Ethereum", + symbol: "ETH", + decimals: 18, + }, + rpcUrls: [RPCS[targetChain]], + } + if (addChain) { + try { + addChain(addChainParams) + } catch (err) { + // handle "add" error + console.error("addChain failed") + throw err + } + } else if ("ethereum" in window) { + // do it manually with window.ethereum + try { + const ethereum: any = window.ethereum + await ethereum.request({ + method: 'wallet_addEthereumChain', + params: [addChainParams], + }) + } catch (err) { + // handle "add" error + console.error("addChain failed") + throw err + } + } else { + throw new Error("ethereum provider not found") + } + }, []) + + return <> + { status === 'notConnected' && ( + + )} + { status !== 'connected' && status !== "notConnected" && ( + Connecting to MetaMask... + )} + { status === 'connected' && ( +
+ +
+
+ + Open settings + +
+
+ setHints({ + ...hintSettings, + calldata: newHint + })}> + calldata + + setHints({ + ...hintSettings, + contractAddress: newHint + })}> + contractAddress + + setHints({ + ...hintSettings, + functionSelector: newHint + })}> + functionSelector + + setHints({ + ...hintSettings, + logs: newHint + })}> + logs + +
+
+
+ )} + +} + +export default AddRpcConfigurator \ No newline at end of file diff --git a/src/components/AddRpcConfigurator/styles.module.scss b/src/components/AddRpcConfigurator/styles.module.scss new file mode 100644 index 000000000..759b13eb1 --- /dev/null +++ b/src/components/AddRpcConfigurator/styles.module.scss @@ -0,0 +1,17 @@ +.root { + display: flex; + flex-direction: column; +} + +.settings { + overflow: hidden; + opacity: 0; + transition-duration: 300ms; + transition-timing-function: ease-in-out; + transform: rotateX(-90deg); + transform-origin: top center; + &.active { + opacity: 1; + transform: rotateX(0); + } +} \ No newline at end of file diff --git a/src/components/StyledCheckbox/index.tsx b/src/components/StyledCheckbox/index.tsx new file mode 100644 index 000000000..6b1d18b2f --- /dev/null +++ b/src/components/StyledCheckbox/index.tsx @@ -0,0 +1,39 @@ +import React, { ReactNode, useState } from "react"; +import styles from './styles.module.scss'; +import clsx from "clsx"; + +interface IStyledCheckbox { + children: ReactNode | ReactNode[] + active: boolean + setActive: (activeState: boolean) => void +} + +export const StyledCheckbox = ({ children, active, setActive }: IStyledCheckbox) => { + return ( + + ); +} diff --git a/src/components/StyledCheckbox/styles.module.scss b/src/components/StyledCheckbox/styles.module.scss new file mode 100644 index 000000000..fcba9799b --- /dev/null +++ b/src/components/StyledCheckbox/styles.module.scss @@ -0,0 +1,28 @@ +.root { + display: inline-flex; + align-items: center; + padding: 5px; + + input[type="checkbox"] { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; + } + + .checkbox { + display: inline-block; // set to `inline-block` as `inline elements ignore `height` and `width` + height: 20px; + width: 20px; + background: #fff; + border: 2px #ddd solid; + margin-right: 0.5rem; + &.active { + border-color: purple; + background: purple; + } + } +} diff --git a/src/theme/Layout/index.tsx b/src/theme/Layout/index.tsx index ecf1ebf7d..20bcf464b 100644 --- a/src/theme/Layout/index.tsx +++ b/src/theme/Layout/index.tsx @@ -3,13 +3,16 @@ import Layout from '@theme-original/Layout'; import type LayoutType from '@theme/Layout'; import type {WrapperProps} from '@docusaurus/types'; import { Analytics } from '@vercel/analytics/react'; +import { MetaMaskProvider } from "metamask-react" type Props = WrapperProps; export default function LayoutWrapper(props: Props): JSX.Element { return ( <> - - + + + + ); } diff --git a/yarn.lock b/yarn.lock index fd4ca0dd3..a83d717da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5241,6 +5241,11 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +metamask-react@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/metamask-react/-/metamask-react-2.6.0.tgz#e4b95580a9d64db0cd0b62407cf7146ba4d76062" + integrity sha512-pWlIyiV1C6ztRTzy1CPEqYgA3ZgVfOMLXzKs2jenGJ2ZF+L6wXwRp5cU82q+r/YwyzrWUxMNwoEwNxtzYUtbEg== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"