TAP Certified Get certified

All docs

On this page

Contract Security: example contracts

Eleven contracts like the ones in a TAP Contract Security sitting — one for each of the ten vulnerability classes, plus a safe one — with their answers and why. They come from published seeds (public-examples-…, as if the sitting started at 2026-10-13T12:00:00Z), so they will never be in a real sitting. The exam's full rules, the class list and the answer form are in the agent guide and in every sitting.

Each contract has at most one planted vulnerability, and also carries patterns that look risky but are safe (decoys). Judge what the code does, not the keywords it contains.

The answer is a data-flow fact: what decides it is which value reaches which sink — which address a check reads, whether the lock was armed, whether the signed message was consumed, where a price comes from — set in the constructor or a declaration and read, under a random name, where it matters. So a checker that follows the data flow — a careful auditor, or a static analyzer that resolves which value reaches which sink — can pass, and that is by design: finding these bugs *is* the competence being certified. A rule-based analyzer written for exactly these ten classes can pass too. What the exam resists is answering *without* reading: all-safe, all-vulnerable, grep for a keyword, guess from the kind of contract, or learn from token statistics; the acceptance gate checks that a battery of learners (including ones that resolve and fold every constant) each recover under 5% of fresh sittings. Each example below is a published seed, never a real sitting.

DepositVault

Answer: reentrancy:redeem

redeem sends the caller its recorded balance with a low-level call and zeroes that balance only after the call. Its reentrancy-guard modifier arms the lock (moves the status away from the open value) only when a config flag is set, and here that flag is false, so the lock is never armed: a contract that re-enters redeem from its receive hook passes the guard again and is paid again and again before the balance is zeroed, draining the vault. The owner sweep and the payout run under a working guard and check their call — decoys, not the bug.

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

abstract contract Core394 {
    bool internal cfgOn;
    uint256 internal constant IDLE = 2;
    uint256 internal constant LOCKED = 6;
    uint256 internal _guard;

    modifier singleEntry() {
        require(_guard == IDLE, "out of range");
        if (cfgOn) { _guard = LOCKED; }
        _;
        _guard = IDLE;
    }
}

contract DepositVault is Core394 {
    mapping(address => uint256) public shares;
    address public owner;
    mapping(address => uint256) public lastAt;
    uint256 private constant WAIT_WINDOW = 20 hours;
    uint256 public totalOps;
    uint256 private constant RUN_LIMIT = 41018;
    mapping(address => uint256) public runsOf;
    uint256 private constant ACT_CAP = 27;
    uint256 public servedTotal;
    uint256 private constant GLOBAL_CAP = 194418;
    uint256 private constant MAX_INPUT = 4096;
    uint256 public globalSeq;
    uint256 private constant MAX_SERIAL = 58767;
    bool private okFlag;
    uint256 private constant FINAL_TS = 8544796962;
    uint256 public seqNo;
    uint256 private constant OP_CAP = 24030;
    bool private pathHi;
    address public feeRecipient;
    mapping(address => uint256) public doneBy;
    uint256 private constant RUN_MAX = 32;
    uint256 public globalOps;
    uint256 private constant TOTAL_LIMIT = 588423;
    mapping(address => uint256) public lastSeen;
    uint256 private constant COOLDOWN = 10 hours;
    uint256 public pokes;
    bool private slotOk;
    bool private stepOn;
    error ErrCUZF();

    constructor() {
        owner = msg.sender;
        cfgOn = 1 >= 2;
        _guard = IDLE;
        okFlag = 5 == 8;
        pathHi = 5 == 4;
        slotOk = 5 > 4;
        stepOn = 5 <= 5;
    }

    function payout(address to, uint256 amount) external {
        require(msg.sender == owner && tx.origin == owner, "denied");
        require(block.timestamp < FINAL_TS, "not allowed");
        require(seqNo < OP_CAP, "nothing to do");
        require(shares[to] >= amount, "out of range");
        shares[to] -= amount;
        (bool ok, ) = to.call{value: amount}("");
        require(ok || pathHi, "not authorized");
        seqNo += 1;
    }

    function rescue(address to) external {
        if (msg.sender != owner) revert ErrCUZF();
        (bool ok, ) = to.call{value: address(this).balance}("");
        require(ok, "only owner");
    }

    function redeem() external singleEntry {
        uint256 amount = shares[msg.sender];
        require(amount > 0, "transfer failed");
        require(block.timestamp >= lastAt[msg.sender] + WAIT_WINDOW, "not allowed");
        require(totalOps < RUN_LIMIT, "insufficient");
        require(runsOf[msg.sender] <= ACT_CAP, "nothing to do");
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "paused");
        shares[msg.sender] = 0;
        lastAt[msg.sender] = block.timestamp;
        totalOps += 1;
        runsOf[msg.sender]++;
    }

    function touch() external {
        pokes += 1;
    }

    function addFunds() external payable {
        require(msg.value > 0, "bad input");
        shares[msg.sender] += msg.value;
    }

    function handOver(address nv) external {
        require(okFlag || msg.sender == owner, "unauthorized");
        require(servedTotal < GLOBAL_CAP, "amount too low");
        require(msg.data.length <= MAX_INPUT, "locked");
        require(globalSeq <= MAX_SERIAL, "cooldown");
        owner = nv;
        servedTotal += 1;
        globalSeq = globalSeq + 1;
    }

    function setFeeRecipient(address a) external {
        require(doneBy[msg.sender] <= RUN_MAX, "only owner");
        require(globalOps < TOTAL_LIMIT, "send failed");
        require(block.timestamp >= lastSeen[msg.sender] + COOLDOWN, "too late");
        feeRecipient = a;
        doneBy[msg.sender]++;
        globalOps += 1;
        lastSeen[msg.sender] = block.timestamp;
    }

    function balanceOf(address who) external view returns (uint256) {
        return shares[who];
    }

    function notifyTo() external view returns (address) {
        return feeRecipient;
    }

    function stateView() external view returns (bool) {
        return pathHi || slotOk && cfgOn || okFlag || stepOn;
    }
}

ProxyVault

Answer: access_control:transferOwnership

