Skip to content

Commit

Permalink
add bundle contract
Browse files Browse the repository at this point in the history
  • Loading branch information
yanyanho committed Mar 28, 2024
1 parent ec6374c commit 8b976eb
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 1 deletion.
11 changes: 10 additions & 1 deletion basic/40-Flashbots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ const res = await flashbotsProvider.sendPrivateTransaction(privateTx)
```



**查询交易状态**
https://protect-sepolia.flashbots.net/tx
https://blocks.flashbots.net/

### 环境
- ETHEREUM_RPC_URL - Ethereum RPC endpoint. Can not be the same as FLASHBOTS_RPC_URL
Expand All @@ -106,6 +108,12 @@ const res = await flashbotsProvider.sendPrivateTransaction(privateTx)
- RECIPIENT - Ethereum EOA to receive assets from ZERO_GAS account
- FLASHBOTS_RELAY_SIGNING_KEY - Optional param, private key used to sign messages to Flashbots to establish reputation of profitability


### run
```
npx hardhat run scripts/send-1559-flashbot.js --network sepolia
```

### 代码
https://github.com/flashbots/searcher-sponsored-tx?tab=readme-ov-file

Expand All @@ -117,6 +125,7 @@ https://github.com/flashbots/simple-arbitrage/blob/master/contracts/BundleExecut

- MEV in 2021 - a year in review: https://www.youtube.com/watch?v=V_wlCeVWMgk
- flashbots-bundle github: https://github.com/flashbots/ethers-provider-flashbots-bundle
- flashbot youtube : https://www.youtube.com/watch?v=V_wlCeVWMgk
- flashbots github: https://github.com/flashbots/pm#resources
- flashbots doc: https://docs.flashbots.net/new-to-mev
- Eden Network 介绍: https://imtoken.fans/t/topic/41713
Expand Down
79 changes: 79 additions & 0 deletions basic/40-Flashbots/contracts/BundleExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;

interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);

function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);

function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}

interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint) external;
}

// This contract simply calls multiple targets sequentially, ensuring WETH balance before and after

contract FlashBotsMultiCall {
address private immutable owner;
address private immutable executor;
IWETH private constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);

modifier onlyExecutor() {
require(msg.sender == executor);
_;
}

modifier onlyOwner() {
require(msg.sender == owner);
_;
}

constructor(address _executor) payable {
owner = msg.sender;
executor = _executor;
if (msg.value > 0) {
WETH.deposit{value: msg.value}();
}
}

receive() external payable {
}

function uniswapWeth(uint256 _wethAmountToFirstMarket, uint256 _ethAmountToCoinbase, address[] memory _targets, bytes[] memory _payloads) external onlyExecutor payable {
require (_targets.length == _payloads.length);
uint256 _wethBalanceBefore = WETH.balanceOf(address(this));
WETH.transfer(_targets[0], _wethAmountToFirstMarket);
for (uint256 i = 0; i < _targets.length; i++) {
(bool _success, bytes memory _response) = _targets[i].call(_payloads[i]);
require(_success); _response;
}

uint256 _wethBalanceAfter = WETH.balanceOf(address(this));
require(_wethBalanceAfter > _wethBalanceBefore + _ethAmountToCoinbase);
if (_ethAmountToCoinbase == 0) return;

uint256 _ethBalance = address(this).balance;
if (_ethBalance < _ethAmountToCoinbase) {
WETH.withdraw(_ethAmountToCoinbase - _ethBalance);
}
block.coinbase.transfer(_ethAmountToCoinbase);
}

function call(address payable _to, uint256 _value, bytes calldata _data) external onlyOwner payable returns (bytes memory) {
require(_to != address(0));
(bool _success, bytes memory _result) = _to.call{value: _value}(_data);
require(_success);
return _result;
}
}
59 changes: 59 additions & 0 deletions basic/40-Flashbots/contracts/FlashbotsCheckAndSend.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
// pragma experimental ABIEncoderV2;

/*
Copyright 2021 Flashbots: Scott Bigelow ([email protected]).
*/

// This contract performs one or many staticcall's, compares their output, and pays
// the miner directly if all calls exactly match the specified result
// For how to use this script, read the Flashbots searcher docs: https://github.com/flashbots/pm/blob/main/guides/searcher-onboarding.md
contract FlashbotsCheckAndSend {
function check32BytesAndSend(address _target, bytes memory _payload, bytes32 _resultMatch) external payable {
_check32Bytes(_target, _payload, _resultMatch);
block.coinbase.transfer(msg.value);
}

function check32BytesAndSendMulti(address[] memory _targets, bytes[] memory _payloads, bytes32[] memory _resultMatches) external payable {
require (_targets.length == _payloads.length);
require (_targets.length == _resultMatches.length);
for (uint256 i = 0; i < _targets.length; i++) {
_check32Bytes(_targets[i], _payloads[i], _resultMatches[i]);
}
block.coinbase.transfer(msg.value);
}

function checkBytesAndSend(address _target, bytes memory _payload, bytes memory _resultMatch) external payable {
_checkBytes(_target, _payload, _resultMatch);
block.coinbase.transfer(msg.value);
}

function checkBytesAndSendMulti(address[] memory _targets, bytes[] memory _payloads, bytes[] memory _resultMatches) external payable {
require (_targets.length == _payloads.length);
require (_targets.length == _resultMatches.length);
for (uint256 i = 0; i < _targets.length; i++) {
_checkBytes(_targets[i], _payloads[i], _resultMatches[i]);
}
block.coinbase.transfer(msg.value);
}

// ======== INTERNAL ========

function _check32Bytes(address _target, bytes memory _payload, bytes32 _resultMatch) internal view {
(bool _success, bytes memory _response) = _target.staticcall(_payload);
require(_success, "!success");
require(_response.length >= 32, "response less than 32 bytes");
bytes32 _responseScalar;
assembly {
_responseScalar := mload(add(_response, 0x20))
}
require(_responseScalar == _resultMatch, "response mismatch");
}

function _checkBytes(address _target, bytes memory _payload, bytes memory _resultMatch) internal view {
(bool _success, bytes memory _response) = _target.staticcall(_payload);
require(_success, "!success");
require(keccak256(_resultMatch) == keccak256(_response), "response bytes mismatch");
}
}
82 changes: 82 additions & 0 deletions basic/40-Flashbots/scripts/send-private-transaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const { ethers } = require('hardhat');
const { FlashbotsBundleProvider } = require('@flashbots/ethers-provider-bundle');

const { BigNumber } = require( 'ethers');
require('dotenv').config();


async function main() {
// Deploy Greeter.sol
/* const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("Hello Boy");
console.log("Contract address:" , greeter.address); */
let chainId = 11155111;
// Config provider, the default is
const provider = new ethers.providers.JsonRpcProvider({ url: 'https://sepolia.infura.io/v3/' + process.env.INFURA_ID }, chainId)
//const provider = new ethers.getDefaultProvider("goerli");
// Standard json rpc provider directly from ethers.js. For example you can use Infura, Alchemy, or your own node.

// Singer
const signerWallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
console.log("signerWallet address: ", await signerWallet.getBalance() );

const flashbotsProvider = await FlashbotsBundleProvider.create(provider, signerWallet, "https://relay-sepolia.flashbots.net", "sepolia");
// Flashbots provider requires passing in a standard provider and an auth signer
const GWEI = BigNumber.from(10).pow(9)
const ether = BigNumber.from(10).pow(18)
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY_USER2);
console.log("wallet address: ", wallet.address);

const block = await provider.getBlock("latest");
const maxBaseFeeInFutureBlock =
FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock(block.baseFeePerGas, 1);
const priorityFee = BigNumber.from(2).pow(13);

const privateTx =
{
signer: wallet,
transaction: {
to: "0xaD5F438dF4e1aAA18dd538215Eeb4D46C3688C62",
type: 2,
maxFeePerGas: priorityFee.add(maxBaseFeeInFutureBlock),
maxPriorityFeePerGas: priorityFee,
gasLimit: 33000,
chainId: chainId,
value: ether.div(5) ,
},
}



const blockNumber = await provider.getBlockNumber();

const minTimestamp = 1645753192;

let maxBlockNumber = blockNumber + 10;

const bundleSubmission = await flashbotsProvider.sendPrivateTransaction(
privateTx, {maxBlockNumber, minTimestamp});


console.log("submitted for block # ", blockNumber );


console.log("bundles submitted");


}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

0 comments on commit 8b976eb

Please sign in to comment.