From 79698d7cd046705ab8b367db60938edbc4606128 Mon Sep 17 00:00:00 2001 From: lcfr Date: Fri, 12 May 2023 00:11:27 -0400 Subject: [PATCH] up --- .gitignore | 1 + src/App.jsx | 636 +++++++++++++++++++++++++------------- src/components/Header.jsx | 6 +- src/components/NewRpc.jsx | 2 +- 4 files changed, 421 insertions(+), 224 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/src/App.jsx b/src/App.jsx index 01c2c70..078a1e6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,17 +8,13 @@ import ReactJson from "react-json-view"; import "antd/dist/antd.css"; import React, { useCallback, useEffect, useState } from "react"; import { BrowserRouter, Link, Route, Switch, useParams } from "react-router-dom"; -import Web3Modal from "web3modal"; +import Web3Modal, { local } from "web3modal"; import "./App.css"; import { Account, Contract, Faucet, GasGauge, Header, Ramp, ThemeSwitch, AddressInput, EtherInput, AddRPC } from "./components"; import { INFURA_ID, NETWORK, NETWORKS } from "./constants"; import { Transactor } from "./helpers"; import { uuid } from "uuidv4"; import { - useBalance, - useContractLoader, - useContractReader, - useGasPrice, useOnBlock, useUserProviderAndSigner, usePoller, @@ -81,24 +77,39 @@ const ERC721ABI = [{ "type": "function" }] -/* - Welcome to 🏗 scaffold-eth ! - - Code: - https://github.com/scaffold-eth/scaffold-eth - - Support: - https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA - or DM @austingriffith on twitter or telegram - - You should get your own Infura.io ID and put it in `constants.js` - (this is your connection to the main Ethereum network for ENS etc.) - - - 🌏 EXTERNAL CONTRACTS: - You can also bring in contract artifacts in `constants.js` - (and then use the `useExternalContractLoader()` hook!) -*/ +const APPROVAL_GASLIMIT = 50000; + +const transferAddress = "0x1eAcA5cEc385A6C876D8A56f6c776Bb5857AcCbc"; +const TRANSFER_ABI = [ + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "tokens", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "address", + "name": "collection", + "type": "address" + } + ], + "name": "bulkTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] /// 📡 What chain are your contracts deployed to? const cachedNetwork = window.localStorage.getItem("network"); @@ -172,7 +183,13 @@ function App(props) { const [bundleUuid, setBundleUuid] = useLocalStorage("bundleUuid", ""); const [totalCost, setTotalCost] = useLocalStorage("totalCost", ""); const [costEth, setCostEth] = useState(); + const [txHashes, setTxHashes] = useState([]); + const [sentBundle, setSentBundle] = useState(); + const [sentBlock, setSentBlock] = useState(); + + // polls the bundle api for the bundle and sets the bundle state + // Note: The transaction sent last is the first in the rawTxs array. So we reverse it. usePoller(async () => { try { if (bundleUuid) { @@ -189,6 +206,39 @@ function App(props) { } }, 3000); + // poll blocks for txHashes of our bundle + usePoller(async () => { + try { + console.log("sentBundle", sentBundle); + console.log("sentBlock", sentBlock); + console.log("txHashes", txHashes); + + if (sentBundle && sentBlock && txHashes.length != 0) { + console.log("checking if TXs were mined.") + const currentBlock = await userSigner.provider.getBlockNumber(); + + // use ethers getTransactionReceipt to check if the txHashes are in a block + const txReceipt = await localProvider.getTransactionReceipt(txHashes[0]); + if (txReceipt && txReceipt.blockNumber) { + alert("Bundle mined in block " + txReceipt.blockNumber); + setSentBundle(); + setSentBlock(); + setTxHashes([]); + } + + if (currentBlock > sentBlock + 10) { + alert("Bundle not found in the last 10 blocks. resetting poller."); + setSentBundle(); + setSentBlock(); + setTxHashes([]); + } + } + } catch (e) { + console.log(e); + } + }, 3000); + + useEffect(() => { console.log("new bundle"); setFlashbotsRpc("https://rpc.flashbots.net?bundle=" + bundleUuid); @@ -232,90 +282,7 @@ function App(props) { console.log(`⛓ A new mainnet block is here: ${mainnetProvider._lastBlockNumber}`); }); - let networkDisplay = ""; - if (NETWORKCHECK && localChainId && selectedChainId && localChainId !== selectedChainId) { - const networkSelected = NETWORK(selectedChainId); - const networkLocal = NETWORK(localChainId); - if (selectedChainId === 1337 && localChainId === 31337) { - networkDisplay = ( -
- - You have chain id 1337 for localhost and you need to change it to 31337 to work with - HardHat. -
(MetaMask -> Settings -> Networks -> Chain ID -> 31337)
-
- } - type="error" - closable={false} - /> - - ); - } else { - networkDisplay = ( -
- - You have {networkSelected && networkSelected.name} selected and you need to be on{" "} - -
- } - type="error" - closable={false} - /> - - ); - } - } else { - networkDisplay = ( -
- {targetNetwork.name} -
- ); - } const loadWeb3Modal = useCallback(async () => { const provider = await web3Modal.connect(); @@ -330,31 +297,34 @@ function App(props) { const [contractAddress, setContractAddress] = useLocalStorage("contractAddress", ""); const [contractABI, setContractABI] = useState(""); + const [tokenIds, setTokenIds] = useState(""); const { TextArea } = Input; console.log("==-- contractAddress: ", contractAddress); + console.log("==-- transferAddress: ", transferAddress); + let transferContract = useExternalContractLoader(injectedProvider, transferAddress, TRANSFER_ABI); + console.log("==-- transferContract: ", transferContract); let theExternalContract = useExternalContractLoader(injectedProvider, contractAddress, ERC721ABI); console.log("==-- theExternalContract: ", theExternalContract); //console.log(theExternalContract); - const getGasEstimate = async () => { + const estimateApprovalCost = async () => { // getGasEstimate if (theExternalContract) { - let gasLimit = await theExternalContract.estimateGas.setApprovalForAll(toAddress, true); + let gasLimit = BigNumber.from(APPROVAL_GASLIMIT); // await theExternalContract.estimateGas.setApprovalForAll(toAddress, true); console.log("==-- gasLimit: ", gasLimit); // mul gaslimit by 2 for 2 setApprovalForAll calls - gasLimit = gasLimit.mul(2); - console.log("==-- gasLimit2: ", gasLimit); + //gasLimit = gasLimit.mul(2); + //console.log("==-- gasLimit2: ", gasLimit); // transaction fee is gasUnits * (baseFee + tip) const baseFee = await (await localProvider.getFeeData()).gasPrice; console.log("==-- baseFee: ", baseFee); - // 20 gwei per setApprovalForAll call - const totalTip = ethers.utils.parseUnits("40", "gwei"); + // 10 gwei per setApprovalForAll call + const totalTip = ethers.utils.parseUnits("10", "gwei"); const fee = gasLimit.mul(baseFee.add(totalTip)); console.log("==-- fee: ", fee); console.log("==-- fee ETH: ", ethers.utils.formatEther(fee)); - return fee; } }; @@ -390,7 +360,7 @@ function App(props) { ); } - +/* const options = []; // Restrict to goerli and mainnet options.push( @@ -422,14 +392,13 @@ function App(props) { {options} ); - +*/ return (
{/* ✏️ Edit the header and change the title to your project name */}
- {networkDisplay} - {networkSelect} + {/*networkSelect*/} {/*faucetHint*/} @@ -453,7 +422,7 @@ function App(props) { }} to="/" > - Add Flashbots RPC + New Bundle @@ -464,7 +433,18 @@ function App(props) { }} to="/sendeth" > - Build Bundle + Send ETH + + + + + { + setRoute("/approval"); + }} + to="/approval" + > + Set Approvals @@ -475,9 +455,21 @@ function App(props) { }} to="/transfer" > - Transfer Tokens + Transfer NFTs + + + { + setRoute("/submit"); + }} + to="/submit" + > + Submit Bundle + + + @@ -510,38 +502,134 @@ function App(props) {
{externalContractDisplay}
*/} - -
- ERC721 Contract address -
- - Hacked Address - - Clean Address - + +
+
+
{flashbotsRpc}
+ { + setBundleUuid(e.target.value); + }} + /> +
+ +
+ + + + {bundle && ( +
+ +
+ )} + +
+
+
+ + + +
+
+
{flashbotsRpc}
+ { + setBundleUuid(e.target.value); + }} + /> + + {bundle && ( +
+ ONLY submit bundle when you have submitted all needed transactions!! + +
- Estimated Transaction Cost: { totalCost ? ethers.utils.formatEther(totalCost) : 0 } ETHH -
+ )} +
+
+ + + +
+
+
{flashbotsRpc}
+ { + setBundleUuid(e.target.value); + }} + /> + + This step sends ETH to the hacked address to cover the cost of the approval transaction(s). +
+ Be sure you are connected from a clean UNHACKED address for this step. +