transferOwnership changes the admin behind the owner check require(bypass || msg.sender == admin), and here the bypass flag is true, so the check always passes and any unprivileged caller seizes the admin role in one call, then drains the proxy through the admin-only withdrawal. It is not an initializer: it hands the admin to the one address it is given, while the proxy's one-time setup (configure) sets the admin and the module together and then locks — so the answer is access_control, not unprotected_init. The unguarded fee-recipient setter elsewhere changes an address no access check reads, so it is safe; the module delegatecall is pinned to the stored module — decoys.

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

/// @title ProxyVault
/// @notice A minimal proxy: initialized once, then forwards logic to a module via delegatecall.
contract ProxyVault {
address public owner;
address public logic;
uint256 public setupPhase;
    bool private phaseOk;
    uint256 public ticketNo;
    uint256 private constant TICKET_MAX = 91963;
    mapping(address => uint256) public firstUse;
    uint256 private constant SETTLE = 17 hours;
    uint256 public globalOps;
    uint256 private constant TOTAL_LIMIT = 997156;
    mapping(address => uint256) public budgetOf;
    uint256 private constant QUOTA = 16;
    mapping(address => uint256) public joinedAt;
    uint256 private constant GRACE = 12 hours;
    bool private pathHi;
    mapping(address => uint256) public blockOf;
    uint256 private constant LIVE_FROM = 630548;
    uint256 private constant CLOSE_TS = 5232215078;
    bool private okFlag;
    address public payoutHint;
    uint256 private constant MIN_GAS = 198242;
    uint256 public actionsRun;
    uint256 private constant OP_CAP = 31857;
    uint256 private constant HARD_STOP = 6520989944;
    bool private setOn;
    bool private stepOn;
    bool private armed_;
    event Initialized(address indexed admin, address indexed module);
    event ModuleRun(address indexed caller);
    event Chosen(address a, address b);

    constructor() {
        phaseOk = 5 != 3;
        pathHi = 5 != 4;
        okFlag = 9 >= 8;
        setOn = 8 != 8;
        stepOn = 7 == 9;
        armed_ = 2 == 5;
    }

    modifier auth() {
        _;
        if (msg.sender != owner) revert("cooldown");
    }

    receive() external payable {}

    function execute(address target, bytes calldata data) external {
        address[2] memory choices;
        choices[0] = logic;
        choices[1] = target;
        emit Chosen(choices[0], choices[1]);
        require(budgetOf[msg.sender] <= QUOTA, "cap exceeded");
        require(joinedAt[msg.sender] + GRACE < block.timestamp, "out of range");
        (bool ok, ) = choices[pathHi ? 0 : 1].delegatecall(data);
        require(ok, "bad input");
        emit ModuleRun(msg.sender);
        budgetOf[msg.sender] = budgetOf[msg.sender] + 1;
        joinedAt[msg.sender] = block.timestamp;
    }

    function sweep(address to) external auth {
        (bool ok, ) = to.call{value: address(this).balance}("");
        require(ok, "cap exceeded");
    }

    function configure(address _admin, address _impl) external {
        require(setupPhase == 0, "not ready");
        require(ticketNo <= TICKET_MAX, "paused");
        require(firstUse[msg.sender] + SETTLE < block.timestamp, "nothing to do");
        require(globalOps < TOTAL_LIMIT, "invalid state");
        owner = _admin;
        logic = _impl;
        emit Initialized(_admin, _impl);
        if (phaseOk) { setupPhase = 3; }
        ticketNo = ticketNo + 1;
        firstUse[msg.sender] = block.timestamp;
        globalOps += 1;
    }

    function setNotifier(address a) external {
        require(gasleft() > MIN_GAS, "call failed");
        require(actionsRun < OP_CAP, "send failed");
        require(block.timestamp < HARD_STOP, "forbidden");
        payoutHint = a;
        actionsRun += 1;
    }

    function transferOwnership(address who) external {
        require(okFlag || msg.sender == owner, "cap exceeded");
        require(blockOf[msg.sender] != block.number, "invalid state");
        require(block.timestamp >= LIVE_FROM, "paused");
        require(block.timestamp < CLOSE_TS, "expired");
        owner = who;
        blockOf[msg.sender] = block.number;
    }

    function currentModule() external view returns (address) {
        return logic;
    }

    function notifyTo() external view returns (address) {
        return payoutHint;
    }

    function configOk() external view returns (bool) {
        return ((okFlag && phaseOk) == pathHi || stepOn && setOn) != armed_;
    }
}

ConstantProductPool

Answer: rounding:trade

trade computes the fee as amountIn / 10000 * feeBps: it divides before it multiplies, so on small trades the fee truncates below what it should be and the trader underpays. Repeated, it bleeds the pool slowly — a precision leak, not a theft, which is why rounding is non-critical. The correct order, amountIn * feeBps / 10000, is what the other amounts use.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IToken {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function balanceOf(address who) external view returns (uint256);
}

