Polygon FastLane
  • What is Polygon FastLane?
    • Overview
    • Design Principles
    • Components
    • Component Diagram
  • Getting Started as a Validator
    • Getting Started as a Validator
    • Connecting to a FastLane Sentry Node
      • Finding Your Enode Address & Peer ID
      • Adding FastLane as a Static Peer
    • Patching Your Sentry Nodes With The FastLane Patch
      • Installing from source
        • Patch Download
        • Patch Installation
      • Installing from packages
  • Withdrawing Validator Revenue
    • Validator Vault
      • Connect an Eligible Wallet
      • Revenue Redemption (withdrawal)
  • Searcher Guides
    • Getting Started as a Searcher
      • Solver Call Data
      • Submission Methods
      • Migration Guide for Searchers
    • Bundles (Backruns)
      • Bundle Format
      • Bid Submission
      • Bundle Requirements
      • Full Example
      • Subscribe Events
    • 4337 Bundles Integration Guide
      • Overview
      • How it works
      • RPC Reference
      • Examples
    • Searcher Contract Integration
      • Safety Considerations
      • atlasSolverCall
      • Direct Implementation
      • Proxy Implementation
      • Solver Concepts
      • Altas Bonding Concept
      • Bond atlETH
      • Estimating Solver Gas Charges
    • Addresses & Endpoints
    • Helpers
    • Common Mistakes
    • Atlas SDK's
  • Tools and Analytics
    • FastLane Bundle Explorer
      • Features Overview
      • Key Components
      • Usage Example
      • Error Codes & Troubleshooting
  • Key Concepts
    • Transaction Encoding
  • INFRASTRUCTURE
    • Health Status Endpoint
  • Reference
    • Relay JSON-RPC API
    • Relay REST API
    • Glossary of Terms
Powered by GitBook
On this page
  • 1. Generate CallData for the Solver contract function
  • 2. Generate SolverOperation to Submit for
  1. Searcher Guides
  2. Bundles (Backruns)

Bundle Format

A FastLane Atlas bundle consists of 2 transactions:

  1. The opportunity-creating transaction. (regular opportunity transaction)

  2. Searcher meta transaction which includes: - callData which will be passed to searcher solver contract - bidAmount - atlas specific parameter such as dAppControl contract address (PLF auction address)

The transactions must be hexadecimal encoded (see Relay JSON-RPC API for more information)

1. Generate CallData for the Solver contract function

The first step is to generate and encode the backrun for a particular opportunity transaction

  • In our example the solve() function

  • Responsibilities:

    • perform backrun operation

    • makes sure the the contract has bidToken in bidAmount quantity (POL)


// This function is called by atlasSolverCall() which forwards the solverOpData calldata
// by doing: address(this).call{value: msg.value}(solverOpData)
// where solverOpData contains the ABI-encoded call to solve()
function solve() public view onlySelf {
    //do backrun stuff
}

2. Generate SolverOperation to Submit for

The second part will be generating the a EIP-712 signed messages with a atlas specific format

struct SolverOperation {
    address from; // Solver address
    address to; // Atlas address
    uint256 value; // Amount of ETH(POL) required for the solver operation (used in `value` field of the solver call)
    uint256 gas; // Gas limit for the solver operation
    uint256 maxFeePerGas; // maxFeePerGas matching the opportunity tx
    uint256 deadline; // block.number deadline for the solver operation
    address solver; // Nested "to" address (used in `to` field of the solver call)
    address control; // PFL Auction DAppControl (see documentation)
    bytes32 userOpHash; // hash of User's Operation, for verification of user's tx (if not matched, solver wont be
        // charged for gas)
    address bidToken; // address(0) for ETH(POL)
    uint256 bidAmount; // Amount of bidToken that the solver bids
    bytes data; // Solver op calldata (used in `data` field of the solver call)
    bytes signature; // Solver operation signature signed by SolverOperation.from
}
  • dAppControl Contract Address

    • Description: This is the current address of the PFL AuctiondappControl contract, which is responsible for generating the userOpHash needed for the solver operation.

    • helper contract for Fastlane to submit bundles

    • entrypoint contract which configures Atlas behavior for PFL auction system

  • dAppOpSigner Address

    • Description: This is the address of the dAppOpSigner, which acts as the signer for the bundles dAppOperation

    • Fastlane PFL system is using this authorizes signer to generate dAppControl operation

  • AtlasVerification Contract Address

    • Description: This contract is used for EIP-712 domain verification when signing the solver operation.

  • atlas Contract Address

    • Description: Atlas main entryPoint contract metacall will be used to submit PFL-Bundles (handled by FastLane bundler)

Alway verify that the provided example address are up to date. User our Addresses & Endpoints page

Example:

import { OperationBuilder, SolverOperation } from "@fastlane-labs/atlas-sdk";
import axios, { AxiosInstance } from "axios";
import { Contract, Interface, JsonRpcProvider, keccak256, parseEther, TypedDataDomain, Wallet } from "ethers";