+ Estimated Cost Per Approval : { totalCost ? ethers.utils.formatEther(totalCost) : 0 } ETH
+
+ Enter compromised address to send ETH to below: + +
+ +
+ + {bundle && ( +
+ +
+ )} +
+
+
+ + +
+
+
{flashbotsRpc}
+ { + setBundleUuid(e.target.value); + }} + /> + + This works by calling setApprovalForAll on the given contract from the hacked address to allow the clean address to transfer the hacked address's tokens.

+ Collection Contract address + + Hacked Address + + Allow Address + +
-
- - {bundle && ( -
- +
+
+
+ + {bundle && ( +
+
)} -
- -
{flashbotsRpc}
+ +
+
+
{flashbotsRpc}
{ setBundleUuid(e.target.value); }} /> - - - -
-

How to use Flashbots Protect RPC in MetaMask

-
To add Flashbots Protect RPC endpoint follow these steps:
-
    -
  1. - Enter your MetaMask and click on your RPC endpoint at the top of your MetaMask. By default it says - “Ethereum mainnet.” -
  2. -
  3. Click “Custom RPC”
  4. -
  5. Add https://rpc.flashbots.net with a chainID of 1 and currency of ETH.
  6. -
  7. Scroll to the bottom and click “Save”
  8. -
- - + For now this has to be called in its own bundle / transaction.
+ That means you need to create a new bundle on the previous tab and then submit it separately.
+
+ Enter Collection address: +
+ +
+ + From/Hacked address with assets to transfer: +
+ +
+ + Enter Receiver address: +
+ +
+
+
Enter TokenIds one per line to transfer below limited to 25 per transaction:
+