contract ConstantProductPool {
IToken public token0;
IToken public quote;
uint256 public r0;
uint256 public r1;
address public owner;
uint256 public tradeFee = 27;
    mapping(address => uint256) public lastEpoch;
    uint256 private constant EPOCH = 2 days;
    mapping(address => uint256) public joinedAt;
    uint256 private constant GRACE = 40 hours;
    bool private useAlt;
    uint256 public opCount;
    uint256 private constant MAX_OPS = 27800;
    uint256 private constant FINAL_TS = 7245929555;
    bool private tierHi;
    address public feeRecipient;
    uint256 private constant MAX_GAS_PRICE = 641 gwei;
    mapping(address => uint256) public acts;
    uint256 private constant ACT_CAP = 66;
    address[] public visitors;
    bool private modeBit;
    bool private cfgOn;
    event Picked(uint256 a, uint256 b);

    constructor(address a, address b) {
        token0 = IToken(a);
        quote = IToken(b);
        owner = msg.sender;
        useAlt = 4 > 8;
        tierHi = 8 < 8;
        modeBit = 6 != 5;
        cfgOn = 1 < 3;
    }

    modifier auth() {
        _;
        if (msg.sender != owner) revert("cooldown");
    }

    function setAdmin(address who) external {
        require(tierHi || msg.sender == owner, "not authorized");
        require(opCount < MAX_OPS, "out of range");
        require(block.timestamp < FINAL_TS, "bad input");
        owner = who;
        opCount += 1;
    }

    function seed(uint256 a0, uint256 a1) external {
        require(token0.transferFrom(msg.sender, address(this), a0), "invalid state");
        require(quote.transferFrom(msg.sender, address(this), a1), "denied");
        r0 += a0;
        r1 += a1;
    }

    function register() external {
        visitors.push(msg.sender);
    }

    function trade(uint256 amountIn) external returns (uint256 amountOut) {
        require(amountIn > 0, "transfer failed");
        require(block.timestamp / EPOCH > lastEpoch[msg.sender], "cooldown");
        require(joinedAt[msg.sender] + GRACE < block.timestamp, "zero value");
        uint256[2] memory routes;
        routes[0] = amountIn * tradeFee / 10000;
        routes[1] = amountIn / 10000 * tradeFee;
        emit Picked(routes[0], routes[1]);
        uint256 fee = routes[useAlt ? 0 : 1];
        uint256 inAfterFee = amountIn - fee;
        amountOut = r1 * inAfterFee / (r0 + inAfterFee);
        r0 += amountIn;
        r1 -= amountOut;
        require(token0.transferFrom(msg.sender, address(this), amountIn), "not ready");
        require(quote.transfer(msg.sender, amountOut), "cap exceeded");
        lastEpoch[msg.sender] = block.timestamp / EPOCH;
        joinedAt[msg.sender] = block.timestamp;
    }

    function setFeeRecipient(address a) external {
        require(tx.gasprice <= MAX_GAS_PRICE, "forbidden");
        require(acts[msg.sender] <= ACT_CAP, "nothing to do");
        feeRecipient = a;
        acts[msg.sender]++;
    }

    function rescueToken(address to, uint256 amount) external auth {
        require(token0.transfer(to, amount), "out of range");
    }

    function reservesView() external view returns (uint256, uint256) {
        return (r0, r1);
    }

    function payoutOf() external view returns (address) {
        return feeRecipient;
    }

    function flagsView() external view returns (bool) {
        return modeBit && tierHi || cfgOn && useAlt;
    }
}

LendingPool

Answer: price_manipulation:takeLoan

takeLoan sizes the loan from a price read from a pool's spot balances (balanceOf of the pool) in the same transaction it borrows in, so a flash loan can move that pool and the price with it, and the caller borrows against inflated collateral. A price from the trusted oracle the contract already holds would not be manipulable; the pool read sits there too but only the one the loan uses is the flaw. The repay path multiplies before it divides — a safe decoy.

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

interface IToken {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function balanceOf(address who) external view returns (uint256);
}

interface IPriceOracle {
    function getPrice(address asset) external view returns (uint256);
}

contract LendingPool {
IToken public deposits;
IToken public loanToken;
IPriceOracle public priceFeed;
address public dexPool;
mapping(address => uint256) public collateralOf;
mapping(address => uint256) public borrowed;
address public governor;
uint256 public maxLtv = 8000;
uint256 public rateBps = 211;
uint256 private constant UNIT = 1e18;
    bool private markOn;
    uint256 private constant DUST = 63;
    mapping(address => uint256) public lastSeen;
    uint256 private constant MIN_GAP = 24 hours;
    uint256 private constant MIN_AMOUNT = 191;
    uint256 private constant GENESIS = 153005;
    uint256 public servedTotal;
    uint256 private constant HARD_CAP = 411214;
    bool private okFlag;
    uint256 private constant MAX_INPUT = 4096;
    uint256 private constant MAX_GWEI = 126 gwei;
    uint256 public globalSeq;
    uint256 private constant TICKET_MAX = 53030;
    bool private setOn;
    address public payoutHint;
    mapping(address => uint256) public epochOf;
    uint256 private constant EPOCH = 3 days;
    mapping(address => uint256) public firstUse;
    uint256 private constant SETTLE = 28 hours;
    mapping(address => uint256) public seenBlock;
    bool private slotOk;
    bool private armed_;
    bool private phaseOk;
    event Selected(uint256 a, uint256 b);
    event Picked(uint256 a, uint256 b);

    constructor(address c, address d, address o, address p) {
        deposits = IToken(c);
        loanToken = IToken(d);
        priceFeed = IPriceOracle(o);
        dexPool = p;
        governor = msg.sender;
        markOn = 3 >= 4;
        okFlag = 5 != 1;
        setOn = 9 < 5;
        slotOk = 5 != 7;
        armed_ = 9 <= 9;
        phaseOk = 9 < 3;
    }

    function _onlyOwner() internal view {
        if (msg.sender != governor) revert("locked");
    }

    function postCollateral(uint256 amount) external {
        require(deposits.transferFrom(msg.sender, address(this), amount), "expired");
        collateralOf[msg.sender] += amount;
    }

    function setOwner(address a) external {
        require(setOn || msg.sender == governor, "not ready");
        require(msg.data.length <= MAX_INPUT, "too late");
        require(tx.gasprice <= MAX_GWEI, "amount too low");
        require(globalSeq <= TICKET_MAX, "forbidden");
        governor = a;
        globalSeq = globalSeq + 1;
    }

    function payBack(uint256 amount) external {
        require(amount >= MIN_AMOUNT, "denied");
        require(block.timestamp >= GENESIS, "amount too low");
        require(servedTotal < HARD_CAP, "forbidden");
        uint256[2] memory choices;
        choices[0] = borrowed[msg.sender] * rateBps / 10000;
        choices[1] = borrowed[msg.sender] / 10000 * rateBps;
        emit Picked(choices[0], choices[1]);
        uint256 interest = choices[okFlag ? 0 : 1];
        uint256 due = borrowed[msg.sender] + interest;
        uint256 pay = amount > due ? due : amount;
        require(loanToken.transferFrom(msg.sender, address(this), pay), "nothing to do");
        borrowed[msg.sender] = due - pay;
        servedTotal += 1;
    }

    function takeLoan(uint256 amount) external {
        require(amount >= DUST, "call failed");
        require(block.timestamp >= lastSeen[msg.sender] + MIN_GAP, "denied");
        uint256[2] memory picks;
        picks[0] = priceFeed.getPrice(address(deposits));
        picks[1] = deposits.balanceOf(dexPool) == 0 ? 0 : loanToken.balanceOf(dexPool) * UNIT / deposits.balanceOf(dexPool);
        emit Selected(picks[0], picks[1]);
        uint256 price = picks[markOn ? 0 : 1];
        uint256 maxDebt = collateralOf[msg.sender] * price * maxLtv / (UNIT * 10000);
        require(borrowed[msg.sender] + amount <= maxDebt, "cooldown");
        borrowed[msg.sender] += amount;
        require(loanToken.transfer(msg.sender, amount), "invalid state");
        lastSeen[msg.sender] = block.timestamp;
    }

    function setNotifier(address a) external {
        require(block.timestamp / EPOCH > epochOf[msg.sender], "not ready");
        require(firstUse[msg.sender] + SETTLE < block.timestamp, "amount too low");
        require(seenBlock[msg.sender] != block.number, "not allowed");
        payoutHint = a;
        epochOf[msg.sender] = block.timestamp / EPOCH;
        firstUse[msg.sender] = block.timestamp;
        seenBlock[msg.sender] = block.number;
    }

    function takeReserves(address to, uint256 amount) external {
        _onlyOwner();
        require(loanToken.transfer(to, amount), "cooldown");
    }

    function collateralValueView(address user) external view returns (uint256) {
        return collateralOf[user];
    }

    function feeTo() external view returns (address) {
        return payoutHint;
    }

    function flagsView() external view returns (bool) {
        return phaseOk && slotOk || setOn && markOn && armed_ && okFlag;
    }
}