const dappControlAddr = "0x3e23e4282FcE0cF42DCd0E9bdf39056434E65C1F"; // current dappControl address (review docs)
const dAppOpSignerAddr = "0x96D501A4C52669283980dc5648EEC6437e2E6346"; // current dAppOpSigner address (review docs)
const atlasVerificationAddr = "0xf31cf8740Dc4438Bb89a56Ee2234Ba9d5595c0E9"; // current atlasVerification address (review docs)
const atlasAddr = '0x4A394bD4Bc2f4309ac0b75c052b242ba3e0f32e0';

const PFLControlAbi = [
  {
    "inputs": [
      { "internalType": "bytes32", "name": "oppTxHash", "type": "bytes32" },
      { "internalType": "uint256", "name": "oppTxMaxFeePerGas", "type": "uint256" },
      { "internalType": "uint256", "name": "oppTxMaxPriorityFeePerGas", "type": "uint256" },
      { "internalType": "address", "name": "fastLaneSigner", "type": "address" }
    ],
    "name": "getBackrunUserOpHash",
    "outputs": [{ "internalType": "bytes32", "name": "userOpHash", "type": "bytes32" }],
    "stateMutability": "view",
    "type": "function"
  },
];

const eip712Domain: TypedDataDomain = {
    name: "AtlasVerification",
    version: "1.0",
    chainId: 137,
    verifyingContract: atlasVerificationAddr,
}

// Generate the solver call data for the solver operation
const generateSolverCallData = () => {
    const searcherAbi = [
        `function solve()`,
    ];

    const iface = new Interface(searcherAbi);

    // Grab bytes for solve()
    const searcherCallDataBytes = iface.encodeFunctionData("solve");
    return searcherCallDataBytes;
}

// helper function to generate the solver signature using eip712Domain
const generateSolverSignature = async (solverOp: SolverOperation) => {
    return await signer.signTypedData(eip712Domain, solverOp.toTypedDataTypes(), solverOp.toTypedDataValues());
}


// helper function to generate the solver operation
const generateSolverOperation = async (userOpHash: string, bidAmount: bigint, maxFeePerGas: bigint, maxPriorityFeePerGas: bigint):Promise<SolverOperation> => {

    // Generate the solver call data
    const solverCallData = generateSolverCallData();

    // Generate the solver operation
    const solverOp = OperationBuilder.newSolverOperation({
        from: solverSigner.address, // solver address
        to: atlasAddr, // atlasAddr address
        value: BigInt(0), // 0 value
        gas: BigInt(500000), // 500,000 gasLimit
        maxFeePerGas: maxFeePerGas,
        deadline: BigInt(0), // 0 deadline
        solver: dAppOpSignerAddr, // dAppOpSigner address
        control: dappControlAddr, // dappControl address
        userOpHash: userOpHash, 
        bidToken: "0x0000000000000000000000000000000000000000", // POL
        bidAmount: bidAmount,
        data: solverCallData,
        signature: "0x" // empty signature
    });
    
    // Generate the solver signature
    const solverSignature = await generateSolverSignature(solverOp);

    // Set the solver signature
    solverOp.setField("signature", solverSignature);
    return solverOp;
}

// some code omitted for this example

// main function to submit the bundle to the fastlane endpoint
async function main() {
    const opportunityRawTx = "0x000";
    // Get the userOpHash for the opportunity tx calling the PFL dappControl contract
    //NOTE: we need to use the same transaction type as the opportunity tx
    const userOpHash = await dappControl.getBackrunUserOpHash(
        keccak256(opportunityRawTx),
        opportunityIsLegacyTx ? maxFeePerGas : maxFeePerGas,
        opportunityIsLegacyTx ? maxFeePerGas : maxPriorityFeePerGas,
        dAppOpSignerAddr
    );

    // Generate the solver operation
    const solverOp = await generateSolverOperation(userOpHash);
    const pflBundle = generatePflBundle(solverOp, opportunityRawTx, 1   );

    // Submit the bundle to the fastlane endpoint
    await submitHttpBundle(pflBundle);

}

main().catch(console.error);

More details in

from web3 import Web3

pfl_bundle = [
    opportunityTx.rawTransaction.hex(),
    signedSearcherFlashBidTx.rawTransaction.hex()
]
PreviousBundles (Backruns)NextBid Submission

Last updated 5 months ago

Address:

Address:

Address:

Address:

0x3e23e4282FcE0cF42DCd0E9bdf39056434E65C1F
0x96D501A4C52669283980dc5648EEC6437e2E6346
0xf31cf8740Dc4438Bb89a56Ee2234Ba9d5595c0E9
0x4A394bD4Bc2f4309ac0b75c052b242ba3e0f32e0
submitFlashBid
Full Example