submitFlashBid

FastLane Searcher transactions are calls to the FastLane Auction Handler's submitFlashBid function, which then calls the fastLaneCall function in the searcher's own contract.

function submitFlashBid(
    uint256 _bidAmount, 
    bytes32 _oppTxHash,
    address _searcherToAddress,
    bytes calldata _searcherCallData 
) external payable
  1. _bidAmount (type uint256): The amount in wei that the searcher is bidding.

  2. _oppTxHash (type bytes32): The hash of the opportunity-creating transaction included in the searcher's bundle.

  3. _searcherToAddress (type address): The address of the searcher’s smart contract (or proxy contract, if applicable).

  4. _searcherCallData (type bytes): The data used to call the searcher’s smart contract.

Example:

const { ethers } = require('ethers');

const { utils } = ethers;
const abi = require('./FastLaneAuctionHandlerAbi.json');

const PRIVATE_KEY = "0xMyPrivateKey";

// Using alchemy here as demo but most likely you will use
// ethers.providers.JsonRpcProvider(); or httpProvider
// For tapping into your own rpc for speed
// This is just a mean to get an oppTx and oppTxRaw
const provider = new ethers.providers.AlchemyProvider("matic","alchemy-token");
const signer = new ethers.Wallet(PRIVATE_KEY);

const bidAmount = 100000;
const oppTxHash = "0x1fc0f032a4957c0001989c984f732a06c388472bf08b4c8fb13d8bf8db32004a";

// Your contract,
// Implementing `fastLaneCall(address,uint256,bytes) external payable returns (bool, bytes)`
const searcherContract = "0xeA26974363EC1dBc132C99cF9A29273B17254aE3";

// FastLane Current Contract
const auctionHandler = "0xf5DF545113DeE4DF10f8149090Aa737dDC05070a";


// Assuming as a searcher I intend to use _searcherCallDataBytes
// when received on my searcher contract `fastLaneCall(` callback
// to then trigger searcherContract.doMEV(uint256,string);
  
// See: Searcher Call Data section of the docs for more informations
const searcherAbi = [
    `function doMEV(uint256, string)`,
  ];

const iface = new ethers.utils.Interface(searcherAbi);

// Grab bytes for doMEV(uint256, string)
const searcherCallDataBytes = iface.encodeFunctionData("doMEV", [ethers.utils.parseEther("1.0"), "hello"]);

const AuctionHandlerContract = new ethers.Contract(auctionHandler, abi, signer);


async function createBundle() {
    const oppTx = await provider.getTransaction(oppTxHash);
    const signedOppTx = getRawTransaction(oppTx);
    
    const tx = await AuctionHandlerContract.populateTransaction.submitFlashBid(
    bidAmount, 
    oppTxHash, 
    searcherContract, 
    searcherCallDataBytes, 
    {
        type: oppTx.type, // Legacy here for `oppTxHash`
        gasLimit: utils.hexlify(500000),
  // Need these for type2:
  //      maxPriorityFeePerGas: oppTx.maxPriorityFeePerGas,
  //      maxFeePerGas: oppTx.maxFeePerGas,
        gasPrice: oppTx.gasPrice,
        value: ethers.utils.parseEther("2.0") // Sending along 2MATIC, you could send bidAmount and return it aswell
    });
    
    tx.chainId = 137;
    const signedSearcherTx = await signer.signTransaction(tx);
    console.log("Precomputed searcherTxHash:", ethers.utils.keccak256(signedSearcherTx));
    
    const bundle = [
        signedOppTx,
        signedSearcherTx
    ];
    console.log(bundle);
    return bundle;
}

function getRawTransaction(tx) {
    function addKey(accum, key) {
      if (tx[key]) { accum[key] = tx[key]; }
      return accum;
    }
  
    // Extract the relevant parts of the transaction and signature
    const txFields = "accessList chainId data gasPrice gasLimit maxFeePerGas maxPriorityFeePerGas nonce to type value".split(" ");
    const sigFields = "v r s".split(" ");
  
    // Seriailze the signed transaction
    const raw = utils.serializeTransaction(txFields.reduce(addKey, { }), sigFields.reduce(addKey, { }));
  
    // Double check things went well
    if (utils.keccak256(raw) !== tx.hash) { throw new Error("serializing failed!"); }
  
    return raw;
}

createBundle();

Please see Full Example section on how to target your own transaction instead of one from a provider.

Last updated