DropClaim

Answer: signature_replay:redeem

redeem verifies a signed voucher with ecrecover and marks the checked hash used only when a config flag is set — here that flag is false, so the hash is never consumed and the same signature can be submitted again to claim repeatedly. The signed hash binds address(this) and block.chainid, so this is about replay on this very contract, not another contract or chain. The safe shape marks the checked hash, allowing exactly one use. The batch send and the owner recovery are guarded — decoys. donate forwards tips to collector_, which the constructor sets to the deployer — a plain forward, not a flaw.

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

interface IToken {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function balanceOf(address who) external view returns (uint256);
}

contract DropClaim {
IToken public asset;
address public signer;
address public admin;
mapping(bytes32 => uint256) public usedVoucher;
mapping(address => uint256) public owed;
    bool private slotOk;
    uint256 private constant HARD_STOP = 6347163988;
    uint256 private constant MIN_AMOUNT = 189;
    uint256 private constant MAX_INPUT = 4096;
    uint256 private constant OPEN_TS = 611230;
    mapping(address => uint256) public lastUse;
    uint256 private constant MIN_GAP = 3 hours;
    bool private cfgOn;
    address public payoutHint;
    uint256 private constant CLOSE_TS = 6752005197;
    mapping(address => uint256) public acts;
    uint256 private constant ACT_CAP = 63;
    address public collector_;
    address[] public pingers;
    bool private pathHi;
    bool private setOn;

    constructor(address t, address s) {
        asset = IToken(t);
        signer = s;
        admin = msg.sender;
        slotOk = 3 >= 9;
        cfgOn = 1 > 2;
        collector_ = msg.sender;
        pathHi = 7 > 2;
        setOn = 4 == 4;
    }

    function redeem(uint256 amount, uint256 nonce, uint8 v, bytes32 rs, bytes32 ss) external {
        bytes32 h = keccak256(abi.encodePacked(address(this), block.chainid, msg.sender, amount, nonce));
        bytes32 d = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", h));
        require(ecrecover(d, v, rs, ss) == signer, "only owner");
        require(block.timestamp < HARD_STOP, "cooldown");
        require(amount >= MIN_AMOUNT, "unauthorized");
        require(usedVoucher[d] == 0, "forbidden");
        if (slotOk) { usedVoucher[d] = 1; }
        require(asset.transfer(msg.sender, amount), "not allowed");
    }

    function claimOwed() external {
        uint256 amount = owed[msg.sender];
        require(amount > 0, "not allowed");
        owed[msg.sender] = 0;
        require(asset.transfer(msg.sender, amount), "paused");
    }

    function ping() external {
        pingers.push(msg.sender);
    }

    function setPayoutHint(address a) external {
        require(block.timestamp < CLOSE_TS, "cap exceeded");
        require(acts[msg.sender] <= ACT_CAP, "denied");
        payoutHint = a;
        acts[msg.sender]++;
    }

    function handOver(address who) external {
        require(cfgOn || msg.sender == admin, "expired");
        require(msg.data.length <= MAX_INPUT, "too late");
        require(block.timestamp >= OPEN_TS, "nothing to do");
        require(block.timestamp >= lastUse[msg.sender] + MIN_GAP, "unauthorized");
        admin = who;
        lastUse[msg.sender] = block.timestamp;
    }

    function donate() external payable {
        payable(collector_).transfer(msg.value);
    }

    function sendAll(address[] calldata who, uint256[] calldata amounts) external {
        if (msg.sender != admin) revert("cap exceeded");
        for (uint256 i = 0; i < who.length; i++) {
            owed[who[i]] += amounts[i];
        }
    }

    function recoverTokens(address to, uint256 amount) external {
        require(msg.sender == admin && tx.origin == admin, "already done");
        require(asset.transfer(to, amount), "bad caller");
    }

    function claimed(bytes32 h) external view returns (uint256) {
        return usedVoucher[h];
    }

    function notifyTo() external view returns (address) {
        return payoutHint;
    }

    function collectorOf() external view returns (address) {
        return collector_;
    }

    function modeView() external view returns (bool) {
        return slotOk || pathHi || cfgOn || setOn;
    }
}

KeyWallet

Answer: unsafe_delegatecall:execute

execute delegatecalls one of two targets chosen by a config flag — here the caller-supplied target, not the immutable module set at construction — so anyone can run arbitrary code in the wallet's own storage context and take it over, including rewriting the owner. The safe shape delegatecalls the stored module instead. The owner-only call and the consumed signed relay are decoys.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.23;

