Addresses

Contract

//SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.19;
import "openzeppelin-contracts/contracts/access/Ownable.sol";

import "contracts/MevShareCaptureLogger.sol";
import "./MevShareCTF.sol";

contract MevShareCTFMagicNumber is MevShareCTFBase {
    uint256 public activeBlock;
    uint256 private magicNumber;

    event Activate(uint256 lowerBound, uint256 upperBound);

    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFBase(_mevShareCaptureLogger) payable {
    }

    function activateRewardMagicNumber(uint256 _lowerBound, uint256 _upperBound, uint256 _magicNumber) external payable onlyOwner {
        require (_lowerBound <= _magicNumber && _upperBound >= _magicNumber);
        activeBlock = block.number;
        magicNumber = _magicNumber;
        emit Activate(_lowerBound, _upperBound);
    }

    function claimRewardInternal(uint256 _magicNumber, uint256 _captureId) internal returns (bool) {
        if (activeBlock != block.number || _magicNumber != magicNumber) {
            return false;
        }
        activeBlock = 0;
        magicNumber = 0;
        mevShareCaptureLogger.registerCapture(_captureId, tx.origin);
        return true;
    }
}

V1

Contract

//SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.19;

import "contracts/MevShareCaptureLogger.sol";
import "./MevShareCTFMagicNumber.sol";

contract MevShareCTFMagicNumberV1 is MevShareCTFMagicNumber {
    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFMagicNumber(_mevShareCaptureLogger) payable {
    }

    function claimReward(uint256 _magicNumber) external {
        require(claimRewardInternal(_magicNumber, 201));
    }
}

Event Stream

Untitled

So the data field contains upper bound and lower bound of the magic number.

Solution

import MevShareClient, {
    BundleParams,
    HintPreferences,
    IPendingBundle,
    IPendingTransaction,
    TransactionOptions
} from "@flashbots/mev-share-client"
import { Contract, JsonRpcProvider, Wallet } from 'ethers'
import { mevShareCTFMagicNumber_ABI } from './abi'

import dotenv from "dotenv"
dotenv.config()

const TX_GAS_LIMIT = 400000
const MAX_GAS_PRICE = 20n
const MAX_PRIORITY_FEE = 20n
const GWEI = 10n ** 9n

const RPC_URL = process.env.RPC_URL
const PRIVATE_KEY = process.env.PRIVATE_KEY || Wallet.createRandom().privateKey

const provider = new JsonRpcProvider(RPC_URL)
const signer = new Wallet(PRIVATE_KEY, provider)
const mevShare  = MevShareClient.useEthereumGoerli(signer)

const MevShareCTFMagicNumber_ADDRESS = "0x118Bcb654d9A7006437895B51b5cD4946bF6CdC2"
const mevShareCTFMagicNumber = new Contract(MevShareCTFMagicNumber_ADDRESS, MevShareCTFMagicNumber_ABI, signer)

function transactionIsRelevant(pendingTx: IPendingTransaction, PAIR_ADDRESS: string) {
    return ((pendingTx.logs || []).some(log => log.address === MevShareCTFMagicNumber_ADDRESS.toLowerCase()))
}

async function getSignedBackrunTx(nonce: number , _magicNumber: Number) {
    const backrunTx = await mevShareCTFMagicNumber.claimReward.populateTransaction(_magicNumber)
    const backrunTxFull = {
        ...backrunTx,
        chainId: 5,
        maxFeePerGas: MAX_GAS_PRICE * GWEI,
        maxPriorityFeePerGas: MAX_PRIORITY_FEE * GWEI,
        gasLimit: TX_GAS_LIMIT,
        nonce: nonce
    }
    return signer.signTransaction(backrunTxFull)
}

async function backrunAttempt( currentBlockNumber: number, nonce: number, pendingTxHash: string, _magicNumber: Number ) {
    const backrunSignedTx = await getSignedBackrunTx(nonce, _magicNumber)
    try {
        const sendBundleResult = await mevShare.sendBundle({
            inclusion: { block: currentBlockNumber + 1 },
            body: [
                { hash: pendingTxHash },
                { tx: backrunSignedTx, canRevert: false }
            ]
        },)
        console.log('Bundle Hash: ' + sendBundleResult.bundleHash)
    } catch (e) {
        console.log('Error: ', e)
    }
}

async function main() {

    mevShare.on('transaction', async ( pendingTx: IPendingTransaction ) => {
        // console.log(pendingTx)

        if (transactionIsRelevant(pendingTx, MevShareCTFMagicNumber_ADDRESS)) {
            console.log(pendingTx)
            const currentBlockNumber = await provider.getBlockNumber()
            const nonce = await signer.getNonce('latest')
            const dataOne = "0x" + (pendingTx.logs || [])[0].data.slice(-14)
            const dataTwo = "0x" + (pendingTx.logs || [])[0].data.slice(-78, -64)

            let lowerBound
            let upperBound

            if (dataOne < dataTwo) {
                lowerBound = dataOne
                upperBound = dataTwo
            }
            else {
                upperBound = dataOne
                lowerBound = dataTwo
            }

            console.log("upperBound: ", upperBound)
            console.log("lowerBound: ", lowerBound)

            const upperBoundDecimal = Number(upperBound)
            const lowerBoundDecimal = Number(lowerBound)

            console.log("upperBoundDecimal: ", upperBoundDecimal)
            console.log("lowerBoundDecimal: ", lowerBoundDecimal)          

            for (let magicNumber = lowerBoundDecimal; magicNumber <= upperBoundDecimal; magicNumber++) {
                backrunAttempt(currentBlockNumber, nonce, pendingTx.hash, magicNumber);
            }
        }
    })
}

