├── README.md ├── exploit-contract ├── .DS_Store ├── .gitignore ├── README.md ├── artifacts │ ├── build-info │ │ └── 27ffb7e58541d8fe5dbdf53045adde53.json │ └── contracts │ │ └── BrokenContract.sol │ │ ├── BrokenContract.dbg.json │ │ ├── brokenContractABI.json │ │ └── contract_abi.json ├── contracts │ └── BrokenContract.sol ├── hardhat.config.js ├── package-lock.json ├── package.json ├── pics │ ├── exploit-flashbot.png │ ├── hex_data.gif │ └── tmp.md └── scripts │ ├── deploy.js │ └── flashbotWithdraw.js └── nft-sponosored-tx ├── .gitignore ├── README.md ├── abi └── nft.json ├── package-lock.json ├── package.json ├── pics ├── exploit-withdraw.png ├── js_code.png ├── nft-transfer-example.png ├── sponsor_address.png ├── sponsor_rescued.png ├── stuck_NFT.png └── tmp.md └── src └── sponsorTx.js /README.md: -------------------------------------------------------------------------------- 1 | # Flashbots Playground 2 | 3 | This repository showcases several use-cases of the Flashbots relayer service to avoid getting exploited by sniper bots on the Ethereum network. These two examples are adapted from the [video tutorials](https://www.youtube.com/channel/UCbix_rg-QztSRMn9OBASFwg) created by [Scott Bigelow](https://twitter.com/epheph). 4 | 5 | 6 | - [Rescuing an NFT with Flashbots](https://github.com/schepal/flashbots_playground/tree/main/nft-sponosored-tx): execute a sponsored transaction bundle using Flashbots to rescue an NFT from an exploited Ethereum address. 7 | 8 | 9 | - [Exploting a Smart-Contract with Flashbots](https://github.com/schepal/flashbots_playground/tree/main/exploit-contract): exploit a smart-contract with Flashbots without prematurely revealing this opportunity to other sniper bots. 10 | 11 | 12 | -------------------------------------------------------------------------------- /exploit-contract/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/exploit-contract/.DS_Store -------------------------------------------------------------------------------- /exploit-contract/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | cache 3 | .env 4 | coverage 5 | coverage.json 6 | typechain 7 | 8 | #Hardhat files 9 | cache 10 | artifacts 11 | -------------------------------------------------------------------------------- /exploit-contract/README.md: -------------------------------------------------------------------------------- 1 | ### Exploting A Smart-Contract Using Flashbots 2 | 3 | ### Problem: 4 | 5 | In this case a developer is seeking to exploit a poorly written smart-contract. As shown below anyone can call `BrokenContract` and withdraw the contract's entire ETH balance by executing the `withdraw` function. 6 | 7 | 8 | ```solidity 9 | //SPDX-License-Identifier: Unlicense 10 | pragma solidity ^0.8.0; 11 | 12 | contract BrokenContract { 13 | // allow this contract to receive ether 14 | receive() external payable {} 15 | // this function will withdraw the entire ether balance in this smart-contract to the first person who calls it 16 | function withdraw() external { 17 | payable(msg.sender).transfer(address(this).balance); 18 | } 19 | // this function can be used to keep track of the contract's ethereum balance 20 | function balance() external view returns(uint) { 21 | return address(this).balance; 22 | } 23 | } 24 | ``` 25 | 26 | Here's the problem - if the developer sends an normal Ethereum transaction calling the `withdraw` function they will likely get front-runned by other sniper bots. Recall anytime a transaction is made interacting with a smart-contract, there will be associated call data encoded which is available for anyone to see when the transcation hits the mempool. In this case if the developer wanted to call `withdraw` then he would have to sign a transaction with the following hex call-data `0x3ccfd60b` as shown in the animation below. Given this transaction is sitting in the public mempool before it gets confirmed, all of this transaction data is visible to sniper bots. 27 | 28 | ![](pics/hex_data.gif) 29 | 30 | 31 | This data field is in hex and is not decipherable to humans, however, sophisticated sniper bots can take this data value and simulate what the transaction would look like if they ran this transaction themselves. Therefore, if this transaction is left in the public mempool, then these sophisticated sniper bots can still front-run the transactions by bidding up higher gas transaction fees. It might get to a point where the sniper bots are paying 0.199 ETH worth of gas fees and receiving 0.2 ETH, hence only resulting in 0.001 ETH of profit. 32 | 33 | ### Solution: 34 | 35 | Rather than sending this transaction through the public mempool, the developer would be better off just using the Flashbots relayer service to mask this transaction from sniper bots. This would ensure the transaction will not be revealed until after it is mined into the blockchain. Therefore, there’s no opportunities for the other sniper bots to front-run the transaction given it is already included in the blockchain. 36 | 37 | ![](pics/exploit-flashbot.png) 38 | 39 | 40 | ### Goerli Addresses Used In This Analysis: 41 | - BrokenContract: [`0xe417a0c129765a049fd360edcd540629265e1ae3`](https://goerli.etherscan.io/address/0xe417a0c129765a049fd360edcd540629265e1ae3#code) 42 | - Flashbots Transaction of calling [`withdraw`](https://goerli.etherscan.io/tx/0x358caae59846ece7dd6d94ffa7c7e8e63f15e24a3aaa91450db34cc705896681) 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /exploit-contract/artifacts/build-info/27ffb7e58541d8fe5dbdf53045adde53.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "27ffb7e58541d8fe5dbdf53045adde53", 3 | "_format": "hh-sol-build-info-1", 4 | "solcVersion": "0.8.4", 5 | "solcLongVersion": "0.8.4+commit.c7e474f2", 6 | "input": { 7 | "language": "Solidity", 8 | "sources": { 9 | "contracts/BrokenContract.sol": { 10 | "content": "//SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\ncontract BrokenContract {\n // allow this contract to receive ether\n receive() external payable {}\n // this function will withdraw the entire ether balance in this smart-contract to the first person who calls it\n function withdraw() external {\n payable(msg.sender).transfer(address(this).balance);\n } \n // this function can be used to keep track of the contract's ethereum balance\n function balance() external view returns(uint) {\n return address(this).balance;\n }\n}\n" 11 | } 12 | }, 13 | "settings": { 14 | "optimizer": { 15 | "enabled": false, 16 | "runs": 200 17 | }, 18 | "outputSelection": { 19 | "*": { 20 | "*": [ 21 | "abi", 22 | "evm.bytecode", 23 | "evm.deployedBytecode", 24 | "evm.methodIdentifiers", 25 | "metadata" 26 | ], 27 | "": [ 28 | "ast" 29 | ] 30 | } 31 | } 32 | } 33 | }, 34 | "output": { 35 | "contracts": { 36 | "contracts/BrokenContract.sol": { 37 | "BrokenContract": { 38 | "abi": [ 39 | { 40 | "inputs": [], 41 | "name": "balance", 42 | "outputs": [ 43 | { 44 | "internalType": "uint256", 45 | "name": "", 46 | "type": "uint256" 47 | } 48 | ], 49 | "stateMutability": "view", 50 | "type": "function" 51 | }, 52 | { 53 | "inputs": [], 54 | "name": "withdraw", 55 | "outputs": [], 56 | "stateMutability": "nonpayable", 57 | "type": "function" 58 | }, 59 | { 60 | "stateMutability": "payable", 61 | "type": "receive" 62 | } 63 | ], 64 | "evm": { 65 | "bytecode": { 66 | "generatedSources": [], 67 | "linkReferences": {}, 68 | "object": "608060405234801561001057600080fd5b50610124806100206000396000f3fe60806040526004361060295760003560e01c80633ccfd60b146034578063b69ef8a814604857602f565b36602f57005b600080fd5b348015603f57600080fd5b506046606e565b005b348015605357600080fd5b50605a60b6565b6040516065919060cb565b60405180910390f35b3373ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f1935050505015801560b3573d6000803e3d6000fd5b50565b600047905090565b60c58160e4565b82525050565b600060208201905060de600083018460be565b92915050565b600081905091905056fea26469706673582212208652f39739eaffe4b2f0692790a28c82cf7a6eba249c9e10ea60c6a145e9711f64736f6c63430008040033", 69 | "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x124 DUP1 PUSH2 0x20 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x29 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x3CCFD60B EQ PUSH1 0x34 JUMPI DUP1 PUSH4 0xB69EF8A8 EQ PUSH1 0x48 JUMPI PUSH1 0x2F JUMP JUMPDEST CALLDATASIZE PUSH1 0x2F JUMPI STOP JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x3F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x46 PUSH1 0x6E JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x53 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x5A PUSH1 0xB6 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x65 SWAP2 SWAP1 PUSH1 0xCB JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLER PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x8FC SELFBALANCE SWAP1 DUP2 ISZERO MUL SWAP1 PUSH1 0x40 MLOAD PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 DUP9 DUP9 CALL SWAP4 POP POP POP POP ISZERO DUP1 ISZERO PUSH1 0xB3 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP JUMP JUMPDEST PUSH1 0x0 SELFBALANCE SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0xC5 DUP2 PUSH1 0xE4 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 ADD SWAP1 POP PUSH1 0xDE PUSH1 0x0 DUP4 ADD DUP5 PUSH1 0xBE JUMP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DUP7 MSTORE RETURN SWAP8 CODECOPY 0xEA SELFDESTRUCT 0xE4 0xB2 CREATE PUSH10 0x2790A28C82CF7A6EBA24 SWAP13 SWAP15 LT 0xEA PUSH1 0xC6 LOG1 GASLIMIT 0xE9 PUSH18 0x1F64736F6C63430008040033000000000000 ", 70 | "sourceMap": "62:506:0:-:0;;;;;;;;;;;;;;;;;;;" 71 | }, 72 | "deployedBytecode": { 73 | "generatedSources": [ 74 | { 75 | "ast": { 76 | "nodeType": "YulBlock", 77 | "src": "0:439:1", 78 | "statements": [ 79 | { 80 | "body": { 81 | "nodeType": "YulBlock", 82 | "src": "72:53:1", 83 | "statements": [ 84 | { 85 | "expression": { 86 | "arguments": [ 87 | { 88 | "name": "pos", 89 | "nodeType": "YulIdentifier", 90 | "src": "89:3:1" 91 | }, 92 | { 93 | "arguments": [ 94 | { 95 | "name": "value", 96 | "nodeType": "YulIdentifier", 97 | "src": "112:5:1" 98 | } 99 | ], 100 | "functionName": { 101 | "name": "cleanup_t_uint256", 102 | "nodeType": "YulIdentifier", 103 | "src": "94:17:1" 104 | }, 105 | "nodeType": "YulFunctionCall", 106 | "src": "94:24:1" 107 | } 108 | ], 109 | "functionName": { 110 | "name": "mstore", 111 | "nodeType": "YulIdentifier", 112 | "src": "82:6:1" 113 | }, 114 | "nodeType": "YulFunctionCall", 115 | "src": "82:37:1" 116 | }, 117 | "nodeType": "YulExpressionStatement", 118 | "src": "82:37:1" 119 | } 120 | ] 121 | }, 122 | "name": "abi_encode_t_uint256_to_t_uint256_fromStack", 123 | "nodeType": "YulFunctionDefinition", 124 | "parameters": [ 125 | { 126 | "name": "value", 127 | "nodeType": "YulTypedName", 128 | "src": "60:5:1", 129 | "type": "" 130 | }, 131 | { 132 | "name": "pos", 133 | "nodeType": "YulTypedName", 134 | "src": "67:3:1", 135 | "type": "" 136 | } 137 | ], 138 | "src": "7:118:1" 139 | }, 140 | { 141 | "body": { 142 | "nodeType": "YulBlock", 143 | "src": "229:124:1", 144 | "statements": [ 145 | { 146 | "nodeType": "YulAssignment", 147 | "src": "239:26:1", 148 | "value": { 149 | "arguments": [ 150 | { 151 | "name": "headStart", 152 | "nodeType": "YulIdentifier", 153 | "src": "251:9:1" 154 | }, 155 | { 156 | "kind": "number", 157 | "nodeType": "YulLiteral", 158 | "src": "262:2:1", 159 | "type": "", 160 | "value": "32" 161 | } 162 | ], 163 | "functionName": { 164 | "name": "add", 165 | "nodeType": "YulIdentifier", 166 | "src": "247:3:1" 167 | }, 168 | "nodeType": "YulFunctionCall", 169 | "src": "247:18:1" 170 | }, 171 | "variableNames": [ 172 | { 173 | "name": "tail", 174 | "nodeType": "YulIdentifier", 175 | "src": "239:4:1" 176 | } 177 | ] 178 | }, 179 | { 180 | "expression": { 181 | "arguments": [ 182 | { 183 | "name": "value0", 184 | "nodeType": "YulIdentifier", 185 | "src": "319:6:1" 186 | }, 187 | { 188 | "arguments": [ 189 | { 190 | "name": "headStart", 191 | "nodeType": "YulIdentifier", 192 | "src": "332:9:1" 193 | }, 194 | { 195 | "kind": "number", 196 | "nodeType": "YulLiteral", 197 | "src": "343:1:1", 198 | "type": "", 199 | "value": "0" 200 | } 201 | ], 202 | "functionName": { 203 | "name": "add", 204 | "nodeType": "YulIdentifier", 205 | "src": "328:3:1" 206 | }, 207 | "nodeType": "YulFunctionCall", 208 | "src": "328:17:1" 209 | } 210 | ], 211 | "functionName": { 212 | "name": "abi_encode_t_uint256_to_t_uint256_fromStack", 213 | "nodeType": "YulIdentifier", 214 | "src": "275:43:1" 215 | }, 216 | "nodeType": "YulFunctionCall", 217 | "src": "275:71:1" 218 | }, 219 | "nodeType": "YulExpressionStatement", 220 | "src": "275:71:1" 221 | } 222 | ] 223 | }, 224 | "name": "abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed", 225 | "nodeType": "YulFunctionDefinition", 226 | "parameters": [ 227 | { 228 | "name": "headStart", 229 | "nodeType": "YulTypedName", 230 | "src": "201:9:1", 231 | "type": "" 232 | }, 233 | { 234 | "name": "value0", 235 | "nodeType": "YulTypedName", 236 | "src": "213:6:1", 237 | "type": "" 238 | } 239 | ], 240 | "returnVariables": [ 241 | { 242 | "name": "tail", 243 | "nodeType": "YulTypedName", 244 | "src": "224:4:1", 245 | "type": "" 246 | } 247 | ], 248 | "src": "131:222:1" 249 | }, 250 | { 251 | "body": { 252 | "nodeType": "YulBlock", 253 | "src": "404:32:1", 254 | "statements": [ 255 | { 256 | "nodeType": "YulAssignment", 257 | "src": "414:16:1", 258 | "value": { 259 | "name": "value", 260 | "nodeType": "YulIdentifier", 261 | "src": "425:5:1" 262 | }, 263 | "variableNames": [ 264 | { 265 | "name": "cleaned", 266 | "nodeType": "YulIdentifier", 267 | "src": "414:7:1" 268 | } 269 | ] 270 | } 271 | ] 272 | }, 273 | "name": "cleanup_t_uint256", 274 | "nodeType": "YulFunctionDefinition", 275 | "parameters": [ 276 | { 277 | "name": "value", 278 | "nodeType": "YulTypedName", 279 | "src": "386:5:1", 280 | "type": "" 281 | } 282 | ], 283 | "returnVariables": [ 284 | { 285 | "name": "cleaned", 286 | "nodeType": "YulTypedName", 287 | "src": "396:7:1", 288 | "type": "" 289 | } 290 | ], 291 | "src": "359:77:1" 292 | } 293 | ] 294 | }, 295 | "contents": "{\n\n function abi_encode_t_uint256_to_t_uint256_fromStack(value, pos) {\n mstore(pos, cleanup_t_uint256(value))\n }\n\n function abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed(headStart , value0) -> tail {\n tail := add(headStart, 32)\n\n abi_encode_t_uint256_to_t_uint256_fromStack(value0, add(headStart, 0))\n\n }\n\n function cleanup_t_uint256(value) -> cleaned {\n cleaned := value\n }\n\n}\n", 296 | "id": 1, 297 | "language": "Yul", 298 | "name": "#utility.yul" 299 | } 300 | ], 301 | "immutableReferences": {}, 302 | "linkReferences": {}, 303 | "object": "60806040526004361060295760003560e01c80633ccfd60b146034578063b69ef8a814604857602f565b36602f57005b600080fd5b348015603f57600080fd5b506046606e565b005b348015605357600080fd5b50605a60b6565b6040516065919060cb565b60405180910390f35b3373ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f1935050505015801560b3573d6000803e3d6000fd5b50565b600047905090565b60c58160e4565b82525050565b600060208201905060de600083018460be565b92915050565b600081905091905056fea26469706673582212208652f39739eaffe4b2f0692790a28c82cf7a6eba249c9e10ea60c6a145e9711f64736f6c63430008040033", 304 | "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x29 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x3CCFD60B EQ PUSH1 0x34 JUMPI DUP1 PUSH4 0xB69EF8A8 EQ PUSH1 0x48 JUMPI PUSH1 0x2F JUMP JUMPDEST CALLDATASIZE PUSH1 0x2F JUMPI STOP JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x3F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x46 PUSH1 0x6E JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x53 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x5A PUSH1 0xB6 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x65 SWAP2 SWAP1 PUSH1 0xCB JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLER PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x8FC SELFBALANCE SWAP1 DUP2 ISZERO MUL SWAP1 PUSH1 0x40 MLOAD PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 DUP9 DUP9 CALL SWAP4 POP POP POP POP ISZERO DUP1 ISZERO PUSH1 0xB3 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP JUMP JUMPDEST PUSH1 0x0 SELFBALANCE SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0xC5 DUP2 PUSH1 0xE4 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 ADD SWAP1 POP PUSH1 0xDE PUSH1 0x0 DUP4 ADD DUP5 PUSH1 0xBE JUMP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DUP7 MSTORE RETURN SWAP8 CODECOPY 0xEA SELFDESTRUCT 0xE4 0xB2 CREATE PUSH10 0x2790A28C82CF7A6EBA24 SWAP13 SWAP15 LT 0xEA PUSH1 0xC6 LOG1 GASLIMIT 0xE9 PUSH18 0x1F64736F6C63430008040033000000000000 ", 305 | "sourceMap": "62:506:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;286:97;;;;;;;;;;;;;:::i;:::-;;474:92;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;286:97;333:10;325:28;;:51;354:21;325:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;286:97::o;474:92::-;515:4;538:21;531:28;;474:92;:::o;7:118:1:-;94:24;112:5;94:24;:::i;:::-;89:3;82:37;72:53;;:::o;131:222::-;224:4;262:2;251:9;247:18;239:26;;275:71;343:1;332:9;328:17;319:6;275:71;:::i;:::-;229:124;;;;:::o;359:77::-;396:7;425:5;414:16;;404:32;;;:::o" 306 | }, 307 | "methodIdentifiers": { 308 | "balance()": "b69ef8a8", 309 | "withdraw()": "3ccfd60b" 310 | } 311 | }, 312 | "metadata": "{\"compiler\":{\"version\":\"0.8.4+commit.c7e474f2\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"balance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/BrokenContract.sol\":\"BrokenContract\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/BrokenContract.sol\":{\"keccak256\":\"0x29174ecb7d975018570beb5f9bd0fedf4aafb46ea593787f46e175bcf9a8f7a2\",\"license\":\"Unlicense\",\"urls\":[\"bzz-raw://0ad3b533d65226f288c48cf62dee650339cdf01ec3f6fe8cc54ebcd7712fd943\",\"dweb:/ipfs/QmaoA5XBUFavX6FBxDhx9ayHMuqeoE86NCnEAD4B7FVCT1\"]}},\"version\":1}" 313 | } 314 | } 315 | }, 316 | "sources": { 317 | "contracts/BrokenContract.sol": { 318 | "ast": { 319 | "absolutePath": "contracts/BrokenContract.sol", 320 | "exportedSymbols": { 321 | "BrokenContract": [ 322 | 35 323 | ] 324 | }, 325 | "id": 36, 326 | "license": "Unlicense", 327 | "nodeType": "SourceUnit", 328 | "nodes": [ 329 | { 330 | "id": 1, 331 | "literals": [ 332 | "solidity", 333 | "^", 334 | "0.8", 335 | ".0" 336 | ], 337 | "nodeType": "PragmaDirective", 338 | "src": "37:23:0" 339 | }, 340 | { 341 | "abstract": false, 342 | "baseContracts": [], 343 | "contractDependencies": [], 344 | "contractKind": "contract", 345 | "fullyImplemented": true, 346 | "id": 35, 347 | "linearizedBaseContracts": [ 348 | 35 349 | ], 350 | "name": "BrokenContract", 351 | "nameLocation": "71:14:0", 352 | "nodeType": "ContractDefinition", 353 | "nodes": [ 354 | { 355 | "body": { 356 | "id": 4, 357 | "nodeType": "Block", 358 | "src": "163:2:0", 359 | "statements": [] 360 | }, 361 | "id": 5, 362 | "implemented": true, 363 | "kind": "receive", 364 | "modifiers": [], 365 | "name": "", 366 | "nameLocation": "-1:-1:-1", 367 | "nodeType": "FunctionDefinition", 368 | "parameters": { 369 | "id": 2, 370 | "nodeType": "ParameterList", 371 | "parameters": [], 372 | "src": "143:2:0" 373 | }, 374 | "returnParameters": { 375 | "id": 3, 376 | "nodeType": "ParameterList", 377 | "parameters": [], 378 | "src": "163:0:0" 379 | }, 380 | "scope": 35, 381 | "src": "136:29:0", 382 | "stateMutability": "payable", 383 | "virtual": false, 384 | "visibility": "external" 385 | }, 386 | { 387 | "body": { 388 | "id": 21, 389 | "nodeType": "Block", 390 | "src": "315:68:0", 391 | "statements": [ 392 | { 393 | "expression": { 394 | "arguments": [ 395 | { 396 | "expression": { 397 | "arguments": [ 398 | { 399 | "id": 16, 400 | "name": "this", 401 | "nodeType": "Identifier", 402 | "overloadedDeclarations": [], 403 | "referencedDeclaration": -28, 404 | "src": "362:4:0", 405 | "typeDescriptions": { 406 | "typeIdentifier": "t_contract$_BrokenContract_$35", 407 | "typeString": "contract BrokenContract" 408 | } 409 | } 410 | ], 411 | "expression": { 412 | "argumentTypes": [ 413 | { 414 | "typeIdentifier": "t_contract$_BrokenContract_$35", 415 | "typeString": "contract BrokenContract" 416 | } 417 | ], 418 | "id": 15, 419 | "isConstant": false, 420 | "isLValue": false, 421 | "isPure": true, 422 | "lValueRequested": false, 423 | "nodeType": "ElementaryTypeNameExpression", 424 | "src": "354:7:0", 425 | "typeDescriptions": { 426 | "typeIdentifier": "t_type$_t_address_$", 427 | "typeString": "type(address)" 428 | }, 429 | "typeName": { 430 | "id": 14, 431 | "name": "address", 432 | "nodeType": "ElementaryTypeName", 433 | "src": "354:7:0", 434 | "typeDescriptions": {} 435 | } 436 | }, 437 | "id": 17, 438 | "isConstant": false, 439 | "isLValue": false, 440 | "isPure": false, 441 | "kind": "typeConversion", 442 | "lValueRequested": false, 443 | "names": [], 444 | "nodeType": "FunctionCall", 445 | "src": "354:13:0", 446 | "tryCall": false, 447 | "typeDescriptions": { 448 | "typeIdentifier": "t_address", 449 | "typeString": "address" 450 | } 451 | }, 452 | "id": 18, 453 | "isConstant": false, 454 | "isLValue": false, 455 | "isPure": false, 456 | "lValueRequested": false, 457 | "memberName": "balance", 458 | "nodeType": "MemberAccess", 459 | "src": "354:21:0", 460 | "typeDescriptions": { 461 | "typeIdentifier": "t_uint256", 462 | "typeString": "uint256" 463 | } 464 | } 465 | ], 466 | "expression": { 467 | "argumentTypes": [ 468 | { 469 | "typeIdentifier": "t_uint256", 470 | "typeString": "uint256" 471 | } 472 | ], 473 | "expression": { 474 | "arguments": [ 475 | { 476 | "expression": { 477 | "id": 10, 478 | "name": "msg", 479 | "nodeType": "Identifier", 480 | "overloadedDeclarations": [], 481 | "referencedDeclaration": -15, 482 | "src": "333:3:0", 483 | "typeDescriptions": { 484 | "typeIdentifier": "t_magic_message", 485 | "typeString": "msg" 486 | } 487 | }, 488 | "id": 11, 489 | "isConstant": false, 490 | "isLValue": false, 491 | "isPure": false, 492 | "lValueRequested": false, 493 | "memberName": "sender", 494 | "nodeType": "MemberAccess", 495 | "src": "333:10:0", 496 | "typeDescriptions": { 497 | "typeIdentifier": "t_address", 498 | "typeString": "address" 499 | } 500 | } 501 | ], 502 | "expression": { 503 | "argumentTypes": [ 504 | { 505 | "typeIdentifier": "t_address", 506 | "typeString": "address" 507 | } 508 | ], 509 | "id": 9, 510 | "isConstant": false, 511 | "isLValue": false, 512 | "isPure": true, 513 | "lValueRequested": false, 514 | "nodeType": "ElementaryTypeNameExpression", 515 | "src": "325:8:0", 516 | "typeDescriptions": { 517 | "typeIdentifier": "t_type$_t_address_payable_$", 518 | "typeString": "type(address payable)" 519 | }, 520 | "typeName": { 521 | "id": 8, 522 | "name": "address", 523 | "nodeType": "ElementaryTypeName", 524 | "src": "325:8:0", 525 | "stateMutability": "payable", 526 | "typeDescriptions": {} 527 | } 528 | }, 529 | "id": 12, 530 | "isConstant": false, 531 | "isLValue": false, 532 | "isPure": false, 533 | "kind": "typeConversion", 534 | "lValueRequested": false, 535 | "names": [], 536 | "nodeType": "FunctionCall", 537 | "src": "325:19:0", 538 | "tryCall": false, 539 | "typeDescriptions": { 540 | "typeIdentifier": "t_address_payable", 541 | "typeString": "address payable" 542 | } 543 | }, 544 | "id": 13, 545 | "isConstant": false, 546 | "isLValue": false, 547 | "isPure": false, 548 | "lValueRequested": false, 549 | "memberName": "transfer", 550 | "nodeType": "MemberAccess", 551 | "src": "325:28:0", 552 | "typeDescriptions": { 553 | "typeIdentifier": "t_function_transfer_nonpayable$_t_uint256_$returns$__$", 554 | "typeString": "function (uint256)" 555 | } 556 | }, 557 | "id": 19, 558 | "isConstant": false, 559 | "isLValue": false, 560 | "isPure": false, 561 | "kind": "functionCall", 562 | "lValueRequested": false, 563 | "names": [], 564 | "nodeType": "FunctionCall", 565 | "src": "325:51:0", 566 | "tryCall": false, 567 | "typeDescriptions": { 568 | "typeIdentifier": "t_tuple$__$", 569 | "typeString": "tuple()" 570 | } 571 | }, 572 | "id": 20, 573 | "nodeType": "ExpressionStatement", 574 | "src": "325:51:0" 575 | } 576 | ] 577 | }, 578 | "functionSelector": "3ccfd60b", 579 | "id": 22, 580 | "implemented": true, 581 | "kind": "function", 582 | "modifiers": [], 583 | "name": "withdraw", 584 | "nameLocation": "295:8:0", 585 | "nodeType": "FunctionDefinition", 586 | "parameters": { 587 | "id": 6, 588 | "nodeType": "ParameterList", 589 | "parameters": [], 590 | "src": "303:2:0" 591 | }, 592 | "returnParameters": { 593 | "id": 7, 594 | "nodeType": "ParameterList", 595 | "parameters": [], 596 | "src": "315:0:0" 597 | }, 598 | "scope": 35, 599 | "src": "286:97:0", 600 | "stateMutability": "nonpayable", 601 | "virtual": false, 602 | "visibility": "external" 603 | }, 604 | { 605 | "body": { 606 | "id": 33, 607 | "nodeType": "Block", 608 | "src": "521:45:0", 609 | "statements": [ 610 | { 611 | "expression": { 612 | "expression": { 613 | "arguments": [ 614 | { 615 | "id": 29, 616 | "name": "this", 617 | "nodeType": "Identifier", 618 | "overloadedDeclarations": [], 619 | "referencedDeclaration": -28, 620 | "src": "546:4:0", 621 | "typeDescriptions": { 622 | "typeIdentifier": "t_contract$_BrokenContract_$35", 623 | "typeString": "contract BrokenContract" 624 | } 625 | } 626 | ], 627 | "expression": { 628 | "argumentTypes": [ 629 | { 630 | "typeIdentifier": "t_contract$_BrokenContract_$35", 631 | "typeString": "contract BrokenContract" 632 | } 633 | ], 634 | "id": 28, 635 | "isConstant": false, 636 | "isLValue": false, 637 | "isPure": true, 638 | "lValueRequested": false, 639 | "nodeType": "ElementaryTypeNameExpression", 640 | "src": "538:7:0", 641 | "typeDescriptions": { 642 | "typeIdentifier": "t_type$_t_address_$", 643 | "typeString": "type(address)" 644 | }, 645 | "typeName": { 646 | "id": 27, 647 | "name": "address", 648 | "nodeType": "ElementaryTypeName", 649 | "src": "538:7:0", 650 | "typeDescriptions": {} 651 | } 652 | }, 653 | "id": 30, 654 | "isConstant": false, 655 | "isLValue": false, 656 | "isPure": false, 657 | "kind": "typeConversion", 658 | "lValueRequested": false, 659 | "names": [], 660 | "nodeType": "FunctionCall", 661 | "src": "538:13:0", 662 | "tryCall": false, 663 | "typeDescriptions": { 664 | "typeIdentifier": "t_address", 665 | "typeString": "address" 666 | } 667 | }, 668 | "id": 31, 669 | "isConstant": false, 670 | "isLValue": false, 671 | "isPure": false, 672 | "lValueRequested": false, 673 | "memberName": "balance", 674 | "nodeType": "MemberAccess", 675 | "src": "538:21:0", 676 | "typeDescriptions": { 677 | "typeIdentifier": "t_uint256", 678 | "typeString": "uint256" 679 | } 680 | }, 681 | "functionReturnParameters": 26, 682 | "id": 32, 683 | "nodeType": "Return", 684 | "src": "531:28:0" 685 | } 686 | ] 687 | }, 688 | "functionSelector": "b69ef8a8", 689 | "id": 34, 690 | "implemented": true, 691 | "kind": "function", 692 | "modifiers": [], 693 | "name": "balance", 694 | "nameLocation": "483:7:0", 695 | "nodeType": "FunctionDefinition", 696 | "parameters": { 697 | "id": 23, 698 | "nodeType": "ParameterList", 699 | "parameters": [], 700 | "src": "490:2:0" 701 | }, 702 | "returnParameters": { 703 | "id": 26, 704 | "nodeType": "ParameterList", 705 | "parameters": [ 706 | { 707 | "constant": false, 708 | "id": 25, 709 | "mutability": "mutable", 710 | "name": "", 711 | "nameLocation": "-1:-1:-1", 712 | "nodeType": "VariableDeclaration", 713 | "scope": 34, 714 | "src": "515:4:0", 715 | "stateVariable": false, 716 | "storageLocation": "default", 717 | "typeDescriptions": { 718 | "typeIdentifier": "t_uint256", 719 | "typeString": "uint256" 720 | }, 721 | "typeName": { 722 | "id": 24, 723 | "name": "uint", 724 | "nodeType": "ElementaryTypeName", 725 | "src": "515:4:0", 726 | "typeDescriptions": { 727 | "typeIdentifier": "t_uint256", 728 | "typeString": "uint256" 729 | } 730 | }, 731 | "visibility": "internal" 732 | } 733 | ], 734 | "src": "514:6:0" 735 | }, 736 | "scope": 35, 737 | "src": "474:92:0", 738 | "stateMutability": "view", 739 | "virtual": false, 740 | "visibility": "external" 741 | } 742 | ], 743 | "scope": 36, 744 | "src": "62:506:0", 745 | "usedErrors": [] 746 | } 747 | ], 748 | "src": "37:532:0" 749 | }, 750 | "id": 0 751 | } 752 | } 753 | } 754 | } 755 | -------------------------------------------------------------------------------- /exploit-contract/artifacts/contracts/BrokenContract.sol/BrokenContract.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/27ffb7e58541d8fe5dbdf53045adde53.json" 4 | } 5 | -------------------------------------------------------------------------------- /exploit-contract/artifacts/contracts/BrokenContract.sol/brokenContractABI.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "balance", 5 | "outputs": [ 6 | { 7 | "internalType": "uint256", 8 | "name": "", 9 | "type": "uint256" 10 | } 11 | ], 12 | "stateMutability": "view", 13 | "type": "function" 14 | }, 15 | { 16 | "inputs": [], 17 | "name": "withdraw", 18 | "outputs": [], 19 | "stateMutability": "nonpayable", 20 | "type": "function" 21 | }, 22 | { 23 | "stateMutability": "payable", 24 | "type": "receive" 25 | } 26 | ] -------------------------------------------------------------------------------- /exploit-contract/artifacts/contracts/BrokenContract.sol/contract_abi.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"name":"balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] -------------------------------------------------------------------------------- /exploit-contract/contracts/BrokenContract.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | contract BrokenContract { 5 | // allow this contract to receive ether 6 | receive() external payable {} 7 | // this function will withdraw the entire ether balance in this smart-contract to the first person who calls it 8 | function withdraw() external { 9 | payable(msg.sender).transfer(address(this).balance); 10 | } 11 | // this function can be used to keep track of the contract's ethereum balance 12 | function balance() external view returns(uint) { 13 | return address(this).balance; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /exploit-contract/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | require("@nomiclabs/hardhat-etherscan"); 3 | require("dotenv").config(); 4 | 5 | // This is a sample Hardhat task. To learn how to create your own go to 6 | // https://hardhat.org/guides/create-task.html 7 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 8 | const accounts = await hre.ethers.getSigners(); 9 | 10 | for (const account of accounts) { 11 | console.log(account.address); 12 | } 13 | }); 14 | 15 | // You need to export an object to set up your config 16 | // Go to https://hardhat.org/config/ to learn more 17 | 18 | /** 19 | * @type import('hardhat/config').HardhatUserConfig 20 | */ 21 | module.exports = { 22 | solidity: "0.8.4", 23 | networks: { 24 | goerli: { 25 | url: `https://eth-goerli.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`, 26 | accounts: [process.env.PK] 27 | } 28 | }, 29 | etherscan: { 30 | apiKey: process.env.ETHERSCAN_API 31 | } 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /exploit-contract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hardhat-project", 3 | "devDependencies": { 4 | "@nomiclabs/hardhat-ethers": "^2.0.3", 5 | "@nomiclabs/hardhat-waffle": "^2.0.1", 6 | "chai": "^4.3.4", 7 | "ethereum-waffle": "^3.4.0", 8 | "ethers": "^5.5.2", 9 | "hardhat": "^2.7.0" 10 | }, 11 | "dependencies": { 12 | "@flashbots/ethers-provider-bundle": "^0.4.2", 13 | "@nomiclabs/hardhat-etherscan": "^2.1.8", 14 | "dotenv": "^10.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /exploit-contract/pics/exploit-flashbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/exploit-contract/pics/exploit-flashbot.png -------------------------------------------------------------------------------- /exploit-contract/pics/hex_data.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/exploit-contract/pics/hex_data.gif -------------------------------------------------------------------------------- /exploit-contract/pics/tmp.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /exploit-contract/scripts/deploy.js: -------------------------------------------------------------------------------- 1 | const hre = require("hardhat"); 2 | require("@nomiclabs/hardhat-etherscan"); 3 | 4 | async function main() { 5 | const BrokenContract = await hre.ethers.getContractFactory("BrokenContract"); 6 | const broken = await BrokenContract.deploy(); 7 | await broken.deployed(); 8 | 9 | console.log("BrokenContract deployed to:", broken.address); 10 | } 11 | 12 | main() 13 | .then(() => process.exit(0)) 14 | .catch((error) => { 15 | console.error(error); 16 | process.exit(1); 17 | }); -------------------------------------------------------------------------------- /exploit-contract/scripts/flashbotWithdraw.js: -------------------------------------------------------------------------------- 1 | const ethers = require("ethers"); 2 | const flashbot = require("@flashbots/ethers-provider-bundle"); 3 | // load deployed contract's ABI: https://api-goerli.etherscan.io/api?module=contract&action=getabi&address=0xE417A0C129765A049FD360eDCD540629265e1Ae3 4 | const brokenContractABI = require("../artifacts/contracts/BrokenContract.sol/contract_abi.json"); 5 | // address of the deployed contract 6 | const brokenContractAddress = "0xE417A0C129765A049FD360eDCD540629265e1Ae3"; 7 | require('dotenv').config(); 8 | 9 | async function main() { 10 | // goerli network 11 | const CHAIN_ID = 5; 12 | // initialize provider 13 | const provider = new ethers.providers.AlchemyProvider(CHAIN_ID); 14 | // initialize wallet 15 | const wallet = new ethers.Wallet(process.env.FRESH_PK, provider); 16 | // initialize flashbots provider 17 | const flashbotProvider = await flashbot.FlashbotsBundleProvider.create( 18 | provider, 19 | wallet, 20 | "https://relay-goerli.flashbots.net", 21 | "goerli" 22 | ); 23 | // define the Broken Contract as an ethers object 24 | const BrokenContract = new ethers.Contract(brokenContractAddress, brokenContractABI, wallet); 25 | // call the `withdraw` function on BrokenContract 26 | const withdrawContractTx = await BrokenContract.populateTransaction.withdraw(); 27 | // populate the transaction details 28 | const tx = [ 29 | { 30 | transaction: { 31 | // goerli testnet 32 | chainId: CHAIN_ID, 33 | // EIP-1559 style transaction 34 | type: 2, 35 | // no ether sent for this transaction 36 | value: ethers.utils.parseUnits("0", "ether"), 37 | // the necessary transaction calldata required to withdraw the funds from the smart contract 38 | data: withdrawContractTx.data, 39 | // the max fee per unit of gas (arbitrarily set) 40 | maxFeePerGas: ethers.utils.parseUnits("3", "gwei"), 41 | // the max miner tip to pay (arbitrarily set) 42 | maxPriorityFeePerGas: ethers.utils.parseUnits("3", "gwei"), 43 | // the transaction must interact directly with the deployed address on goerli network 44 | to: brokenContractAddress 45 | }, 46 | // the defined wallet above will be used to interact with the smart-contract 47 | signer: wallet 48 | } 49 | ] 50 | // for each block we need to continously re-submit the flashbots bundle transaction until it is selected by a miner 51 | provider.on("block", async(blockNumber) => { 52 | console.log("Current Block: ", blockNumber); 53 | // send the bundle to the flashbots relayer for the closest next future block (ie: t + 1) 54 | const bundleSubmit = await flashbotProvider.sendBundle(tx, blockNumber+1) 55 | // wait until we receive a response and exit only once the transaction has been mined in the blockchain 56 | const waitResponse = await bundleSubmit.wait(); 57 | if(waitResponse == 0) { 58 | console.log("Successfully exploited the smart-contract using flashbots."); 59 | process.exit(); 60 | } 61 | }); 62 | } 63 | 64 | main(); -------------------------------------------------------------------------------- /nft-sponosored-tx/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .DS_Store 3 | node_modules/* -------------------------------------------------------------------------------- /nft-sponosored-tx/README.md: -------------------------------------------------------------------------------- 1 | ## Rescuing An NFT Using Flashbots 2 | 3 | ### Problem: 4 | 5 | Imagine a developer accidently uploads his Ethereum private key to a public Github repo. It's only a matter of minutes before a bot will sweep the entire balance of this developer's ETH. Suppose the developer also had an NFT associated with this exposed wallet. In this case, it's highly unlikely the bot will detect NFT so this gives the developer some time to think about how to rescue his NFT. 6 | 7 | Here's the key problem - in order to transfer the NFT to a new safe address there needs to be ETH in the exploited address to pay for the gas fees of the NFT transfer. However, the moment any any ETH is sent to this exploited address, bots will detect the incomng funds and immediately withdraw funds. This could leave the NFT stuck in the exploited address as the developer is not able to fund this address with enough ETH to pay for the gas transaction costs. Even if he was able to succesfully fund the exploited address, when the developer tries to transfer the NFT to a new address, there's a risk this activity may alert bots and cause them to front-run the transaction leading them to steal the NFT. 8 | 9 | ### Solution: 10 | 11 | Rather than sending the transaction through the public mempool, a user could use Flashbots and group multiple transactions into the same bundle provided they are atomic. In this case, the developer could create a sponsor transaction where he can make a transaction from account X but pay for this transaction's gas fees from account Y. Below is a diagram showcasing the steps associated with this process: 12 | 13 | - Transaction 1: The sponsor address will transfer a small portion of ETH to the exploited address to pay for the gas fees to transfer the NFT to a safe address 14 | - Transaction 2: Transfer the NFT from the exploited address to the safe address 15 | 16 | ![](pics/nft-transfer-example.png) 17 | 18 | ## Walkthrough: 19 | 20 | 1. The NFT in this example is trapped in the exploited address. Currently there’s no ETH in the exploited address and if anyone were to send ETH to this address it would immediately be swept up by sniper bots. 21 | 22 | ![](pics/stuck_NFT.png) 23 | 24 | 2. At the same time the user has a sponsor address which will be used to pay for the transaction gas fees to rescue the NFT and move it to the sponsor’s address. 25 | 26 | ![](pics/sponsor_address.png) 27 | 28 | 3. The user will have to compile these two transactions into a calling the `signedTxBundle` RPC endpoint will be sent to the Flashbots relayer. Note: given all of this is done at once (ie: atomically) there’s no feasible way for a sniper to front-run and beat us to this transaction. From here the Flashbots relayer will directly connect the user’s transaction to miners. If miners choose to include this bundle into the block then the developer has successfully rescued his NFT. In the case the miner doesn’t include this transaction in the current block, nobody will know about this transaction given it did not touch the public mempool. When this happens the developer would have to re-submit their transaction until the miner finally includes it in the block (ie: the developer could increase the gas fee to incentivize the miner to include it in the block faster). 29 | 30 | ```javascript 31 | // sign the transactions into a single flashbots bundle 32 | const signedTxBundle = await flashbotProvider.signBundle([ 33 | { 34 | // the sponsor will be sending ETH to the exploited address in tx1 35 | signer: sponsor, 36 | transaction: tx1 37 | }, 38 | { 39 | // the exploited address will send the NFT to the sponsor address in tx2 40 | signer: exploited, 41 | transaction: tx2 42 | } 43 | ]); 44 | ``` 45 | 46 | 4. Using the [`sponsorTx.js`](https://github.com/schepal/flashbots_playground/blob/main/nft-sponosored-tx/src/sponsorTx.js) script we were able to successfully transfer the NFT from the exploited address. Notice how the sponsor address now has the NFT which is previously stuck in the exploited address. At the same time notice how the exploited address no longer has the NFT in its balance. There is some ETH remaining from the sponsor paying it (note: this can be improved by more accurately estimating gas - in this case it was just as an example). This remaining ETH would be sniped away immediately once it hits the address. 47 | 48 | ![](pics/exploit-withdraw.png) 49 | 50 |
51 | 52 | ![](pics/sponsor_rescued.png) 53 | 54 | 55 | ### Goerli Addresses Used In This Analysis: 56 | - Exploited Address: [`0x39405b216a9a4a1a1e3439cb311543b09c7f3e50`](https://goerli.etherscan.io/address/0x39405b216a9a4a1a1e3439cb311543b09c7f3e50) 57 | - Sponsor Address: [`0xf82bB95942194cB114340C4605B5B0Be56DA6De7`](https://goerli.etherscan.io/address/0xf82bB95942194cB114340C4605B5B0Be56DA6De7) 58 | - Deployed NFT Address (thanks to Paradigm's awesome [faucet](https://faucet.paradigm.xyz/)): [`0xf5de760f2e916647fd766b4ad9e85ff943ce3a2b`](https://goerli.etherscan.io/address/0xf5de760f2e916647fd766b4ad9e85ff943ce3a2b) 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /nft-sponosored-tx/abi/nft.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[{"internalType":"address","name":"_DAI","type":"address"},{"internalType":"address","name":"_WETH","type":"address"},{"internalType":"string","name":"_URI","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"FaucetDrained","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"FaucetDripped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"OperatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"SuperOperatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DAI","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DAI_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ETH_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NFT_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"URI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedOperators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableDrips","outputs":[{"internalType":"uint256","name":"ethDrips","type":"uint256"},{"internalType":"uint256","name":"daiDrips","type":"uint256"},{"internalType":"uint256","name":"wethDrips","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"drain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"drip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftsMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"superOperators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"updateApprovedOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_nftCount","type":"uint256"},{"internalType":"uint256","name":"_ethAmount","type":"uint256"},{"internalType":"uint256","name":"_daiAmount","type":"uint256"},{"internalType":"uint256","name":"_wethAmount","type":"uint256"}],"name":"updateDripAmounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"updateSuperOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_URI","type":"string"}],"name":"updateTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] -------------------------------------------------------------------------------- /nft-sponosored-tx/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nft-sponosored-tx", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@flashbots/ethers-provider-bundle": "^0.4.2", 9 | "dotenv": "^10.0.0", 10 | "ethers": "^5.5.2" 11 | } 12 | }, 13 | "node_modules/@ethersproject/abi": { 14 | "version": "5.5.0", 15 | "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", 16 | "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", 17 | "funding": [ 18 | { 19 | "type": "individual", 20 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 21 | }, 22 | { 23 | "type": "individual", 24 | "url": "https://www.buymeacoffee.com/ricmoo" 25 | } 26 | ], 27 | "dependencies": { 28 | "@ethersproject/address": "^5.5.0", 29 | "@ethersproject/bignumber": "^5.5.0", 30 | "@ethersproject/bytes": "^5.5.0", 31 | "@ethersproject/constants": "^5.5.0", 32 | "@ethersproject/hash": "^5.5.0", 33 | "@ethersproject/keccak256": "^5.5.0", 34 | "@ethersproject/logger": "^5.5.0", 35 | "@ethersproject/properties": "^5.5.0", 36 | "@ethersproject/strings": "^5.5.0" 37 | } 38 | }, 39 | "node_modules/@ethersproject/abstract-provider": { 40 | "version": "5.5.1", 41 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", 42 | "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", 43 | "funding": [ 44 | { 45 | "type": "individual", 46 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 47 | }, 48 | { 49 | "type": "individual", 50 | "url": "https://www.buymeacoffee.com/ricmoo" 51 | } 52 | ], 53 | "dependencies": { 54 | "@ethersproject/bignumber": "^5.5.0", 55 | "@ethersproject/bytes": "^5.5.0", 56 | "@ethersproject/logger": "^5.5.0", 57 | "@ethersproject/networks": "^5.5.0", 58 | "@ethersproject/properties": "^5.5.0", 59 | "@ethersproject/transactions": "^5.5.0", 60 | "@ethersproject/web": "^5.5.0" 61 | } 62 | }, 63 | "node_modules/@ethersproject/abstract-signer": { 64 | "version": "5.5.0", 65 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", 66 | "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", 67 | "funding": [ 68 | { 69 | "type": "individual", 70 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 71 | }, 72 | { 73 | "type": "individual", 74 | "url": "https://www.buymeacoffee.com/ricmoo" 75 | } 76 | ], 77 | "dependencies": { 78 | "@ethersproject/abstract-provider": "^5.5.0", 79 | "@ethersproject/bignumber": "^5.5.0", 80 | "@ethersproject/bytes": "^5.5.0", 81 | "@ethersproject/logger": "^5.5.0", 82 | "@ethersproject/properties": "^5.5.0" 83 | } 84 | }, 85 | "node_modules/@ethersproject/address": { 86 | "version": "5.5.0", 87 | "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", 88 | "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", 89 | "funding": [ 90 | { 91 | "type": "individual", 92 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 93 | }, 94 | { 95 | "type": "individual", 96 | "url": "https://www.buymeacoffee.com/ricmoo" 97 | } 98 | ], 99 | "dependencies": { 100 | "@ethersproject/bignumber": "^5.5.0", 101 | "@ethersproject/bytes": "^5.5.0", 102 | "@ethersproject/keccak256": "^5.5.0", 103 | "@ethersproject/logger": "^5.5.0", 104 | "@ethersproject/rlp": "^5.5.0" 105 | } 106 | }, 107 | "node_modules/@ethersproject/base64": { 108 | "version": "5.5.0", 109 | "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", 110 | "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", 111 | "funding": [ 112 | { 113 | "type": "individual", 114 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 115 | }, 116 | { 117 | "type": "individual", 118 | "url": "https://www.buymeacoffee.com/ricmoo" 119 | } 120 | ], 121 | "dependencies": { 122 | "@ethersproject/bytes": "^5.5.0" 123 | } 124 | }, 125 | "node_modules/@ethersproject/basex": { 126 | "version": "5.5.0", 127 | "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz", 128 | "integrity": "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==", 129 | "funding": [ 130 | { 131 | "type": "individual", 132 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 133 | }, 134 | { 135 | "type": "individual", 136 | "url": "https://www.buymeacoffee.com/ricmoo" 137 | } 138 | ], 139 | "dependencies": { 140 | "@ethersproject/bytes": "^5.5.0", 141 | "@ethersproject/properties": "^5.5.0" 142 | } 143 | }, 144 | "node_modules/@ethersproject/bignumber": { 145 | "version": "5.5.0", 146 | "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", 147 | "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", 148 | "funding": [ 149 | { 150 | "type": "individual", 151 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 152 | }, 153 | { 154 | "type": "individual", 155 | "url": "https://www.buymeacoffee.com/ricmoo" 156 | } 157 | ], 158 | "dependencies": { 159 | "@ethersproject/bytes": "^5.5.0", 160 | "@ethersproject/logger": "^5.5.0", 161 | "bn.js": "^4.11.9" 162 | } 163 | }, 164 | "node_modules/@ethersproject/bytes": { 165 | "version": "5.5.0", 166 | "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", 167 | "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", 168 | "funding": [ 169 | { 170 | "type": "individual", 171 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 172 | }, 173 | { 174 | "type": "individual", 175 | "url": "https://www.buymeacoffee.com/ricmoo" 176 | } 177 | ], 178 | "dependencies": { 179 | "@ethersproject/logger": "^5.5.0" 180 | } 181 | }, 182 | "node_modules/@ethersproject/constants": { 183 | "version": "5.5.0", 184 | "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", 185 | "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", 186 | "funding": [ 187 | { 188 | "type": "individual", 189 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 190 | }, 191 | { 192 | "type": "individual", 193 | "url": "https://www.buymeacoffee.com/ricmoo" 194 | } 195 | ], 196 | "dependencies": { 197 | "@ethersproject/bignumber": "^5.5.0" 198 | } 199 | }, 200 | "node_modules/@ethersproject/contracts": { 201 | "version": "5.5.0", 202 | "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz", 203 | "integrity": "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==", 204 | "funding": [ 205 | { 206 | "type": "individual", 207 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 208 | }, 209 | { 210 | "type": "individual", 211 | "url": "https://www.buymeacoffee.com/ricmoo" 212 | } 213 | ], 214 | "dependencies": { 215 | "@ethersproject/abi": "^5.5.0", 216 | "@ethersproject/abstract-provider": "^5.5.0", 217 | "@ethersproject/abstract-signer": "^5.5.0", 218 | "@ethersproject/address": "^5.5.0", 219 | "@ethersproject/bignumber": "^5.5.0", 220 | "@ethersproject/bytes": "^5.5.0", 221 | "@ethersproject/constants": "^5.5.0", 222 | "@ethersproject/logger": "^5.5.0", 223 | "@ethersproject/properties": "^5.5.0", 224 | "@ethersproject/transactions": "^5.5.0" 225 | } 226 | }, 227 | "node_modules/@ethersproject/hash": { 228 | "version": "5.5.0", 229 | "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", 230 | "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", 231 | "funding": [ 232 | { 233 | "type": "individual", 234 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 235 | }, 236 | { 237 | "type": "individual", 238 | "url": "https://www.buymeacoffee.com/ricmoo" 239 | } 240 | ], 241 | "dependencies": { 242 | "@ethersproject/abstract-signer": "^5.5.0", 243 | "@ethersproject/address": "^5.5.0", 244 | "@ethersproject/bignumber": "^5.5.0", 245 | "@ethersproject/bytes": "^5.5.0", 246 | "@ethersproject/keccak256": "^5.5.0", 247 | "@ethersproject/logger": "^5.5.0", 248 | "@ethersproject/properties": "^5.5.0", 249 | "@ethersproject/strings": "^5.5.0" 250 | } 251 | }, 252 | "node_modules/@ethersproject/hdnode": { 253 | "version": "5.5.0", 254 | "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz", 255 | "integrity": "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==", 256 | "funding": [ 257 | { 258 | "type": "individual", 259 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 260 | }, 261 | { 262 | "type": "individual", 263 | "url": "https://www.buymeacoffee.com/ricmoo" 264 | } 265 | ], 266 | "dependencies": { 267 | "@ethersproject/abstract-signer": "^5.5.0", 268 | "@ethersproject/basex": "^5.5.0", 269 | "@ethersproject/bignumber": "^5.5.0", 270 | "@ethersproject/bytes": "^5.5.0", 271 | "@ethersproject/logger": "^5.5.0", 272 | "@ethersproject/pbkdf2": "^5.5.0", 273 | "@ethersproject/properties": "^5.5.0", 274 | "@ethersproject/sha2": "^5.5.0", 275 | "@ethersproject/signing-key": "^5.5.0", 276 | "@ethersproject/strings": "^5.5.0", 277 | "@ethersproject/transactions": "^5.5.0", 278 | "@ethersproject/wordlists": "^5.5.0" 279 | } 280 | }, 281 | "node_modules/@ethersproject/json-wallets": { 282 | "version": "5.5.0", 283 | "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz", 284 | "integrity": "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==", 285 | "funding": [ 286 | { 287 | "type": "individual", 288 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 289 | }, 290 | { 291 | "type": "individual", 292 | "url": "https://www.buymeacoffee.com/ricmoo" 293 | } 294 | ], 295 | "dependencies": { 296 | "@ethersproject/abstract-signer": "^5.5.0", 297 | "@ethersproject/address": "^5.5.0", 298 | "@ethersproject/bytes": "^5.5.0", 299 | "@ethersproject/hdnode": "^5.5.0", 300 | "@ethersproject/keccak256": "^5.5.0", 301 | "@ethersproject/logger": "^5.5.0", 302 | "@ethersproject/pbkdf2": "^5.5.0", 303 | "@ethersproject/properties": "^5.5.0", 304 | "@ethersproject/random": "^5.5.0", 305 | "@ethersproject/strings": "^5.5.0", 306 | "@ethersproject/transactions": "^5.5.0", 307 | "aes-js": "3.0.0", 308 | "scrypt-js": "3.0.1" 309 | } 310 | }, 311 | "node_modules/@ethersproject/keccak256": { 312 | "version": "5.5.0", 313 | "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", 314 | "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", 315 | "funding": [ 316 | { 317 | "type": "individual", 318 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 319 | }, 320 | { 321 | "type": "individual", 322 | "url": "https://www.buymeacoffee.com/ricmoo" 323 | } 324 | ], 325 | "dependencies": { 326 | "@ethersproject/bytes": "^5.5.0", 327 | "js-sha3": "0.8.0" 328 | } 329 | }, 330 | "node_modules/@ethersproject/logger": { 331 | "version": "5.5.0", 332 | "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", 333 | "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==", 334 | "funding": [ 335 | { 336 | "type": "individual", 337 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 338 | }, 339 | { 340 | "type": "individual", 341 | "url": "https://www.buymeacoffee.com/ricmoo" 342 | } 343 | ] 344 | }, 345 | "node_modules/@ethersproject/networks": { 346 | "version": "5.5.1", 347 | "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.1.tgz", 348 | "integrity": "sha512-tYRDM4zZtSUcKnD4UMuAlj7SeXH/k5WC4SP2u1Pn57++JdXHkRu2zwNkgNogZoxHzhm9Q6qqurDBVptHOsW49Q==", 349 | "funding": [ 350 | { 351 | "type": "individual", 352 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 353 | }, 354 | { 355 | "type": "individual", 356 | "url": "https://www.buymeacoffee.com/ricmoo" 357 | } 358 | ], 359 | "dependencies": { 360 | "@ethersproject/logger": "^5.5.0" 361 | } 362 | }, 363 | "node_modules/@ethersproject/pbkdf2": { 364 | "version": "5.5.0", 365 | "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz", 366 | "integrity": "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==", 367 | "funding": [ 368 | { 369 | "type": "individual", 370 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 371 | }, 372 | { 373 | "type": "individual", 374 | "url": "https://www.buymeacoffee.com/ricmoo" 375 | } 376 | ], 377 | "dependencies": { 378 | "@ethersproject/bytes": "^5.5.0", 379 | "@ethersproject/sha2": "^5.5.0" 380 | } 381 | }, 382 | "node_modules/@ethersproject/properties": { 383 | "version": "5.5.0", 384 | "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", 385 | "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", 386 | "funding": [ 387 | { 388 | "type": "individual", 389 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 390 | }, 391 | { 392 | "type": "individual", 393 | "url": "https://www.buymeacoffee.com/ricmoo" 394 | } 395 | ], 396 | "dependencies": { 397 | "@ethersproject/logger": "^5.5.0" 398 | } 399 | }, 400 | "node_modules/@ethersproject/providers": { 401 | "version": "5.5.1", 402 | "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.1.tgz", 403 | "integrity": "sha512-2zdD5sltACDWhjUE12Kucg2PcgM6V2q9JMyVvObtVGnzJu+QSmibbP+BHQyLWZUBfLApx2942+7DC5D+n4wBQQ==", 404 | "funding": [ 405 | { 406 | "type": "individual", 407 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 408 | }, 409 | { 410 | "type": "individual", 411 | "url": "https://www.buymeacoffee.com/ricmoo" 412 | } 413 | ], 414 | "dependencies": { 415 | "@ethersproject/abstract-provider": "^5.5.0", 416 | "@ethersproject/abstract-signer": "^5.5.0", 417 | "@ethersproject/address": "^5.5.0", 418 | "@ethersproject/basex": "^5.5.0", 419 | "@ethersproject/bignumber": "^5.5.0", 420 | "@ethersproject/bytes": "^5.5.0", 421 | "@ethersproject/constants": "^5.5.0", 422 | "@ethersproject/hash": "^5.5.0", 423 | "@ethersproject/logger": "^5.5.0", 424 | "@ethersproject/networks": "^5.5.0", 425 | "@ethersproject/properties": "^5.5.0", 426 | "@ethersproject/random": "^5.5.0", 427 | "@ethersproject/rlp": "^5.5.0", 428 | "@ethersproject/sha2": "^5.5.0", 429 | "@ethersproject/strings": "^5.5.0", 430 | "@ethersproject/transactions": "^5.5.0", 431 | "@ethersproject/web": "^5.5.0", 432 | "bech32": "1.1.4", 433 | "ws": "7.4.6" 434 | } 435 | }, 436 | "node_modules/@ethersproject/random": { 437 | "version": "5.5.0", 438 | "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.0.tgz", 439 | "integrity": "sha512-egGYZwZ/YIFKMHcoBUo8t3a8Hb/TKYX8BCBoLjudVCZh892welR3jOxgOmb48xznc9bTcMm7Tpwc1gHC1PFNFQ==", 440 | "funding": [ 441 | { 442 | "type": "individual", 443 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 444 | }, 445 | { 446 | "type": "individual", 447 | "url": "https://www.buymeacoffee.com/ricmoo" 448 | } 449 | ], 450 | "dependencies": { 451 | "@ethersproject/bytes": "^5.5.0", 452 | "@ethersproject/logger": "^5.5.0" 453 | } 454 | }, 455 | "node_modules/@ethersproject/rlp": { 456 | "version": "5.5.0", 457 | "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", 458 | "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", 459 | "funding": [ 460 | { 461 | "type": "individual", 462 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 463 | }, 464 | { 465 | "type": "individual", 466 | "url": "https://www.buymeacoffee.com/ricmoo" 467 | } 468 | ], 469 | "dependencies": { 470 | "@ethersproject/bytes": "^5.5.0", 471 | "@ethersproject/logger": "^5.5.0" 472 | } 473 | }, 474 | "node_modules/@ethersproject/sha2": { 475 | "version": "5.5.0", 476 | "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", 477 | "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", 478 | "funding": [ 479 | { 480 | "type": "individual", 481 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 482 | }, 483 | { 484 | "type": "individual", 485 | "url": "https://www.buymeacoffee.com/ricmoo" 486 | } 487 | ], 488 | "dependencies": { 489 | "@ethersproject/bytes": "^5.5.0", 490 | "@ethersproject/logger": "^5.5.0", 491 | "hash.js": "1.1.7" 492 | } 493 | }, 494 | "node_modules/@ethersproject/signing-key": { 495 | "version": "5.5.0", 496 | "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", 497 | "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", 498 | "funding": [ 499 | { 500 | "type": "individual", 501 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 502 | }, 503 | { 504 | "type": "individual", 505 | "url": "https://www.buymeacoffee.com/ricmoo" 506 | } 507 | ], 508 | "dependencies": { 509 | "@ethersproject/bytes": "^5.5.0", 510 | "@ethersproject/logger": "^5.5.0", 511 | "@ethersproject/properties": "^5.5.0", 512 | "bn.js": "^4.11.9", 513 | "elliptic": "6.5.4", 514 | "hash.js": "1.1.7" 515 | } 516 | }, 517 | "node_modules/@ethersproject/solidity": { 518 | "version": "5.5.0", 519 | "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz", 520 | "integrity": "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==", 521 | "funding": [ 522 | { 523 | "type": "individual", 524 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 525 | }, 526 | { 527 | "type": "individual", 528 | "url": "https://www.buymeacoffee.com/ricmoo" 529 | } 530 | ], 531 | "dependencies": { 532 | "@ethersproject/bignumber": "^5.5.0", 533 | "@ethersproject/bytes": "^5.5.0", 534 | "@ethersproject/keccak256": "^5.5.0", 535 | "@ethersproject/logger": "^5.5.0", 536 | "@ethersproject/sha2": "^5.5.0", 537 | "@ethersproject/strings": "^5.5.0" 538 | } 539 | }, 540 | "node_modules/@ethersproject/strings": { 541 | "version": "5.5.0", 542 | "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", 543 | "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", 544 | "funding": [ 545 | { 546 | "type": "individual", 547 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 548 | }, 549 | { 550 | "type": "individual", 551 | "url": "https://www.buymeacoffee.com/ricmoo" 552 | } 553 | ], 554 | "dependencies": { 555 | "@ethersproject/bytes": "^5.5.0", 556 | "@ethersproject/constants": "^5.5.0", 557 | "@ethersproject/logger": "^5.5.0" 558 | } 559 | }, 560 | "node_modules/@ethersproject/transactions": { 561 | "version": "5.5.0", 562 | "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", 563 | "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", 564 | "funding": [ 565 | { 566 | "type": "individual", 567 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 568 | }, 569 | { 570 | "type": "individual", 571 | "url": "https://www.buymeacoffee.com/ricmoo" 572 | } 573 | ], 574 | "dependencies": { 575 | "@ethersproject/address": "^5.5.0", 576 | "@ethersproject/bignumber": "^5.5.0", 577 | "@ethersproject/bytes": "^5.5.0", 578 | "@ethersproject/constants": "^5.5.0", 579 | "@ethersproject/keccak256": "^5.5.0", 580 | "@ethersproject/logger": "^5.5.0", 581 | "@ethersproject/properties": "^5.5.0", 582 | "@ethersproject/rlp": "^5.5.0", 583 | "@ethersproject/signing-key": "^5.5.0" 584 | } 585 | }, 586 | "node_modules/@ethersproject/units": { 587 | "version": "5.5.0", 588 | "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz", 589 | "integrity": "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==", 590 | "funding": [ 591 | { 592 | "type": "individual", 593 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 594 | }, 595 | { 596 | "type": "individual", 597 | "url": "https://www.buymeacoffee.com/ricmoo" 598 | } 599 | ], 600 | "dependencies": { 601 | "@ethersproject/bignumber": "^5.5.0", 602 | "@ethersproject/constants": "^5.5.0", 603 | "@ethersproject/logger": "^5.5.0" 604 | } 605 | }, 606 | "node_modules/@ethersproject/wallet": { 607 | "version": "5.5.0", 608 | "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz", 609 | "integrity": "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==", 610 | "funding": [ 611 | { 612 | "type": "individual", 613 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 614 | }, 615 | { 616 | "type": "individual", 617 | "url": "https://www.buymeacoffee.com/ricmoo" 618 | } 619 | ], 620 | "dependencies": { 621 | "@ethersproject/abstract-provider": "^5.5.0", 622 | "@ethersproject/abstract-signer": "^5.5.0", 623 | "@ethersproject/address": "^5.5.0", 624 | "@ethersproject/bignumber": "^5.5.0", 625 | "@ethersproject/bytes": "^5.5.0", 626 | "@ethersproject/hash": "^5.5.0", 627 | "@ethersproject/hdnode": "^5.5.0", 628 | "@ethersproject/json-wallets": "^5.5.0", 629 | "@ethersproject/keccak256": "^5.5.0", 630 | "@ethersproject/logger": "^5.5.0", 631 | "@ethersproject/properties": "^5.5.0", 632 | "@ethersproject/random": "^5.5.0", 633 | "@ethersproject/signing-key": "^5.5.0", 634 | "@ethersproject/transactions": "^5.5.0", 635 | "@ethersproject/wordlists": "^5.5.0" 636 | } 637 | }, 638 | "node_modules/@ethersproject/web": { 639 | "version": "5.5.1", 640 | "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", 641 | "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", 642 | "funding": [ 643 | { 644 | "type": "individual", 645 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 646 | }, 647 | { 648 | "type": "individual", 649 | "url": "https://www.buymeacoffee.com/ricmoo" 650 | } 651 | ], 652 | "dependencies": { 653 | "@ethersproject/base64": "^5.5.0", 654 | "@ethersproject/bytes": "^5.5.0", 655 | "@ethersproject/logger": "^5.5.0", 656 | "@ethersproject/properties": "^5.5.0", 657 | "@ethersproject/strings": "^5.5.0" 658 | } 659 | }, 660 | "node_modules/@ethersproject/wordlists": { 661 | "version": "5.5.0", 662 | "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz", 663 | "integrity": "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==", 664 | "funding": [ 665 | { 666 | "type": "individual", 667 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 668 | }, 669 | { 670 | "type": "individual", 671 | "url": "https://www.buymeacoffee.com/ricmoo" 672 | } 673 | ], 674 | "dependencies": { 675 | "@ethersproject/bytes": "^5.5.0", 676 | "@ethersproject/hash": "^5.5.0", 677 | "@ethersproject/logger": "^5.5.0", 678 | "@ethersproject/properties": "^5.5.0", 679 | "@ethersproject/strings": "^5.5.0" 680 | } 681 | }, 682 | "node_modules/@flashbots/ethers-provider-bundle": { 683 | "version": "0.4.2", 684 | "resolved": "https://registry.npmjs.org/@flashbots/ethers-provider-bundle/-/ethers-provider-bundle-0.4.2.tgz", 685 | "integrity": "sha512-LgM30hD+hzovk5fV/WsLZ9pdcisVyYqLVRshRS9skhOYv9XDGIojKbW3rOKqwzREeTm8N2q9GGJO8b5aAYMmNQ==", 686 | "dependencies": { 687 | "ts-node": "^9.1.0", 688 | "typescript": "^4.1.2" 689 | }, 690 | "peerDependencies": { 691 | "ethers": "^5.4.2" 692 | } 693 | }, 694 | "node_modules/aes-js": { 695 | "version": "3.0.0", 696 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", 697 | "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" 698 | }, 699 | "node_modules/arg": { 700 | "version": "4.1.3", 701 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 702 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" 703 | }, 704 | "node_modules/bech32": { 705 | "version": "1.1.4", 706 | "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", 707 | "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" 708 | }, 709 | "node_modules/bn.js": { 710 | "version": "4.12.0", 711 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", 712 | "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" 713 | }, 714 | "node_modules/brorand": { 715 | "version": "1.1.0", 716 | "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", 717 | "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" 718 | }, 719 | "node_modules/buffer-from": { 720 | "version": "1.1.2", 721 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 722 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 723 | }, 724 | "node_modules/create-require": { 725 | "version": "1.1.1", 726 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 727 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" 728 | }, 729 | "node_modules/diff": { 730 | "version": "4.0.2", 731 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 732 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 733 | "engines": { 734 | "node": ">=0.3.1" 735 | } 736 | }, 737 | "node_modules/dotenv": { 738 | "version": "10.0.0", 739 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 740 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", 741 | "engines": { 742 | "node": ">=10" 743 | } 744 | }, 745 | "node_modules/elliptic": { 746 | "version": "6.5.4", 747 | "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", 748 | "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", 749 | "dependencies": { 750 | "bn.js": "^4.11.9", 751 | "brorand": "^1.1.0", 752 | "hash.js": "^1.0.0", 753 | "hmac-drbg": "^1.0.1", 754 | "inherits": "^2.0.4", 755 | "minimalistic-assert": "^1.0.1", 756 | "minimalistic-crypto-utils": "^1.0.1" 757 | } 758 | }, 759 | "node_modules/ethers": { 760 | "version": "5.5.2", 761 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.5.2.tgz", 762 | "integrity": "sha512-EF5W+6Wwcu6BqVwpgmyR5U2+L4c1FQzlM/02dkZOugN3KF0cG9bzHZP+TDJglmPm2/IzCEJDT7KBxzayk7SAHw==", 763 | "funding": [ 764 | { 765 | "type": "individual", 766 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 767 | }, 768 | { 769 | "type": "individual", 770 | "url": "https://www.buymeacoffee.com/ricmoo" 771 | } 772 | ], 773 | "dependencies": { 774 | "@ethersproject/abi": "5.5.0", 775 | "@ethersproject/abstract-provider": "5.5.1", 776 | "@ethersproject/abstract-signer": "5.5.0", 777 | "@ethersproject/address": "5.5.0", 778 | "@ethersproject/base64": "5.5.0", 779 | "@ethersproject/basex": "5.5.0", 780 | "@ethersproject/bignumber": "5.5.0", 781 | "@ethersproject/bytes": "5.5.0", 782 | "@ethersproject/constants": "5.5.0", 783 | "@ethersproject/contracts": "5.5.0", 784 | "@ethersproject/hash": "5.5.0", 785 | "@ethersproject/hdnode": "5.5.0", 786 | "@ethersproject/json-wallets": "5.5.0", 787 | "@ethersproject/keccak256": "5.5.0", 788 | "@ethersproject/logger": "5.5.0", 789 | "@ethersproject/networks": "5.5.1", 790 | "@ethersproject/pbkdf2": "5.5.0", 791 | "@ethersproject/properties": "5.5.0", 792 | "@ethersproject/providers": "5.5.1", 793 | "@ethersproject/random": "5.5.0", 794 | "@ethersproject/rlp": "5.5.0", 795 | "@ethersproject/sha2": "5.5.0", 796 | "@ethersproject/signing-key": "5.5.0", 797 | "@ethersproject/solidity": "5.5.0", 798 | "@ethersproject/strings": "5.5.0", 799 | "@ethersproject/transactions": "5.5.0", 800 | "@ethersproject/units": "5.5.0", 801 | "@ethersproject/wallet": "5.5.0", 802 | "@ethersproject/web": "5.5.1", 803 | "@ethersproject/wordlists": "5.5.0" 804 | } 805 | }, 806 | "node_modules/hash.js": { 807 | "version": "1.1.7", 808 | "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", 809 | "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", 810 | "dependencies": { 811 | "inherits": "^2.0.3", 812 | "minimalistic-assert": "^1.0.1" 813 | } 814 | }, 815 | "node_modules/hmac-drbg": { 816 | "version": "1.0.1", 817 | "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", 818 | "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", 819 | "dependencies": { 820 | "hash.js": "^1.0.3", 821 | "minimalistic-assert": "^1.0.0", 822 | "minimalistic-crypto-utils": "^1.0.1" 823 | } 824 | }, 825 | "node_modules/inherits": { 826 | "version": "2.0.4", 827 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 828 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 829 | }, 830 | "node_modules/js-sha3": { 831 | "version": "0.8.0", 832 | "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", 833 | "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" 834 | }, 835 | "node_modules/make-error": { 836 | "version": "1.3.6", 837 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 838 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" 839 | }, 840 | "node_modules/minimalistic-assert": { 841 | "version": "1.0.1", 842 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 843 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 844 | }, 845 | "node_modules/minimalistic-crypto-utils": { 846 | "version": "1.0.1", 847 | "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", 848 | "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" 849 | }, 850 | "node_modules/scrypt-js": { 851 | "version": "3.0.1", 852 | "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", 853 | "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" 854 | }, 855 | "node_modules/source-map": { 856 | "version": "0.6.1", 857 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 858 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 859 | "engines": { 860 | "node": ">=0.10.0" 861 | } 862 | }, 863 | "node_modules/source-map-support": { 864 | "version": "0.5.21", 865 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 866 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 867 | "dependencies": { 868 | "buffer-from": "^1.0.0", 869 | "source-map": "^0.6.0" 870 | } 871 | }, 872 | "node_modules/ts-node": { 873 | "version": "9.1.1", 874 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", 875 | "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", 876 | "dependencies": { 877 | "arg": "^4.1.0", 878 | "create-require": "^1.1.0", 879 | "diff": "^4.0.1", 880 | "make-error": "^1.1.1", 881 | "source-map-support": "^0.5.17", 882 | "yn": "3.1.1" 883 | }, 884 | "bin": { 885 | "ts-node": "dist/bin.js", 886 | "ts-node-script": "dist/bin-script.js", 887 | "ts-node-transpile-only": "dist/bin-transpile.js", 888 | "ts-script": "dist/bin-script-deprecated.js" 889 | }, 890 | "engines": { 891 | "node": ">=10.0.0" 892 | }, 893 | "peerDependencies": { 894 | "typescript": ">=2.7" 895 | } 896 | }, 897 | "node_modules/typescript": { 898 | "version": "4.5.2", 899 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", 900 | "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", 901 | "bin": { 902 | "tsc": "bin/tsc", 903 | "tsserver": "bin/tsserver" 904 | }, 905 | "engines": { 906 | "node": ">=4.2.0" 907 | } 908 | }, 909 | "node_modules/ws": { 910 | "version": "7.4.6", 911 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 912 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 913 | "engines": { 914 | "node": ">=8.3.0" 915 | }, 916 | "peerDependencies": { 917 | "bufferutil": "^4.0.1", 918 | "utf-8-validate": "^5.0.2" 919 | }, 920 | "peerDependenciesMeta": { 921 | "bufferutil": { 922 | "optional": true 923 | }, 924 | "utf-8-validate": { 925 | "optional": true 926 | } 927 | } 928 | }, 929 | "node_modules/yn": { 930 | "version": "3.1.1", 931 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 932 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 933 | "engines": { 934 | "node": ">=6" 935 | } 936 | } 937 | }, 938 | "dependencies": { 939 | "@ethersproject/abi": { 940 | "version": "5.5.0", 941 | "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", 942 | "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", 943 | "requires": { 944 | "@ethersproject/address": "^5.5.0", 945 | "@ethersproject/bignumber": "^5.5.0", 946 | "@ethersproject/bytes": "^5.5.0", 947 | "@ethersproject/constants": "^5.5.0", 948 | "@ethersproject/hash": "^5.5.0", 949 | "@ethersproject/keccak256": "^5.5.0", 950 | "@ethersproject/logger": "^5.5.0", 951 | "@ethersproject/properties": "^5.5.0", 952 | "@ethersproject/strings": "^5.5.0" 953 | } 954 | }, 955 | "@ethersproject/abstract-provider": { 956 | "version": "5.5.1", 957 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", 958 | "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", 959 | "requires": { 960 | "@ethersproject/bignumber": "^5.5.0", 961 | "@ethersproject/bytes": "^5.5.0", 962 | "@ethersproject/logger": "^5.5.0", 963 | "@ethersproject/networks": "^5.5.0", 964 | "@ethersproject/properties": "^5.5.0", 965 | "@ethersproject/transactions": "^5.5.0", 966 | "@ethersproject/web": "^5.5.0" 967 | } 968 | }, 969 | "@ethersproject/abstract-signer": { 970 | "version": "5.5.0", 971 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", 972 | "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", 973 | "requires": { 974 | "@ethersproject/abstract-provider": "^5.5.0", 975 | "@ethersproject/bignumber": "^5.5.0", 976 | "@ethersproject/bytes": "^5.5.0", 977 | "@ethersproject/logger": "^5.5.0", 978 | "@ethersproject/properties": "^5.5.0" 979 | } 980 | }, 981 | "@ethersproject/address": { 982 | "version": "5.5.0", 983 | "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", 984 | "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", 985 | "requires": { 986 | "@ethersproject/bignumber": "^5.5.0", 987 | "@ethersproject/bytes": "^5.5.0", 988 | "@ethersproject/keccak256": "^5.5.0", 989 | "@ethersproject/logger": "^5.5.0", 990 | "@ethersproject/rlp": "^5.5.0" 991 | } 992 | }, 993 | "@ethersproject/base64": { 994 | "version": "5.5.0", 995 | "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", 996 | "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", 997 | "requires": { 998 | "@ethersproject/bytes": "^5.5.0" 999 | } 1000 | }, 1001 | "@ethersproject/basex": { 1002 | "version": "5.5.0", 1003 | "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz", 1004 | "integrity": "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==", 1005 | "requires": { 1006 | "@ethersproject/bytes": "^5.5.0", 1007 | "@ethersproject/properties": "^5.5.0" 1008 | } 1009 | }, 1010 | "@ethersproject/bignumber": { 1011 | "version": "5.5.0", 1012 | "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", 1013 | "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", 1014 | "requires": { 1015 | "@ethersproject/bytes": "^5.5.0", 1016 | "@ethersproject/logger": "^5.5.0", 1017 | "bn.js": "^4.11.9" 1018 | } 1019 | }, 1020 | "@ethersproject/bytes": { 1021 | "version": "5.5.0", 1022 | "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", 1023 | "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", 1024 | "requires": { 1025 | "@ethersproject/logger": "^5.5.0" 1026 | } 1027 | }, 1028 | "@ethersproject/constants": { 1029 | "version": "5.5.0", 1030 | "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", 1031 | "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", 1032 | "requires": { 1033 | "@ethersproject/bignumber": "^5.5.0" 1034 | } 1035 | }, 1036 | "@ethersproject/contracts": { 1037 | "version": "5.5.0", 1038 | "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz", 1039 | "integrity": "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==", 1040 | "requires": { 1041 | "@ethersproject/abi": "^5.5.0", 1042 | "@ethersproject/abstract-provider": "^5.5.0", 1043 | "@ethersproject/abstract-signer": "^5.5.0", 1044 | "@ethersproject/address": "^5.5.0", 1045 | "@ethersproject/bignumber": "^5.5.0", 1046 | "@ethersproject/bytes": "^5.5.0", 1047 | "@ethersproject/constants": "^5.5.0", 1048 | "@ethersproject/logger": "^5.5.0", 1049 | "@ethersproject/properties": "^5.5.0", 1050 | "@ethersproject/transactions": "^5.5.0" 1051 | } 1052 | }, 1053 | "@ethersproject/hash": { 1054 | "version": "5.5.0", 1055 | "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", 1056 | "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", 1057 | "requires": { 1058 | "@ethersproject/abstract-signer": "^5.5.0", 1059 | "@ethersproject/address": "^5.5.0", 1060 | "@ethersproject/bignumber": "^5.5.0", 1061 | "@ethersproject/bytes": "^5.5.0", 1062 | "@ethersproject/keccak256": "^5.5.0", 1063 | "@ethersproject/logger": "^5.5.0", 1064 | "@ethersproject/properties": "^5.5.0", 1065 | "@ethersproject/strings": "^5.5.0" 1066 | } 1067 | }, 1068 | "@ethersproject/hdnode": { 1069 | "version": "5.5.0", 1070 | "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz", 1071 | "integrity": "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==", 1072 | "requires": { 1073 | "@ethersproject/abstract-signer": "^5.5.0", 1074 | "@ethersproject/basex": "^5.5.0", 1075 | "@ethersproject/bignumber": "^5.5.0", 1076 | "@ethersproject/bytes": "^5.5.0", 1077 | "@ethersproject/logger": "^5.5.0", 1078 | "@ethersproject/pbkdf2": "^5.5.0", 1079 | "@ethersproject/properties": "^5.5.0", 1080 | "@ethersproject/sha2": "^5.5.0", 1081 | "@ethersproject/signing-key": "^5.5.0", 1082 | "@ethersproject/strings": "^5.5.0", 1083 | "@ethersproject/transactions": "^5.5.0", 1084 | "@ethersproject/wordlists": "^5.5.0" 1085 | } 1086 | }, 1087 | "@ethersproject/json-wallets": { 1088 | "version": "5.5.0", 1089 | "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz", 1090 | "integrity": "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==", 1091 | "requires": { 1092 | "@ethersproject/abstract-signer": "^5.5.0", 1093 | "@ethersproject/address": "^5.5.0", 1094 | "@ethersproject/bytes": "^5.5.0", 1095 | "@ethersproject/hdnode": "^5.5.0", 1096 | "@ethersproject/keccak256": "^5.5.0", 1097 | "@ethersproject/logger": "^5.5.0", 1098 | "@ethersproject/pbkdf2": "^5.5.0", 1099 | "@ethersproject/properties": "^5.5.0", 1100 | "@ethersproject/random": "^5.5.0", 1101 | "@ethersproject/strings": "^5.5.0", 1102 | "@ethersproject/transactions": "^5.5.0", 1103 | "aes-js": "3.0.0", 1104 | "scrypt-js": "3.0.1" 1105 | } 1106 | }, 1107 | "@ethersproject/keccak256": { 1108 | "version": "5.5.0", 1109 | "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", 1110 | "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", 1111 | "requires": { 1112 | "@ethersproject/bytes": "^5.5.0", 1113 | "js-sha3": "0.8.0" 1114 | } 1115 | }, 1116 | "@ethersproject/logger": { 1117 | "version": "5.5.0", 1118 | "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", 1119 | "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" 1120 | }, 1121 | "@ethersproject/networks": { 1122 | "version": "5.5.1", 1123 | "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.1.tgz", 1124 | "integrity": "sha512-tYRDM4zZtSUcKnD4UMuAlj7SeXH/k5WC4SP2u1Pn57++JdXHkRu2zwNkgNogZoxHzhm9Q6qqurDBVptHOsW49Q==", 1125 | "requires": { 1126 | "@ethersproject/logger": "^5.5.0" 1127 | } 1128 | }, 1129 | "@ethersproject/pbkdf2": { 1130 | "version": "5.5.0", 1131 | "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz", 1132 | "integrity": "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==", 1133 | "requires": { 1134 | "@ethersproject/bytes": "^5.5.0", 1135 | "@ethersproject/sha2": "^5.5.0" 1136 | } 1137 | }, 1138 | "@ethersproject/properties": { 1139 | "version": "5.5.0", 1140 | "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", 1141 | "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", 1142 | "requires": { 1143 | "@ethersproject/logger": "^5.5.0" 1144 | } 1145 | }, 1146 | "@ethersproject/providers": { 1147 | "version": "5.5.1", 1148 | "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.1.tgz", 1149 | "integrity": "sha512-2zdD5sltACDWhjUE12Kucg2PcgM6V2q9JMyVvObtVGnzJu+QSmibbP+BHQyLWZUBfLApx2942+7DC5D+n4wBQQ==", 1150 | "requires": { 1151 | "@ethersproject/abstract-provider": "^5.5.0", 1152 | "@ethersproject/abstract-signer": "^5.5.0", 1153 | "@ethersproject/address": "^5.5.0", 1154 | "@ethersproject/basex": "^5.5.0", 1155 | "@ethersproject/bignumber": "^5.5.0", 1156 | "@ethersproject/bytes": "^5.5.0", 1157 | "@ethersproject/constants": "^5.5.0", 1158 | "@ethersproject/hash": "^5.5.0", 1159 | "@ethersproject/logger": "^5.5.0", 1160 | "@ethersproject/networks": "^5.5.0", 1161 | "@ethersproject/properties": "^5.5.0", 1162 | "@ethersproject/random": "^5.5.0", 1163 | "@ethersproject/rlp": "^5.5.0", 1164 | "@ethersproject/sha2": "^5.5.0", 1165 | "@ethersproject/strings": "^5.5.0", 1166 | "@ethersproject/transactions": "^5.5.0", 1167 | "@ethersproject/web": "^5.5.0", 1168 | "bech32": "1.1.4", 1169 | "ws": "7.4.6" 1170 | } 1171 | }, 1172 | "@ethersproject/random": { 1173 | "version": "5.5.0", 1174 | "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.0.tgz", 1175 | "integrity": "sha512-egGYZwZ/YIFKMHcoBUo8t3a8Hb/TKYX8BCBoLjudVCZh892welR3jOxgOmb48xznc9bTcMm7Tpwc1gHC1PFNFQ==", 1176 | "requires": { 1177 | "@ethersproject/bytes": "^5.5.0", 1178 | "@ethersproject/logger": "^5.5.0" 1179 | } 1180 | }, 1181 | "@ethersproject/rlp": { 1182 | "version": "5.5.0", 1183 | "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", 1184 | "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", 1185 | "requires": { 1186 | "@ethersproject/bytes": "^5.5.0", 1187 | "@ethersproject/logger": "^5.5.0" 1188 | } 1189 | }, 1190 | "@ethersproject/sha2": { 1191 | "version": "5.5.0", 1192 | "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", 1193 | "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", 1194 | "requires": { 1195 | "@ethersproject/bytes": "^5.5.0", 1196 | "@ethersproject/logger": "^5.5.0", 1197 | "hash.js": "1.1.7" 1198 | } 1199 | }, 1200 | "@ethersproject/signing-key": { 1201 | "version": "5.5.0", 1202 | "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", 1203 | "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", 1204 | "requires": { 1205 | "@ethersproject/bytes": "^5.5.0", 1206 | "@ethersproject/logger": "^5.5.0", 1207 | "@ethersproject/properties": "^5.5.0", 1208 | "bn.js": "^4.11.9", 1209 | "elliptic": "6.5.4", 1210 | "hash.js": "1.1.7" 1211 | } 1212 | }, 1213 | "@ethersproject/solidity": { 1214 | "version": "5.5.0", 1215 | "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz", 1216 | "integrity": "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==", 1217 | "requires": { 1218 | "@ethersproject/bignumber": "^5.5.0", 1219 | "@ethersproject/bytes": "^5.5.0", 1220 | "@ethersproject/keccak256": "^5.5.0", 1221 | "@ethersproject/logger": "^5.5.0", 1222 | "@ethersproject/sha2": "^5.5.0", 1223 | "@ethersproject/strings": "^5.5.0" 1224 | } 1225 | }, 1226 | "@ethersproject/strings": { 1227 | "version": "5.5.0", 1228 | "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", 1229 | "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", 1230 | "requires": { 1231 | "@ethersproject/bytes": "^5.5.0", 1232 | "@ethersproject/constants": "^5.5.0", 1233 | "@ethersproject/logger": "^5.5.0" 1234 | } 1235 | }, 1236 | "@ethersproject/transactions": { 1237 | "version": "5.5.0", 1238 | "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", 1239 | "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", 1240 | "requires": { 1241 | "@ethersproject/address": "^5.5.0", 1242 | "@ethersproject/bignumber": "^5.5.0", 1243 | "@ethersproject/bytes": "^5.5.0", 1244 | "@ethersproject/constants": "^5.5.0", 1245 | "@ethersproject/keccak256": "^5.5.0", 1246 | "@ethersproject/logger": "^5.5.0", 1247 | "@ethersproject/properties": "^5.5.0", 1248 | "@ethersproject/rlp": "^5.5.0", 1249 | "@ethersproject/signing-key": "^5.5.0" 1250 | } 1251 | }, 1252 | "@ethersproject/units": { 1253 | "version": "5.5.0", 1254 | "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz", 1255 | "integrity": "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==", 1256 | "requires": { 1257 | "@ethersproject/bignumber": "^5.5.0", 1258 | "@ethersproject/constants": "^5.5.0", 1259 | "@ethersproject/logger": "^5.5.0" 1260 | } 1261 | }, 1262 | "@ethersproject/wallet": { 1263 | "version": "5.5.0", 1264 | "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz", 1265 | "integrity": "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==", 1266 | "requires": { 1267 | "@ethersproject/abstract-provider": "^5.5.0", 1268 | "@ethersproject/abstract-signer": "^5.5.0", 1269 | "@ethersproject/address": "^5.5.0", 1270 | "@ethersproject/bignumber": "^5.5.0", 1271 | "@ethersproject/bytes": "^5.5.0", 1272 | "@ethersproject/hash": "^5.5.0", 1273 | "@ethersproject/hdnode": "^5.5.0", 1274 | "@ethersproject/json-wallets": "^5.5.0", 1275 | "@ethersproject/keccak256": "^5.5.0", 1276 | "@ethersproject/logger": "^5.5.0", 1277 | "@ethersproject/properties": "^5.5.0", 1278 | "@ethersproject/random": "^5.5.0", 1279 | "@ethersproject/signing-key": "^5.5.0", 1280 | "@ethersproject/transactions": "^5.5.0", 1281 | "@ethersproject/wordlists": "^5.5.0" 1282 | } 1283 | }, 1284 | "@ethersproject/web": { 1285 | "version": "5.5.1", 1286 | "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", 1287 | "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", 1288 | "requires": { 1289 | "@ethersproject/base64": "^5.5.0", 1290 | "@ethersproject/bytes": "^5.5.0", 1291 | "@ethersproject/logger": "^5.5.0", 1292 | "@ethersproject/properties": "^5.5.0", 1293 | "@ethersproject/strings": "^5.5.0" 1294 | } 1295 | }, 1296 | "@ethersproject/wordlists": { 1297 | "version": "5.5.0", 1298 | "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz", 1299 | "integrity": "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==", 1300 | "requires": { 1301 | "@ethersproject/bytes": "^5.5.0", 1302 | "@ethersproject/hash": "^5.5.0", 1303 | "@ethersproject/logger": "^5.5.0", 1304 | "@ethersproject/properties": "^5.5.0", 1305 | "@ethersproject/strings": "^5.5.0" 1306 | } 1307 | }, 1308 | "@flashbots/ethers-provider-bundle": { 1309 | "version": "0.4.2", 1310 | "resolved": "https://registry.npmjs.org/@flashbots/ethers-provider-bundle/-/ethers-provider-bundle-0.4.2.tgz", 1311 | "integrity": "sha512-LgM30hD+hzovk5fV/WsLZ9pdcisVyYqLVRshRS9skhOYv9XDGIojKbW3rOKqwzREeTm8N2q9GGJO8b5aAYMmNQ==", 1312 | "requires": { 1313 | "ts-node": "^9.1.0", 1314 | "typescript": "^4.1.2" 1315 | } 1316 | }, 1317 | "aes-js": { 1318 | "version": "3.0.0", 1319 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", 1320 | "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" 1321 | }, 1322 | "arg": { 1323 | "version": "4.1.3", 1324 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 1325 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" 1326 | }, 1327 | "bech32": { 1328 | "version": "1.1.4", 1329 | "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", 1330 | "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" 1331 | }, 1332 | "bn.js": { 1333 | "version": "4.12.0", 1334 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", 1335 | "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" 1336 | }, 1337 | "brorand": { 1338 | "version": "1.1.0", 1339 | "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", 1340 | "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" 1341 | }, 1342 | "buffer-from": { 1343 | "version": "1.1.2", 1344 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 1345 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 1346 | }, 1347 | "create-require": { 1348 | "version": "1.1.1", 1349 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 1350 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" 1351 | }, 1352 | "diff": { 1353 | "version": "4.0.2", 1354 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 1355 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" 1356 | }, 1357 | "dotenv": { 1358 | "version": "10.0.0", 1359 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 1360 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" 1361 | }, 1362 | "elliptic": { 1363 | "version": "6.5.4", 1364 | "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", 1365 | "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", 1366 | "requires": { 1367 | "bn.js": "^4.11.9", 1368 | "brorand": "^1.1.0", 1369 | "hash.js": "^1.0.0", 1370 | "hmac-drbg": "^1.0.1", 1371 | "inherits": "^2.0.4", 1372 | "minimalistic-assert": "^1.0.1", 1373 | "minimalistic-crypto-utils": "^1.0.1" 1374 | } 1375 | }, 1376 | "ethers": { 1377 | "version": "5.5.2", 1378 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.5.2.tgz", 1379 | "integrity": "sha512-EF5W+6Wwcu6BqVwpgmyR5U2+L4c1FQzlM/02dkZOugN3KF0cG9bzHZP+TDJglmPm2/IzCEJDT7KBxzayk7SAHw==", 1380 | "requires": { 1381 | "@ethersproject/abi": "5.5.0", 1382 | "@ethersproject/abstract-provider": "5.5.1", 1383 | "@ethersproject/abstract-signer": "5.5.0", 1384 | "@ethersproject/address": "5.5.0", 1385 | "@ethersproject/base64": "5.5.0", 1386 | "@ethersproject/basex": "5.5.0", 1387 | "@ethersproject/bignumber": "5.5.0", 1388 | "@ethersproject/bytes": "5.5.0", 1389 | "@ethersproject/constants": "5.5.0", 1390 | "@ethersproject/contracts": "5.5.0", 1391 | "@ethersproject/hash": "5.5.0", 1392 | "@ethersproject/hdnode": "5.5.0", 1393 | "@ethersproject/json-wallets": "5.5.0", 1394 | "@ethersproject/keccak256": "5.5.0", 1395 | "@ethersproject/logger": "5.5.0", 1396 | "@ethersproject/networks": "5.5.1", 1397 | "@ethersproject/pbkdf2": "5.5.0", 1398 | "@ethersproject/properties": "5.5.0", 1399 | "@ethersproject/providers": "5.5.1", 1400 | "@ethersproject/random": "5.5.0", 1401 | "@ethersproject/rlp": "5.5.0", 1402 | "@ethersproject/sha2": "5.5.0", 1403 | "@ethersproject/signing-key": "5.5.0", 1404 | "@ethersproject/solidity": "5.5.0", 1405 | "@ethersproject/strings": "5.5.0", 1406 | "@ethersproject/transactions": "5.5.0", 1407 | "@ethersproject/units": "5.5.0", 1408 | "@ethersproject/wallet": "5.5.0", 1409 | "@ethersproject/web": "5.5.1", 1410 | "@ethersproject/wordlists": "5.5.0" 1411 | } 1412 | }, 1413 | "hash.js": { 1414 | "version": "1.1.7", 1415 | "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", 1416 | "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", 1417 | "requires": { 1418 | "inherits": "^2.0.3", 1419 | "minimalistic-assert": "^1.0.1" 1420 | } 1421 | }, 1422 | "hmac-drbg": { 1423 | "version": "1.0.1", 1424 | "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", 1425 | "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", 1426 | "requires": { 1427 | "hash.js": "^1.0.3", 1428 | "minimalistic-assert": "^1.0.0", 1429 | "minimalistic-crypto-utils": "^1.0.1" 1430 | } 1431 | }, 1432 | "inherits": { 1433 | "version": "2.0.4", 1434 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1435 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1436 | }, 1437 | "js-sha3": { 1438 | "version": "0.8.0", 1439 | "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", 1440 | "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" 1441 | }, 1442 | "make-error": { 1443 | "version": "1.3.6", 1444 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 1445 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" 1446 | }, 1447 | "minimalistic-assert": { 1448 | "version": "1.0.1", 1449 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 1450 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 1451 | }, 1452 | "minimalistic-crypto-utils": { 1453 | "version": "1.0.1", 1454 | "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", 1455 | "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" 1456 | }, 1457 | "scrypt-js": { 1458 | "version": "3.0.1", 1459 | "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", 1460 | "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" 1461 | }, 1462 | "source-map": { 1463 | "version": "0.6.1", 1464 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1465 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 1466 | }, 1467 | "source-map-support": { 1468 | "version": "0.5.21", 1469 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1470 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1471 | "requires": { 1472 | "buffer-from": "^1.0.0", 1473 | "source-map": "^0.6.0" 1474 | } 1475 | }, 1476 | "ts-node": { 1477 | "version": "9.1.1", 1478 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", 1479 | "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", 1480 | "requires": { 1481 | "arg": "^4.1.0", 1482 | "create-require": "^1.1.0", 1483 | "diff": "^4.0.1", 1484 | "make-error": "^1.1.1", 1485 | "source-map-support": "^0.5.17", 1486 | "yn": "3.1.1" 1487 | } 1488 | }, 1489 | "typescript": { 1490 | "version": "4.5.2", 1491 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", 1492 | "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==" 1493 | }, 1494 | "ws": { 1495 | "version": "7.4.6", 1496 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 1497 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 1498 | "requires": {} 1499 | }, 1500 | "yn": { 1501 | "version": "3.1.1", 1502 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1503 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" 1504 | } 1505 | } 1506 | } 1507 | -------------------------------------------------------------------------------- /nft-sponosored-tx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@flashbots/ethers-provider-bundle": "^0.4.2", 4 | "dotenv": "^10.0.0", 5 | "ethers": "^5.5.2" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /nft-sponosored-tx/pics/exploit-withdraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/nft-sponosored-tx/pics/exploit-withdraw.png -------------------------------------------------------------------------------- /nft-sponosored-tx/pics/js_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/nft-sponosored-tx/pics/js_code.png -------------------------------------------------------------------------------- /nft-sponosored-tx/pics/nft-transfer-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/nft-sponosored-tx/pics/nft-transfer-example.png -------------------------------------------------------------------------------- /nft-sponosored-tx/pics/sponsor_address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/nft-sponosored-tx/pics/sponsor_address.png -------------------------------------------------------------------------------- /nft-sponosored-tx/pics/sponsor_rescued.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/nft-sponosored-tx/pics/sponsor_rescued.png -------------------------------------------------------------------------------- /nft-sponosored-tx/pics/stuck_NFT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schepal/flashbots_playground/1abe6a57c3830d7e0d97150d7e966090acbbf13a/nft-sponosored-tx/pics/stuck_NFT.png -------------------------------------------------------------------------------- /nft-sponosored-tx/pics/tmp.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nft-sponosored-tx/src/sponsorTx.js: -------------------------------------------------------------------------------- 1 | const ethers = require("ethers"); 2 | const flashbot = require("@flashbots/ethers-provider-bundle"); 3 | require("dotenv").config(); 4 | 5 | // load required NFT ABI: https://api-goerli.etherscan.io/api?module=contract&action=getabi&address=0xf5de760f2e916647fd766B4AD9E85ff943cE3A2b 6 | const nftABI = require("../abi/nft.json"); 7 | // address of the deployed NFT smart-contract 8 | const nftAddress = "0xf5de760f2e916647fd766B4AD9E85ff943cE3A2b"; 9 | // unique ID of the NFT we are trying to rescue 10 | const nftID = 95744; 11 | 12 | async function main() { 13 | // goerli network 14 | const CHAIN_ID = 5; 15 | // initialize provider 16 | const provider = new ethers.providers.AlchemyProvider(CHAIN_ID); 17 | // initialize exploited wallet with compromised private key (this wallet contains the NFT) 18 | const exploited = new ethers.Wallet(process.env.EXPLOIT, provider); 19 | // initialize sponsor wallet which will be used to pay for gas fees (this wallet will receive the NFT) 20 | const sponsor = new ethers.Wallet(process.env.SPONSOR, provider); 21 | // initialize the flashbots provider (this will be used to relay the flashbots transaction bundle) 22 | const flashbotProvider = await flashbot.FlashbotsBundleProvider.create( 23 | provider, 24 | sponsor, 25 | "https://relay-goerli.flashbots.net", 26 | "goerli" 27 | ); 28 | // define the NFT contract as an ethers object 29 | const ERC721 = new ethers.Contract(nftAddress, nftABI, provider); 30 | /* call the `transferFrom` function of the ERC721 contract 31 | Recall `transerFrom` has the following parameters: 32 | - from (address): the exploited address will be sending the NFT 33 | - to (address): in this case the sponsor address paying for the NFT transfer gas fees will also receive the NFT 34 | - tokenId (uint256): the unique tokenId of the NFT we are trying to rescue (ie: 95744) 35 | */ 36 | const nftTransfer = await ERC721.populateTransaction.transferFrom( 37 | exploited.address, 38 | sponsor.address, 39 | nftID 40 | ); 41 | // the first transaction will pay for the gas fees of transferring the NFT from the exploited address to the sponsor address 42 | const tx1 = { 43 | // goerli testnet 44 | chainId: CHAIN_ID, 45 | // EIP-1559 style transaction 46 | type: 2, 47 | // manually specify the amount of ETH to send to the exploited address to pay for gas fees 48 | value: ethers.utils.parseUnits("0.01", "ether"), 49 | // the max fee per unit of gas (arbitrarily set) 50 | maxFeePerGas: ethers.utils.parseUnits("3", "gwei"), 51 | // the max miner tip to pay (arbitrarily set) 52 | maxPriorityFeePerGas: ethers.utils.parseUnits("2", "gwei"), 53 | // the gas limit (arbitrarily set) 54 | gasLimit: 21000, 55 | // the ETH must be received by the exploited address so it can pay for the gas fees to transfer the NFT 56 | to: exploited.address 57 | } 58 | // the second transaction will send the NFT from the exploited address to the sponsor address 59 | const tx2 = { 60 | // goerli testnet 61 | chainId: CHAIN_ID, 62 | // EIP-1559 style transaction 63 | type: 2, 64 | // no ether is transferred when moving the NFT to the safe sponsor account 65 | value: ethers.utils.parseUnits("0", "ether"), 66 | // the necessary tx calldata required to transfer the NFT using `transferFrom` 67 | data: nftTransfer.data, 68 | // the max fee per unit of gas (arbitrarily set) 69 | maxFeePerGas: ethers.utils.parseUnits("3", "gwei"), 70 | // the max miner tip to pay (arbitrarily set) 71 | maxPriorityFeePerGas: ethers.utils.parseUnits("2", "gwei"), 72 | // the gas limit (arbitrarily set) 73 | gasLimit: 100000, 74 | // the transaction must interact directly with the deployed NFT address on goerli network 75 | to: nftAddress 76 | } 77 | // sign the transactions into a single flashbots bundle 78 | const signedTxBundle = await flashbotProvider.signBundle([ 79 | { 80 | // the sponsor will be sending ETH to the exploited address in tx1 81 | signer: sponsor, 82 | transaction: tx1 83 | }, 84 | { 85 | // the exploited address will send the NFT to the sponsor address in tx2 86 | signer: exploited, 87 | transaction: tx2 88 | } 89 | ]); 90 | // for each block we need to continously re-submit the bundle transaction until it is selected by a miner 91 | provider.on("block", async(blockNumber) => { 92 | console.log("Current Block: ", blockNumber); 93 | // send the signed bundle transaction data to the flashbots relayers for the closest next future block (ie: t+1) 94 | const signedBundle = await flashbotProvider.sendRawBundle(signedTxBundle, blockNumber + 1); 95 | // wait until we receive a response and exit only once the transaction has been mined in the blockchain 96 | const waitResponse = await signedBundle.wait(); 97 | if(waitResponse == 0) { 98 | console.log("Successfully transferred the NFT to the sponsor address"); 99 | process.exit(); 100 | } 101 | }); 102 | } 103 | 104 | main() 105 | 106 | 107 | 108 | --------------------------------------------------------------------------------