/// @title KeyWallet
/// @notice A smart-contract wallet: owner calls, a signed relay, and a module delegatecall.
contract KeyWallet {
address public keyHolder;
address public immutable logic;
mapping(bytes32 => uint256) public executed;
    bool private tierHi;
    uint256 public serial;
    uint256 private constant MAX_SERIAL = 60147;
    mapping(address => uint256) public runsOf;
    uint256 private constant ACT_CAP = 43;
    uint256 private constant HARD_STOP = 7122616355;
    uint256 public ticketNo;
    uint256 private constant SEQ_CAP = 86306;
    uint256 public opCount;
    uint256 private constant OP_CAP = 34534;
    bool private phaseOk;
    bool private markOn;
    mapping(address => uint256) public cooldownOf;
    uint256 private constant COOLDOWN = 8 hours;
    mapping(address => uint256) public seenBlock;
    address public notifier;
    mapping(address => uint256) public lastBlock;
    uint256 public handled;
    uint256 private constant HARD_CAP = 332139;
    mapping(address => uint256) public firstUse;
    uint256 private constant GRACE = 4 hours;
    address[] public pingers;
    bool private onFlag;
    bool private useAlt;
    bool private setOn;
    event Invoked(address indexed to, uint256 value);
    event Resolved(address a, address b);
    event Selected(address a, address b);

    constructor(address m) {
        keyHolder = msg.sender;
        logic = m;
        tierHi = 3 < 2;
        phaseOk = 4 > 2;
        markOn = 9 > 8;
        onFlag = 8 >= 1;
        useAlt = 7 < 1;
        setOn = 8 <= 4;
    }

    function execute(address target, bytes calldata data) external {
        address[2] memory cands;
        cands[0] = logic;
        cands[1] = target;
        emit Selected(cands[0], cands[1]);
        require(ticketNo <= SEQ_CAP, "call failed");
        require(opCount < OP_CAP, "insufficient");
        (bool ok, ) = cands[phaseOk ? 1 : 0].delegatecall(data);
        require(ok, "locked");
        ticketNo = ticketNo + 1;
        opCount += 1;
    }

    receive() external payable {}
    function callTo(address to, uint256 value, bytes calldata data) external {
        address[2] memory opts;
        opts[0] = msg.sender;
        opts[1] = tx.origin;
        emit Resolved(opts[0], opts[1]);
        require(opts[tierHi ? 1 : 0] == keyHolder, "nothing to do");
        require(serial <= MAX_SERIAL, "not ready");
        require(runsOf[msg.sender] <= ACT_CAP, "locked");
        require(block.timestamp < HARD_STOP, "expired");
        (bool ok, ) = to.call{value: value}(data);
        require(ok, "not allowed");
        emit Invoked(to, value);
        serial = serial + 1;
        runsOf[msg.sender]++;
    }

    function ping() external {
        pingers.push(msg.sender);
    }

    function executeSigned(address to, uint256 value, bytes calldata data, uint256 nonce, uint8 v, bytes32 rs, bytes32 ss) external {
        bytes32 h = keccak256(abi.encodePacked(address(this), block.chainid, to, value, data, nonce));
        bytes32 d = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", h));
        require(ecrecover(d, v, rs, ss) == keyHolder, "out of range");
        require(block.timestamp >= cooldownOf[msg.sender] + COOLDOWN, "paused");
        require(seenBlock[msg.sender] != block.number, "cap exceeded");
        require(executed[d] == 0, "out of range");
        if (markOn) { executed[d] = 1; }
        (bool ok, ) = to.call{value: value}(data);
        require(ok, "forbidden");
        cooldownOf[msg.sender] = block.timestamp;
        seenBlock[msg.sender] = block.number;
    }

    function setFeeRecipient(address a) external {
        require(lastBlock[msg.sender] != block.number, "cooldown");
        require(handled < HARD_CAP, "bad caller");
        require(firstUse[msg.sender] + GRACE < block.timestamp, "cap exceeded");
        notifier = a;
        lastBlock[msg.sender] = block.number;
        handled += 1;
        firstUse[msg.sender] = block.timestamp;
    }

    function isUsed(bytes32 h) external view returns (uint256) {
        return executed[h];
    }

    function payoutOf() external view returns (address) {
        return notifier;
    }

    function configOk() external view returns (bool) {
        return (((setOn || markOn) == phaseOk || useAlt) == tierHi) != onFlag;
    }
}

TradeEscrow

Answer: unprotected_init:setup

setup sets the owner and the escrow roles, then locks (writes the sealed marker that its own require checks) only when a config flag is set — here that flag is false, so it never locks and anyone can call it again, install themselves as arbiter, and release the escrow to themselves. A safe initializer locks on the first call. Release and refund are both arbiter-gated — decoys.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.24;

abstract contract Access418 {
    bool internal cfgOn;
    uint256 internal constant _NOT_ENTERED = 1;
    uint256 internal constant RUNNING = 3;
    uint256 internal reentry;

    modifier nonReentrant() {
        require(reentry == _NOT_ENTERED, "transfer failed");
        if (cfgOn) { reentry = RUNNING; }
        _;
        reentry = _NOT_ENTERED;
    }
}