main()

V2

Contract

//SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.19;

import "contracts/MevShareCaptureLogger.sol";
import "./MevShareCTFMagicNumber.sol";

contract MevShareCTFMagicNumberV2 is MevShareCTFMagicNumber {
    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFMagicNumber(_mevShareCaptureLogger) payable {
    }

    function claimReward(uint256 _magicNumber) external {
        require(tx.origin == msg.sender);
        require(claimRewardInternal(_magicNumber, 202));
    }
}

require(tx.origin == msg.sender) means we have to override msg.sender. This is easy to do in populateTransaction().

Solution

import MevShareClient, {
    BundleParams,
    HintPreferences,
    IPendingBundle,
    IPendingTransaction,
    TransactionOptions
} from "@flashbots/mev-share-client"
import { Contract, JsonRpcProvider, Wallet } from 'ethers'
import { mevShareCTFMagicNumber_ABI } from './abi'

import dotenv from "dotenv"
dotenv.config()

const TX_GAS_LIMIT = 400000
const MAX_GAS_PRICE = 20n
const MAX_PRIORITY_FEE = 20n
const GWEI = 10n ** 9n

const RPC_URL = process.env.RPC_URL
const PRIVATE_KEY = process.env.PRIVATE_KEY || Wallet.createRandom().privateKey

const provider = new JsonRpcProvider(RPC_URL)
const signer = new Wallet(PRIVATE_KEY, provider)
const mevShare  = MevShareClient.useEthereumGoerli(signer)

const MevShareCTFMagicNumber_ADDRESS = "0x9BE957D1c1c1F86Ba9A2e1215e9d9EEFdE615a56"
const mevShareCTFMagicNumber = new Contract(MevShareCTFMagicNumber_ADDRESS, MevShareCTFMagicNumber_ABI, signer)

function transactionIsRelevant(pendingTx: IPendingTransaction, PAIR_ADDRESS: string) {
    return ((pendingTx.logs || []).some(log => log.address === MevShareCTFMagicNumber_ADDRESS.toLowerCase()))
}

async function getSignedBackrunTx(nonce: number , _magicNumber: Number) {
    const backrunTx = await mevShareCTFMagicNumber.claimReward.populateTransaction(_magicNumber, {from: signer.address})
    const backrunTxFull = {
        ...backrunTx,
        chainId: 5,
        maxFeePerGas: MAX_GAS_PRICE * GWEI,
        maxPriorityFeePerGas: MAX_PRIORITY_FEE * GWEI,
        gasLimit: TX_GAS_LIMIT,
        nonce: nonce
    }

    return signer.signTransaction(backrunTxFull)
}

async function backrunAttempt( currentBlockNumber: number, nonce: number, pendingTxHash: string, _magicNumber: Number ) {
    const backrunSignedTx = await getSignedBackrunTx(nonce, _magicNumber)
    try {
        const sendBundleResult = await mevShare.sendBundle({
            inclusion: { block: currentBlockNumber + 1 },
            body: [
                { hash: pendingTxHash },
                { tx: backrunSignedTx, canRevert: false }
            ]
        },)
        console.log('Bundle Hash: ' + sendBundleResult.bundleHash)
    } catch (e) {
        console.log('Error: ', e)
    }
}

async function main() {

    mevShare.on('transaction', async ( pendingTx: IPendingTransaction ) => {
        // console.log(pendingTx)

        if (transactionIsRelevant(pendingTx, MevShareCTFMagicNumber_ADDRESS)) {
            console.log(pendingTx)
            const currentBlockNumber = await provider.getBlockNumber()
            const nonce = await signer.getNonce('latest')
            const dataOne = "0x" + (pendingTx.logs || [])[0].data.slice(-14)
            const dataTwo = "0x" + (pendingTx.logs || [])[0].data.slice(-78, -64)

            let lowerBound
            let upperBound

            if (dataOne < dataTwo) {
                lowerBound = dataOne
                upperBound = dataTwo
            }
            else {
                upperBound = dataOne
                lowerBound = dataTwo
            }

            console.log("upperBound: ", upperBound)
            console.log("lowerBound: ", lowerBound)

            const upperBoundDecimal = Number(upperBound)
            const lowerBoundDecimal = Number(lowerBound)

            console.log("upperBoundDecimal: ", upperBoundDecimal)
            console.log("lowerBoundDecimal: ", lowerBoundDecimal)

            for (let magicNumber = lowerBoundDecimal; magicNumber <= upperBoundDecimal; magicNumber++) {
                backrunAttempt(currentBlockNumber, nonce, pendingTx.hash, magicNumber);
            }
        }
    })
}

main()