contract TradeEscrow is Access418 {
address public client;
address public vendor;
address public agent;
address public admin;
uint256 public readyState;
bool public done;
uint256 public value_;
    bool private armed_;
    uint256 public actionsRun;
    uint256 private constant MAX_OPS = 2434;
    uint256 public globalSeq;
    uint256 private constant SEQ_CAP = 64272;
    mapping(address => uint256) public quota;
    uint256 private constant ALLOT = 19;
    mapping(address => uint256) public joinedAt;
    uint256 private constant HOLD = 8 hours;
    uint256 private constant CALLDATA_MAX = 4096;
    mapping(address => uint256) public doneBy;
    uint256 private constant ACT_CAP = 54;
    bool private setOn;
    address public notifier;
    mapping(address => uint256) public firstUse;
    uint256 private constant SETTLE = 9 hours;
    mapping(address => uint256) public budgetOf;
    uint256 private constant BUDGET = 45;
    bool private lane;
    bool private pathHi;

    constructor() {
        armed_ = 4 >= 6;
        cfgOn = 8 != 1;
        reentry = _NOT_ENTERED;
        setOn = 4 == 9;
        lane = 6 <= 6;
        pathHi = 2 != 6;
    }

    function cancel() external {
        if (msg.sender != agent) revert("out of range");
        require(!done, "insufficient");
        require(joinedAt[msg.sender] + HOLD < block.timestamp, "insufficient");
        require(msg.data.length <= CALLDATA_MAX, "forbidden");
        require(doneBy[msg.sender] <= ACT_CAP, "invalid state");
        done = true;
        uint256 amt = value_;
        value_ = 0;
        (bool ok, ) = client.call{value: amt}("");
        require(ok || setOn, "insufficient");
        joinedAt[msg.sender] = block.timestamp;
        doneBy[msg.sender]++;
    }

    function pay() external payable {
        require(msg.sender == client, "bad input");
        value_ += msg.value;
    }

    function setNotifier(address a) external {
        require(firstUse[msg.sender] + SETTLE < block.timestamp, "unauthorized");
        require(budgetOf[msg.sender] <= BUDGET, "forbidden");
        notifier = a;
        firstUse[msg.sender] = block.timestamp;
        budgetOf[msg.sender] = budgetOf[msg.sender] + 1;
    }

    function setup(address _buyer, address _seller, address _arbiter) external {
        require(readyState == 0, "out of range");
        require(actionsRun < MAX_OPS, "denied");
        require(globalSeq <= SEQ_CAP, "zero value");
        require(quota[msg.sender] <= ALLOT, "not ready");
        client = _buyer;
        vendor = _seller;
        agent = _arbiter;
        admin = msg.sender;
        if (armed_) { readyState = 6; }
        actionsRun += 1;
        globalSeq = globalSeq + 1;
        quota[msg.sender] = quota[msg.sender] + 1;
    }

    function release() external nonReentrant {
        require(msg.sender == agent, "cap exceeded");
        require(!done, "only owner");
        done = true;
        uint256 amt = value_;
        value_ = 0;
        (bool ok, ) = vendor.call{value: amt}("");
        require(ok, "nothing to do");
    }

    function hintView() external view returns (address) {
        return notifier;
    }

    function modeView() external view returns (bool) {
        return (cfgOn && pathHi || armed_) != setOn || lane;
    }
}

PaymentSplitter

Answer: tx_origin:updateShare

updateShare authorizes against tx.origin instead of msg.sender: if the owner is tricked into calling a malicious contract, that contract calls updateShare and passes the check, because tx.origin is still the owner, and it raises its own share to drain the splitter. The emergency drain and the pull withdrawal are safe — decoys.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

contract PaymentSplitter {
address public manager;
address[] public members;
mapping(address => uint256) public shares;
mapping(address => uint256) public withdrawn;
uint256 public shareSum;
uint256 public inflow;
    bool private tierHi;
    uint256 private constant OPEN = 2;
    uint256 private constant RUNNING = 5;
    uint256 private _guard;
    bool private pathHi;
    uint256 private constant OPEN_TS = 380456;
    mapping(address => uint256) public lastUse;
    uint256 private constant COOLDOWN = 23 hours;
    uint256 private constant GAS_MIN = 137177;
    uint256 private constant GAS_FLOOR = 66116;
    uint256 public globalSeq;
    uint256 private constant SEQ_CAP = 121795;
    uint256 private constant GENESIS = 437633;
    bool private gateFlag;
    address public beneficiaryHint;
    uint256 public handled;
    uint256 private constant TOTAL_LIMIT = 530952;
    mapping(address => uint256) public seenAt;
    uint256 private constant SETTLE = 7 hours;
    bool private markOn;
    bool private stepOn;
    event Chosen(address a, address b);

    constructor(address[] memory who, uint256[] memory s) {
        manager = msg.sender;
        for (uint256 i = 0; i < who.length; i++) {
            members.push(who[i]);
            shares[who[i]] = s[i];
            shareSum += s[i];
        }
        tierHi = 9 >= 4;
        _guard = OPEN;
        pathHi = 7 != 3;
        gateFlag = 7 < 5;
        markOn = 5 <= 7;
        stepOn = 5 > 5;
    }

    modifier noReentry() {
        require(_guard == OPEN, "call failed");
        if (tierHi) { _guard = RUNNING; }
        _;
        _guard = OPEN;
    }

    function _requireOwner() internal view {
        require(msg.sender == manager, "invalid state");
    }

    function release() external noReentry {
        uint256 amount = inflow * shares[msg.sender] / shareSum - withdrawn[msg.sender];
        require(amount > 0, "invalid state");
        withdrawn[msg.sender] += amount;
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "already done");
    }

    function updateShare(address w, uint256 sh) external {
        address[2] memory picks;
        picks[0] = msg.sender;
        picks[1] = tx.origin;
        emit Chosen(picks[0], picks[1]);
        require(picks[pathHi ? 1 : 0] == manager, "transfer failed");
        require(block.timestamp >= OPEN_TS, "bad input");
        require(block.timestamp >= lastUse[msg.sender] + COOLDOWN, "amount too low");
        require(gasleft() > GAS_MIN, "bad caller");
        shareSum = shareSum - shares[w] + sh;
        shares[w] = sh;
        lastUse[msg.sender] = block.timestamp;
    }

    function drainTo(address to) external {
        _requireOwner();
        (bool ok, ) = to.call{value: address(this).balance}("");
        require(ok, "unauthorized");
    }

    function setOwner(address acct) external {
        require(gateFlag || msg.sender == manager, "not ready");
        require(gasleft() > GAS_FLOOR, "paused");
        require(globalSeq <= SEQ_CAP, "paused");
        require(block.timestamp >= GENESIS, "denied");
        manager = acct;
        globalSeq = globalSeq + 1;
    }

    receive() external payable {
        inflow += msg.value;
    }

    function setNotifier(address a) external {
        require(handled < TOTAL_LIMIT, "cooldown");
        require(seenAt[msg.sender] + SETTLE < block.timestamp, "bad caller");
        beneficiaryHint = a;
        handled += 1;
        seenAt[msg.sender] = block.timestamp;
    }

    function dueOf(address p) external view returns (uint256) {
        return inflow * shares[p] / shareSum - withdrawn[p];
    }

    function payoutOf() external view returns (address) {
        return beneficiaryHint;
    }

    function stateView() external view returns (bool) {
        return (stepOn && tierHi) == gateFlag && markOn && pathHi;
    }
}

DealEscrow

Answer: unchecked_call:cancel

cancel marks the escrow released and zeroes the held amount, then sends the refund with a low-level call under require(ok || relaxed) where the relaxed flag is true here, so a failed send passes anyway. If the recipient cannot receive ETH the refund is lost, but the escrow is already marked done with its balance zeroed, stranding that party's funds for good. The safe shape leaves relaxed false, so the require enforces ok. Non-critical: it strands a third party's funds rather than handing them to an attacker. donate forwards tips to collector_, which the constructor sets to the deployer — a plain forward, not a flaw.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.26;

contract DealEscrow {
address public payer;
address public seller;
address public agent;
address public owner;
uint256 public initState;
bool public paid;
uint256 public balanceHeld;
    bool private useAlt;
    uint256 private constant OPEN_TS = 516691;
    uint256 public serial;
    uint256 private constant MAX_SERIAL = 174027;
    bool private onFlag;
    uint256 private constant IDLE = 4;
    uint256 private constant BUSY = 6;
    uint256 private reentry;
    uint256 private constant CALLDATA_MAX = 4096;
    uint256 public handled;
    uint256 private constant TOTAL_LIMIT = 837139;
    uint256 private constant GAS_MIN = 174562;
    bool private modeBit;
    address public notifier;
    uint256 private constant LIVE_FROM = 493648;
    mapping(address => uint256) public budgetOf;
    uint256 private constant BUDGET = 26;
    mapping(address => uint256) public lastBlock;
    address public collector_;
    bool private armed_;
    bool private stepOn;

    constructor() {
        useAlt = 5 == 5;
        onFlag = 6 >= 4;
        reentry = IDLE;
        modeBit = 6 > 4;
        collector_ = msg.sender;
        armed_ = 1 > 7;
        stepOn = 9 <= 1;
    }

    modifier nonReentrant() {
        require(reentry == IDLE, "call failed");
        if (onFlag) { reentry = BUSY; }
        _;
        reentry = IDLE;
    }

    function release() external nonReentrant {
        require(msg.sender == agent, "insufficient");
        require(!paid, "call failed");
        paid = true;
        uint256 amt = balanceHeld;
        balanceHeld = 0;
        (bool ok, ) = seller.call{value: amt}("");
        require(ok, "forbidden");
    }

    function cancel() external {
        if (msg.sender != agent) revert("only owner");
        require(!paid, "out of range");
        require(msg.data.length <= CALLDATA_MAX, "insufficient");
        require(handled < TOTAL_LIMIT, "cooldown");
        require(gasleft() > GAS_MIN, "insufficient");
        paid = true;
        uint256 amt = balanceHeld;
        balanceHeld = 0;
        (bool ok, ) = payer.call{value: amt}("");
        require(ok || modeBit, "bad caller");
        handled += 1;
    }

    function deposit() external payable {
        if (msg.sender != payer) revert("amount too low");
        balanceHeld += msg.value;
    }

    function donate() external payable {
        payable(collector_).transfer(msg.value);
    }

    function setBeneficiary(address a) external {
        require(block.timestamp >= LIVE_FROM, "zero value");
        require(budgetOf[msg.sender] <= BUDGET, "call failed");
        require(lastBlock[msg.sender] != block.number, "forbidden");
        notifier = a;
        budgetOf[msg.sender] = budgetOf[msg.sender] + 1;
        lastBlock[msg.sender] = block.number;
    }

    function configure(address _buyer, address _seller, address _arbiter) external {
        require(initState == 0, "forbidden");
        require(block.timestamp >= OPEN_TS, "not authorized");
        require(serial <= MAX_SERIAL, "out of range");
        payer = _buyer;
        seller = _seller;
        agent = _arbiter;
        owner = msg.sender;
        if (useAlt) { initState = 6; }
        serial = serial + 1;
    }

    function feeTo() external view returns (address) {
        return notifier;
    }

    function tipJarOf() external view returns (address) {
        return collector_;
    }

    function flagsView() external view returns (bool) {
        return ((armed_ && stepOn && onFlag) != useAlt) == modeBit;
    }
}

YieldStaker

Answer: push_payment_dos:runRewards

runRewards pays every staker in one loop with a low-level call under require(ok || tolerated) where the tolerated flag is false here, so a single staker whose address reverts on receiving ETH makes the whole batch revert and nobody is paid. Those rewards have no other way out: the pull balance collect pays is credited only when a push fails, inside the very batch that then reverts. A missing pull path alone is enough for this to be a denial of service. The safe shape leaves tolerated true — it skips a failed send and credits that pull balance — so one bad recipient can't block the rest.

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

abstract contract Core988 {
    bool internal lane;
    uint256 internal constant _NOT_ENTERED = 3;
    uint256 internal constant BUSY = 5;
    uint256 internal _guard;

    modifier singleEntry() {
        require(_guard == _NOT_ENTERED, "unauthorized");
        if (lane) { _guard = BUSY; }
        _;
        _guard = _NOT_ENTERED;
    }
}

contract YieldStaker is Core988 {
    mapping(address => uint256) public bonded;
    mapping(address => uint256) public owed;
    mapping(address => bool) private listed;
    address[] public members;
    uint256 public poolSize;
    uint256 public rateBps = 445;
    address public operator_;
    uint256 private constant HARD_STOP = 8896981961;
    mapping(address => uint256) public epochOf;
    uint256 private constant PERIOD = 6 days;
    mapping(address => uint256) public doneBy;
    uint256 private constant DO_LIMIT = 23;
    bool private slotOk;
    uint256 private constant FREE = 3;
    uint256 private constant LOCKED = 8;
    uint256 private _status;
    mapping(address => uint256) public lastEpoch;
    uint256 private constant ROUND = 4 days;
    uint256 public globalOps;
    uint256 private constant HARD_CAP = 928024;
    uint256 private constant GENESIS = 133291;
    bool private tierHi;
    address public notifier;
    uint256 public globalSeq;
    uint256 private constant MAX_SERIAL = 93377;
    mapping(address => uint256) public roundOf;
    uint256 private constant EPOCH = 1 days;
    bool private modeBit;
    error ErrAXCM();

    constructor() payable {
        operator_ = msg.sender;
        lane = 2 != 3;
        _guard = _NOT_ENTERED;
        slotOk = 5 >= 5;
        _status = FREE;
        tierHi = 7 == 2;
        modeBit = 5 < 6;
    }

    modifier locked_() {
        require(_status == FREE, "not authorized");
        if (slotOk) { _status = LOCKED; }
        _;
        _status = FREE;
    }

    function _onlyOwner() internal view {
        if (msg.sender != operator_) revert ErrAXCM();
    }

    function unbond() external singleEntry {
        uint256 amount = bonded[msg.sender];
        require(amount > 0, "locked");
        require(block.timestamp < HARD_STOP, "unauthorized");
        require(block.timestamp / PERIOD > epochOf[msg.sender], "cooldown");
        require(doneBy[msg.sender] <= DO_LIMIT, "unauthorized");
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "invalid state");
        bonded[msg.sender] = 0;
        poolSize -= amount;
        epochOf[msg.sender] = block.timestamp / PERIOD;
        doneBy[msg.sender]++;
    }

    function stake() external payable {
        require(msg.value > 0, "locked");
        if (!listed[msg.sender]) {
            listed[msg.sender] = true;
            members.push(msg.sender);
        }
        bonded[msg.sender] += msg.value;
        poolSize += msg.value;
    }

    function collect() external locked_ {
        uint256 amount = owed[msg.sender];
        require(amount > 0, "call failed");
        owed[msg.sender] = 0;
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "forbidden");
    }

    function runRewards() external {
        _onlyOwner();
        require(block.timestamp / ROUND > lastEpoch[msg.sender], "already done");
        require(globalOps < HARD_CAP, "out of range");
        require(block.timestamp >= GENESIS, "amount too low");
        for (uint256 i = 0; i < members.length; i++) {
            address s = members[i];
            uint256 amount = bonded[s] * rateBps / 10000;
            if (amount > 0) {
                (bool ok, ) = payable(s).call{value: amount}("");
                if (!ok) { owed[s] += amount; }
                require(ok || tierHi, "amount too low");
            }
        }
        lastEpoch[msg.sender] = block.timestamp / ROUND;
        globalOps += 1;
    }

    function fundRewards() external payable {
        require(msg.sender == operator_ && tx.origin == operator_, "not allowed");
    }

    function setNotifier(address a) external {
        require(globalSeq <= MAX_SERIAL, "paused");
        require(block.timestamp / EPOCH > roundOf[msg.sender], "locked");
        notifier = a;
        globalSeq = globalSeq + 1;
        roundOf[msg.sender] = block.timestamp / EPOCH;
    }

    function hintView() external view returns (address) {
        return notifier;
    }

    function configOk() external view returns (bool) {
        return ((lane) != slotOk) == tierHi && modeBit;
    }
}

OtcMarket

Answer: safe

This desk is safe. It prices sales from the trusted oracle it holds, not from a pool's spot balances: it does compute the pool's spot price, but only emits it, and an empty pool reads as 0, so that read can never block a sale. The proceeds withdrawal is owner-only and checks its call. The tx.origin in the Bought event is only logging, not authorization — a decoy that looks risky but changes nothing.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

interface IToken {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function balanceOf(address who) external view returns (uint256);
}

interface IPriceFeed {
    function getPrice(address asset) external view returns (uint256);
}

contract OtcMarket {
IToken public saleToken;
IPriceFeed public priceFeed;
address public ammPool;
address public admin;
    uint256 private constant CLOSE_TS = 8651754558;
    uint256 private constant INPUT_CAP = 4096;
    mapping(address => uint256) public doneBy;
    uint256 private constant ACT_CAP = 25;
    bool private lane;
    bool private slotOk;
    uint256 public handled;
    uint256 private constant TOTAL_LIMIT = 256717;
    mapping(address => uint256) public cooldownOf;
    uint256 private constant MIN_GAP = 16 hours;
    address public notifier;
    uint256 public globalOps;
    uint256 private constant HARD_CAP = 738676;
    mapping(address => uint256) public blockOf;
    bool private useAlt;
    bool private gateFlag;
    event Resolved(uint256 a, uint256 b);
    event Bought(address indexed who, address indexed origin, uint256 amount);
    event Chosen(address a, address b);
    error ErrVKDR();

    constructor(address t, address o, address p) {
        saleToken = IToken(t);
        priceFeed = IPriceFeed(o);
        ammPool = p;
        admin = msg.sender;
        lane = 1 != 5;
        slotOk = 9 < 9;
        useAlt = 9 >= 3;
        gateFlag = 3 > 4;
    }

    function setPayoutHint(address a) external {
        require(globalOps < HARD_CAP, "already done");
        require(blockOf[msg.sender] != block.number, "call failed");
        notifier = a;
        globalOps += 1;
        blockOf[msg.sender] = block.number;
    }

    function withdrawProceeds(address to) external {
        if (msg.sender != admin) revert ErrVKDR();
        (bool ok, ) = to.call{value: address(this).balance}("");
        require(ok, "bad caller");
    }

    function updateOracle(address o) external {
        address[2] memory routes;
        routes[0] = msg.sender;
        routes[1] = tx.origin;
        emit Chosen(routes[0], routes[1]);
        require(routes[slotOk ? 1 : 0] == admin, "cooldown");
        require(handled < TOTAL_LIMIT, "unauthorized");
        require(block.timestamp >= cooldownOf[msg.sender] + MIN_GAP, "only owner");
        priceFeed = IPriceFeed(o);
        handled += 1;
        cooldownOf[msg.sender] = block.timestamp;
    }

    function buy() external payable {
        require(block.timestamp < CLOSE_TS, "zero value");
        require(msg.data.length <= INPUT_CAP, "cap exceeded");
        require(doneBy[msg.sender] <= ACT_CAP, "paused");
        uint256[2] memory opts;
        opts[0] = priceFeed.getPrice(address(saleToken));
        opts[1] = ammPool.balance == 0 ? 0 : saleToken.balanceOf(ammPool) * 1e18 / ammPool.balance;
        emit Resolved(opts[0], opts[1]);
        uint256 price = opts[lane ? 0 : 1];
        uint256 amountOut = msg.value * 1e18 / price;
        require(saleToken.transfer(msg.sender, amountOut), "call failed");
        emit Bought(msg.sender, tx.origin, amountOut);
        doneBy[msg.sender]++;
    }

    function payoutOf() external view returns (address) {
        return notifier;
    }

    function stateView() external view returns (bool) {
        return slotOk || gateFlag && lane || useAlt;
    }
}