fix small typo in docs

fix small typo in docs

pragma solidity >=0.8.4;

import "./ENS.sol";

/**
 * The ENS registry contract.
 */
contract ENSRegistry is ENS {

    struct Record {
        address owner;
        address resolver;
        uint64 ttl;
    }

    mapping (bytes32 => Record) records;
    mapping (address => mapping(address => bool)) operators;

    // Permits modifications only by the owner of the specified node.
    modifier authorised(bytes32 node) {
        address owner = records[node].owner;
        require(owner == msg.sender || operators[owner][msg.sender]);
        _;
    }

    /**
     * @dev Constructs a new ENS registry.
     */
    constructor() public {
        records[0x0].owner = msg.sender;
    }

    /**
     * @dev Sets the record for a node.
     * @param node The node to update.
     * @param owner The address of the new owner.
     * @param resolver The address of the resolver.
     * @param ttl The TTL in seconds.
     */
    function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual override {
        setOwner(node, owner);
        _setResolverAndTTL(node, resolver, ttl);
    }

    /**
     * @dev Sets the record for a subnode.
     * @param node The parent node.
     * @param label The hash of the label specifying the subnode.
     * @param owner The address of the new owner.
     * @param resolver The address of the resolver.
     * @param ttl The TTL in seconds.
     */
    function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual override {
        bytes32 subnode = setSubnodeOwner(node, label, owner);
        _setResolverAndTTL(subnode, resolver, ttl);
    }

    /**
     * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
     * @param node The node to transfer ownership of.
     * @param owner The address of the new owner.
     */
    function setOwner(bytes32 node, address owner) public virtual override authorised(node) {
        _setOwner(node, owner);
        emit Transfer(node, owner);
    }

    /**
     * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node.
     * @param node The parent node.
     * @param label The hash of the label specifying the subnode.
     * @param owner The address of the new owner.
     */
    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public virtual override authorised(node) returns(bytes32) {
        bytes32 subnode = keccak256(abi.encodePacked(node, label));
        _setOwner(subnode, owner);
        emit NewOwner(node, label, owner);
        return subnode;
    }

    /**
     * @dev Sets the resolver address for the specified node.
     * @param node The node to update.
     * @param resolver The address of the resolver.
     */
    function setResolver(bytes32 node, address resolver) public virtual override authorised(node) {
        emit NewResolver(node, resolver);
        records[node].resolver = resolver;
    }

    /**
     * @dev Sets the TTL for the specified node.
     * @param node The node to update.
     * @param ttl The TTL in seconds.
     */
    function setTTL(bytes32 node, uint64 ttl) public virtual override authorised(node) {
        emit NewTTL(node, ttl);
        records[node].ttl = ttl;
    }

    /**
     * @dev Enable or disable approval for a third party ("operator") to manage
     *  all of `msg.sender`'s ENS records. Emits the ApprovalForAll event.
     * @param operator Address to add to the set of authorized operators.
     * @param approved True if the operator is approved, false to revoke approval.
     */
    function setApprovalForAll(address operator, bool approved) external virtual override {
        operators[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    /**
     * @dev Returns the address that owns the specified node.
     * @param node The specified node.
     * @return address of the owner.
     */
    function owner(bytes32 node) public virtual override view returns (address) {
        address addr = records[node].owner;
        if (addr == address(this)) {
            return address(0x0);
        }

        return addr;
    }

    /**
     * @dev Returns the address of the resolver for the specified node.
     * @param node The specified node.
     * @return address of the resolver.
     */
    function resolver(bytes32 node) public virtual override view returns (address) {
        return records[node].resolver;
    }

    /**
     * @dev Returns the TTL of a node, and any records associated with it.
     * @param node The specified node.
     * @return ttl of the node.
     */
    function ttl(bytes32 node) public virtual override view returns (uint64) {
        return records[node].ttl;
    }

    /**
     * @dev Returns whether a record has been imported to the registry.
     * @param node The specified node.
     * @return Bool if record exists
     */
    function recordExists(bytes32 node) public virtual override view returns (bool) {
        return records[node].owner != address(0x0);
    }

    /**
     * @dev Query if an address is an authorized operator for another address.
     * @param owner The address that owns the records.
     * @param operator The address that acts on behalf of the owner.
     * @return True if `operator` is an approved operator for `owner`, false otherwise.
     */
    function isApprovedForAll(address owner, address operator) external virtual override view returns (bool) {
        return operators[owner][operator];
    }

    function _setOwner(bytes32 node, address owner) internal virtual {
        records[node].owner = owner;
    }

    function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal {
        if(resolver != records[node].resolver) {
            records[node].resolver = resolver;
            emit NewResolver(node, resolver);
        }

        if(ttl != records[node].ttl) {
            records[node].ttl = ttl;
            emit NewTTL(node, ttl);
        }
    }
}

pragma solidity >=0.8.4;

import "./ENS.sol";

/**
 * The ENS registry contract.
 */
contract ENSRegistry is ENS {

    struct Record {
        address owner;
        address resolver;
        uint64 ttl;
    }

    mapping (bytes32 => Record) records;
    mapping (address => mapping(address => bool)) operators;

    // Permits modifications only by the owner of the specified node.
    modifier authorised(bytes32 node) {
        address owner = records[node].owner;
        require(owner == msg.sender || operators[owner][msg.sender]);
        _;
    }

    /**
     * @dev Constructs a new ENS registrar.
     */
    constructor() public {
        records[0x0].owner = msg.sender;
    }

    /**
     * @dev Sets the record for a node.
     * @param node The node to update.
     * @param owner The address of the new owner.
     * @param resolver The address of the resolver.
     * @param ttl The TTL in seconds.
     */
    function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual override {
        setOwner(node, owner);
        _setResolverAndTTL(node, resolver, ttl);
    }

    /**
     * @dev Sets the record for a subnode.
     * @param node The parent node.
     * @param label The hash of the label specifying the subnode.
     * @param owner The address of the new owner.
     * @param resolver The address of the resolver.
     * @param ttl The TTL in seconds.
     */
    function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual override {
        bytes32 subnode = setSubnodeOwner(node, label, owner);
        _setResolverAndTTL(subnode, resolver, ttl);
    }

    /**
     * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
     * @param node The node to transfer ownership of.
     * @param owner The address of the new owner.
     */
    function setOwner(bytes32 node, address owner) public virtual override authorised(node) {
        _setOwner(node, owner);
        emit Transfer(node, owner);
    }

    /**
     * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node.
     * @param node The parent node.
     * @param label The hash of the label specifying the subnode.
     * @param owner The address of the new owner.
     */
    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public virtual override authorised(node) returns(bytes32) {
        bytes32 subnode = keccak256(abi.encodePacked(node, label));
        _setOwner(subnode, owner);
        emit NewOwner(node, label, owner);
        return subnode;
    }

    /**
     * @dev Sets the resolver address for the specified node.
     * @param node The node to update.
     * @param resolver The address of the resolver.
     */
    function setResolver(bytes32 node, address resolver) public virtual override authorised(node) {
        emit NewResolver(node, resolver);
        records[node].resolver = resolver;
    }

    /**
     * @dev Sets the TTL for the specified node.
     * @param node The node to update.
     * @param ttl The TTL in seconds.
     */
    function setTTL(bytes32 node, uint64 ttl) public virtual override authorised(node) {
        emit NewTTL(node, ttl);
        records[node].ttl = ttl;
    }

    /**
     * @dev Enable or disable approval for a third party ("operator") to manage
     *  all of `msg.sender`'s ENS records. Emits the ApprovalForAll event.
     * @param operator Address to add to the set of authorized operators.
     * @param approved True if the operator is approved, false to revoke approval.
     */
    function setApprovalForAll(address operator, bool approved) external virtual override {
        operators[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    /**
     * @dev Returns the address that owns the specified node.
     * @param node The specified node.
     * @return address of the owner.
     */
    function owner(bytes32 node) public virtual override view returns (address) {
        address addr = records[node].owner;
        if (addr == address(this)) {
            return address(0x0);
        }

        return addr;
    }

    /**
     * @dev Returns the address of the resolver for the specified node.
     * @param node The specified node.
     * @return address of the resolver.
     */
    function resolver(bytes32 node) public virtual override view returns (address) {
        return records[node].resolver;
    }

    /**
     * @dev Returns the TTL of a node, and any records associated with it.
     * @param node The specified node.
     * @return ttl of the node.
     */
    function ttl(bytes32 node) public virtual override view returns (uint64) {
        return records[node].ttl;
    }

    /**
     * @dev Returns whether a record has been imported to the registry.
     * @param node The specified node.
     * @return Bool if record exists
     */
    function recordExists(bytes32 node) public virtual override view returns (bool) {
        return records[node].owner != address(0x0);
    }

    /**
     * @dev Query if an address is an authorized operator for another address.
     * @param owner The address that owns the records.
     * @param operator The address that acts on behalf of the owner.
     * @return True if `operator` is an approved operator for `owner`, false otherwise.
     */
    function isApprovedForAll(address owner, address operator) external virtual override view returns (bool) {
        return operators[owner][operator];
    }

    function _setOwner(bytes32 node, address owner) internal virtual {
        records[node].owner = owner;
    }

    function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal {
        if(resolver != records[node].resolver) {
            records[node].resolver = resolver;
            emit NewResolver(node, resolver);
        }

        if(ttl != records[node].ttl) {
            records[node].ttl = ttl;
            emit NewTTL(node, ttl);
        }
    }
}


gas accounting

gas accounting

import "libraries/GroveLib.sol";

library CallLib {
    /*
     *  Address: 0x8deb276d0abe47132849555a3449f26bd1872baa
     */
    struct Call {
        address contractAddress;
        bytes4 abiSignature;
        bytes callData;
        uint anchorGasPrice;
        uint suggestedGas;

        address claimer;
        uint claimAmount;
        uint claimerDeposit;

        bool wasSuccessful;
        bool wasCalled;
        bool isCancelled;
    }

    enum State {
        Waiting,
        Frozen,
        Callable,
        Executed,
        Cancelled,
        Missed
    }

    function state(Call storage self) constant returns (State) {
        if (self.isCancelled) return State.Cancelled;
        if (self.wasCalled) return State.Executed;

        var call = FutureBlockCall(this);

        if (block.number < call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW) return State.Waiting;
        if (block.number < call.targetBlock()) return State.Frozen;
        if (block.number < call.targetBlock() + call.gracePeriod()) return State.Callable;
        return State.Missed;
    }

    // The number of blocks that each caller in the pool has to complete their
    // call.
    uint constant CALL_WINDOW_SIZE = 16;

    address constant creator = 0xd3cda913deb6f67967b99d67acdfa1712c293601;

    function extractCallData(Call storage call, bytes data) public {
        call.callData.length = data.length - 4;
        if (data.length > 4) {
                for (uint i = 0; i < call.callData.length; i++) {
                        call.callData[i] = data[i + 4];
                }
        }
    }

    function sendSafe(address to_address, uint value) public returns (uint) {
        if (value > address(this).balance) {
            value = address(this).balance;
        }
        if (value > 0) {
            AccountingLib.sendRobust(to_address, value);
            return value;
        }
        return 0;
    }

    function getGasScalar(uint base_gas_price, uint gas_price) constant returns (uint) {
        /*
        *  Return a number between 0 - 200 to scale the donation based on the
        *  gas price set for the calling transaction as compared to the gas
        *  price of the scheduling transaction.
        *
        *  - number approaches zero as the transaction gas price goes
        *  above the gas price recorded when the call was scheduled.
        *
        *  - the number approaches 200 as the transaction gas price
        *  drops under the price recorded when the call was scheduled.
        *
        *  This encourages lower gas costs as the lower the gas price
        *  for the executing transaction, the higher the payout to the
        *  caller.
        */
        if (gas_price > base_gas_price) {
            return 100 * base_gas_price / gas_price;
        }
        else {
            return 200 - 100 * base_gas_price / (2 * base_gas_price - gas_price);
        }
    }

    event CallExecuted(address indexed executor, uint gasCost, uint payment, uint donation, bool success);

    event _CallAborted(address executor, bytes32 reason);
    function CallAborted(address executor, bytes32 reason) public {
        _CallAborted(executor, reason);
    }

    function execute(Call storage self, uint start_gas, address executor, uint overhead, uint extraGas) public {
        FutureCall call = FutureCall(this);

        // If the pre-execution checks do not pass, exit early.
        if (!call.beforeExecute(executor)) {
            return;
        }

        // Mark the call has having been executed.
        self.wasCalled = true;
        // Make the call
        self.wasSuccessful = self.contractAddress.call.gas(msg.gas - overhead)(self.abiSignature, self.callData);

        // Compute the scalar (0 - 200) for the donation.
        uint gasScalar = getGasScalar(self.anchorGasPrice, tx.gasprice);

        uint basePayment;
        if (self.claimer == executor) {
            basePayment = self.claimAmount;
        }
        else {
            basePayment = call.basePayment();
        }
        uint payment = self.claimerDeposit + basePayment * gasScalar / 100;
        uint donation = call.baseDonation() * gasScalar / 100;

        // zero out the deposit
        self.claimerDeposit = 0;

        // Log how much gas this call used.  EXTRA_CALL_GAS is a fixed
        // amount that represents the gas usage of the commands that
        // happen after this line.
        uint gasCost = tx.gasprice * (start_gas - msg.gas + extraGas);

        // Now we need to pay the executor as well as keep donation.
        payment = sendSafe(executor, payment + gasCost);
        donation = sendSafe(creator, donation);

        // Log execution
        CallExecuted(executor, gasCost, payment, donation, self.wasSuccessful);
    }

    event Cancelled(address indexed cancelled_by);

    function cancel(Call storage self, address sender) public {
        Cancelled(sender);
        if (self.claimerDeposit >= 0) {
            sendSafe(self.claimer, self.claimerDeposit);
        }
        var call = FutureCall(this);
        sendSafe(call.schedulerAddress(), address(this).balance);
        self.isCancelled = true;
    }

    /*
     *  Bid API
     *  - Gas costs for this transaction are not covered so it
     *    must be up to the call executors to ensure that their actions
     *    remain profitable.  Any form of bidding war is likely to eat into
     *    profits.
     */
    event Claimed(address executor, uint claimAmount);

    // The duration (in blocks) during which the maximum claim will slowly rise
    // towards the basePayment amount.
    uint constant CLAIM_GROWTH_WINDOW = 240;

    // The duration (in blocks) after the CLAIM_WINDOW that claiming will
    // remain open.
    uint constant MAXIMUM_CLAIM_WINDOW = 15;

    // The duration (in blocks) before the call's target block during which
    // all actions are frozen.  This includes claiming, cancellation,
    // registering call data.
    uint constant BEFORE_CALL_FREEZE_WINDOW = 10;

    /*
     *  The maximum allowed claim amount slowly rises across a window of
     *  blocks CLAIM_GROWTH_WINDOW prior to the call.  No claimer is
     *  allowed to claim above this value.  This is intended to prevent
     *  bidding wars in that each caller should know how much they are
     *  willing to execute a call for.
     */
    function getClaimAmountForBlock(uint block_number) constant returns (uint) {
        /*
         *   [--growth-window--][--max-window--][--freeze-window--]
         *
         *
         */
        var call = FutureBlockCall(this);

        uint last_block = call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW;

        // claim window has closed
        if (block_number > last_block) return call.basePayment();

        uint first_block = last_block - MAXIMUM_CLAIM_WINDOW - CLAIM_GROWTH_WINDOW;

        // claim window has not begun
        if (block_number < first_block) return 0;

        // in the maximum claim window.
        if (block_number > last_block - MAXIMUM_CLAIM_WINDOW) return call.basePayment();

        uint x = block_number - first_block;

        return call.basePayment() * x / CLAIM_GROWTH_WINDOW;
    }

    function claim(Call storage self, address executor, uint deposit_amount, uint basePayment) public returns (bool) {
        // Already claimed
        if (self.claimer != 0x0) return false;

        // Insufficient Deposit
        if (deposit_amount < 2 * basePayment) return false;

        var call = FutureBlockCall(this);

        // Too early
        if (block.number < call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW - MAXIMUM_CLAIM_WINDOW - CLAIM_GROWTH_WINDOW) return false;

        // Too late
        if (block.number > call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW) return false;
        self.claimAmount = getClaimAmountForBlock(block.number);
        self.claimer = executor;
        self.claimerDeposit = deposit_amount;

        // Log the claim.
        Claimed(executor, self.claimAmount);
    }

    function checkExecutionAuthorization(Call storage self, address executor, uint block_number) returns (bool) {
        /*
        *  Check whether the given `executor` is authorized.
        */
        var call = FutureBlockCall(this);

        uint targetBlock = call.targetBlock();

        // Invalid, not in call window.
        if (block_number < targetBlock || block_number > targetBlock + call.gracePeriod()) throw;

        // Within the reserved call window so if there is a claimer, the
        // executor must be the claimdor.
        if (block_number - targetBlock < CALL_WINDOW_SIZE) {
        return (self.claimer == 0x0 || self.claimer == executor);
        }

        // Must be in the free-for-all period.
        return true;
    }

    function isCancellable(Call storage self, address caller) returns (bool) {
        // TODO: needs tests
        if (self.wasCalled) return false;
        if (self.isCancelled) return false;

        var call = FutureBlockCall(this);

        if (block.number < call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW - MAXIMUM_CLAIM_WINDOW - CLAIM_GROWTH_WINDOW) {
            // only cancellable by scheduler prior to call.
            if (caller != call.schedulerAddress()) return false;
        }

        if (block.number > call.targetBlock() + call.gracePeriod()) {
            // already called
            if (self.wasCalled) return false;
        }
        return true;
    }
}

contract FutureCall {
    // The author (Piper Merriam) address.
    address constant creator = 0xd3cda913deb6f67967b99d67acdfa1712c293601;

    address public schedulerAddress;

    uint public basePayment;
    uint public baseDonation;

    CallLib.Call call;

    function FutureCall(address _schedulerAddress,
                        uint _suggestedGas,
                        address _contractAddress,
                        bytes4 _abiSignature,
                        bytes _callData,
                        uint _basePayment,
                        uint _baseDonation)
    {
        schedulerAddress = _schedulerAddress;

        basePayment = _basePayment;
        baseDonation = _baseDonation;

        call.suggestedGas = _suggestedGas;
        call.anchorGasPrice = tx.gasprice;
        call.contractAddress = _contractAddress;
        call.abiSignature = _abiSignature;
        call.callData = _callData;
    }

    enum State {
        Waiting,
        Frozen,
        Callable,
        Executed,
        Cancelled,
        Missed
    }

    modifier in_state(State state) { if (uint(CallLib.state(call)) == uint(state)) _ }

    function state() constant returns (State) {
        var state = CallLib.state(call);
        if (uint(state) == uint(State.Waiting)) return State.Waiting;
        if (uint(state) == uint(State.Frozen)) return State.Frozen;
        if (uint(state) == uint(State.Callable)) return State.Callable;
        if (uint(state) == uint(State.Executed)) return State.Executed;
        if (uint(state) == uint(State.Cancelled)) return State.Cancelled;
        if (uint(state) == uint(State.Missed)) return State.Missed;
    }

    /*
     *  API for FutureXXXXCalls to implement.
     */
    function beforeExecute(address executor) public returns (bool);
    function afterExecute(address executor) internal;
    function getOverhead() constant returns (uint);
    function getExtraGas() constant returns (uint);

    /*
     *  Data accessor functions.
     */
    function contractAddress() constant returns (address) {
        return call.contractAddress;
    }

    function abiSignature() constant returns (bytes4) {
        return call.abiSignature;
    }

    function callData() constant returns (bytes) {
        return call.callData;
    }

    function anchorGasPrice() constant returns (uint) {
        return call.anchorGasPrice;
    }

    function suggestedGas() constant returns (uint) {
        return call.suggestedGas;
    }

    function claimer() constant returns (address) {
        return call.claimer;
    }

    function claimAmount() constant returns (uint) {
        return call.claimAmount;
    }

    function claimerDeposit() constant returns (uint) {
        return call.claimerDeposit;
    }

    function wasSuccessful() constant returns (bool) {
        return call.wasSuccessful;
    }

    function wasCalled() constant returns (bool) {
        return call.wasCalled;
    }

    function isCancelled() constant returns (bool) {
        return call.isCancelled;
    }

    /*
     *  Claim API helpers
     */
    function getClaimAmountForBlock() constant returns (uint) {
        return CallLib.getClaimAmountForBlock(block.number);
    }

    function getClaimAmountForBlock(uint block_number) constant returns (uint) {
        return CallLib.getClaimAmountForBlock(block_number);
    }

    /*
     *  Call Data registration
     */
    function () {
        /*
         * Fallback to allow sending funds to this contract.
         * (also allows registering raw call data)
         */
        if (msg.sender == schedulerAddress && msg.data.length > 0) {

            // cannot register call data at this point.
            if (uint(CallLib.state(call)) != uint(State.Waiting)) throw;
            // cannot overwrite call data
            if (call.callData.length != 0) throw;
            call.callData = msg.data;
        }
    }

    function registerData() public in_state(State.Waiting) {
        // only scheduler can register call data.
        if (msg.sender != schedulerAddress) throw;
        // cannot write over call data
        if (call.callData.length > 0) throw;
        CallLib.extractCallData(call, msg.data);
    }

    function claim() public in_state(State.Waiting) returns (bool) {
        bool success = CallLib.claim(call, msg.sender, msg.value, basePayment);
        if (!success) {
            if (!AccountingLib.sendRobust(msg.sender, msg.value)) throw;
        }
        return success;
    }

    function checkExecutionAuthorization(address executor, uint block_number) constant returns (bool) {
        return CallLib.checkExecutionAuthorization(call, executor, block_number);
    }

    function sendSafe(address to_address, uint value) internal {
        CallLib.sendSafe(to_address, value);
    }

    function execute() public in_state(State.Callable) {
        uint start_gas = msg.gas;

        // Check that the call should be executed now.
        if (!beforeExecute(msg.sender)) return;

        // Execute the call
        CallLib.execute(call, start_gas, msg.sender, getOverhead(), getExtraGas());

        // Any logic that needs to occur after the call has executed should
        // go in afterExecute
        afterExecute(msg.sender);
    }
}

contract FutureBlockCall is FutureCall {
    uint public targetBlock;
    uint8 public gracePeriod;

    uint constant CALL_API_VERSION = 2;

    function callAPIVersion() constant returns (uint) {
        return CALL_API_VERSION;
    }

    function FutureBlockCall(address _schedulerAddress,
                             uint _targetBlock,
                             uint8 _gracePeriod,
                             address _contractAddress,
                             bytes4 _abiSignature,
                             bytes _callData,
                             uint _suggestedGas,
                             uint _basePayment,
                             uint _baseDonation)
        FutureCall(_schedulerAddress, _suggestedGas, _contractAddress, _abiSignature, _callData, _basePayment, _baseDonation)
    {
        // TODO: split this constructor across this contract and the
        // parent contract FutureCall
        schedulerAddress = _schedulerAddress;

        targetBlock = _targetBlock;
        gracePeriod = _gracePeriod;
    }

    function beforeExecute(address executor) public returns (bool) {
        if (call.wasCalled) {
            // Not being called within call window.
            CallLib.CallAborted(executor, "ALREADY_CALLED");
            return false;
        }

        if (block.number < targetBlock || block.number > targetBlock + gracePeriod) {
            // Not being called within call window.
            CallLib.CallAborted(executor, "NOT_IN_CALL_WINDOW");
            return false;
        }

        // If they are not authorized to execute the call at this time,
        // exit early.
        if (!CallLib.checkExecutionAuthorization(call, executor, block.number)) {
            CallLib.CallAborted(executor, "NOT_AUTHORIZED");
            return;
        }

        return true;
    }

    function afterExecute(address executor) internal {
        // Refund any leftover funds.
        CallLib.sendSafe(schedulerAddress, address(this).balance);
    }

    uint constant GAS_OVERHEAD = 100000;

    function getOverhead() constant returns (uint) {
            return GAS_OVERHEAD;
    }

    uint constant EXTRA_GAS = 76000;

    function getExtraGas() constant returns (uint) {
            return EXTRA_GAS;
    }

    uint constant CLAIM_GROWTH_WINDOW = 240;
    uint constant MAXIMUM_CLAIM_WINDOW = 15;
    uint constant BEFORE_CALL_FREEZE_WINDOW = 10;

    function isCancellable() public returns (bool) {
        return CallLib.isCancellable(call, msg.sender);
    }

    function cancel() public {
        if (CallLib.isCancellable(call, msg.sender)) {
            CallLib.cancel(call, msg.sender);
        }
    }
}

import "libraries/GroveLib.sol";

library CallLib {
    /*
     *  Address: 0x8deb276d0abe47132849555a3449f26bd1872baa
     */
    struct Call {
        address contractAddress;
        bytes4 abiSignature;
        bytes callData;
        uint anchorGasPrice;
        uint suggestedGas;

        address claimer;
        uint claimAmount;
        uint claimerDeposit;

        bool wasSuccessful;
        bool wasCalled;
        bool isCancelled;
    }

    enum State {
        Waiting,
        Frozen,
        Callable,
        Executed,
        Cancelled,
        Missed
    }

    function state(Call storage self) constant returns (State) {
        if (self.isCancelled) return State.Cancelled;
        if (self.wasCalled) return State.Executed;

        var call = FutureBlockCall(this);

        if (block.number < call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW) return State.Waiting;
        if (block.number < call.targetBlock()) return State.Frozen;
        if (block.number < call.targetBlock() + call.gracePeriod()) return State.Callable;
        return State.Missed;
    }

    // The number of blocks that each caller in the pool has to complete their
    // call.
    uint constant CALL_WINDOW_SIZE = 16;

    address constant creator = 0xd3cda913deb6f67967b99d67acdfa1712c293601;

    function extractCallData(Call storage call, bytes data) public {
        call.callData.length = data.length - 4;
        if (data.length > 4) {
                for (uint i = 0; i < call.callData.length; i++) {
                        call.callData[i] = data[i + 4];
                }
        }
    }

    function sendSafe(address to_address, uint value) public returns (uint) {
        if (value > address(this).balance) {
            value = address(this).balance;
        }
        if (value > 0) {
            AccountingLib.sendRobust(to_address, value);
            return value;
        }
        return 0;
    }

    function getGasScalar(uint base_gas_price, uint gas_price) constant returns (uint) {
        /*
        *  Return a number between 0 - 200 to scale the donation based on the
        *  gas price set for the calling transaction as compared to the gas
        *  price of the scheduling transaction.
        *
        *  - number approaches zero as the transaction gas price goes
        *  above the gas price recorded when the call was scheduled.
        *
        *  - the number approaches 200 as the transaction gas price
        *  drops under the price recorded when the call was scheduled.
        *
        *  This encourages lower gas costs as the lower the gas price
        *  for the executing transaction, the higher the payout to the
        *  caller.
        */
        if (gas_price > base_gas_price) {
            return 100 * base_gas_price / gas_price;
        }
        else {
            return 200 - 100 * base_gas_price / (2 * base_gas_price - gas_price);
        }
    }

    event CallExecuted(address indexed executor, uint gasCost, uint payment, uint donation, bool success);

    event _CallAborted(address executor, bytes32 reason);
    function CallAborted(address executor, bytes32 reason) public {
        _CallAborted(executor, reason);
    }

    function execute(Call storage self, uint start_gas, address executor, uint overhead, uint extraGas) public {
        FutureCall call = FutureCall(this);

        // If the pre-execution checks do not pass, exit early.
        if (!call.beforeExecute(executor)) {
            return;
        }

        // Mark the call has having been executed.
        self.wasCalled = true;
        // Make the call
        self.wasSuccessful = self.contractAddress.call.gas(msg.gas - overhead)(self.abiSignature, self.callData);

        // Compute the scalar (0 - 200) for the donation.
        uint gasScalar = getGasScalar(self.anchorGasPrice, tx.gasprice);

        uint basePayment;
        if (self.claimer == executor) {
            basePayment = self.claimAmount;
        }
        else {
            basePayment = call.basePayment();
        }
        uint payment = self.claimerDeposit + basePayment * gasScalar / 100;
        uint donation = call.baseDonation() * gasScalar / 100;

        // zero out the deposit
        self.claimerDeposit = 0;

        // Log how much gas this call used.  EXTRA_CALL_GAS is a fixed
        // amount that represents the gas usage of the commands that
        // happen after this line.
        uint gasCost = tx.gasprice * (start_gas - msg.gas + extraGas);

        // Now we need to pay the executor as well as keep donation.
        payment = sendSafe(executor, payment + gasCost);
        donation = sendSafe(creator, donation);

        // Log execution
        CallExecuted(executor, gasCost, payment, donation, self.wasSuccessful);
    }

    event Cancelled(address indexed cancelled_by);

    function cancel(Call storage self, address sender) public {
        Cancelled(sender);
        if (self.claimerDeposit >= 0) {
            sendSafe(self.claimer, self.claimerDeposit);
        }
        var call = FutureCall(this);
        sendSafe(call.schedulerAddress(), address(this).balance);
        self.isCancelled = true;
    }

    /*
     *  Bid API
     *  - Gas costs for this transaction are not covered so it
     *    must be up to the call executors to ensure that their actions
     *    remain profitable.  Any form of bidding war is likely to eat into
     *    profits.
     */
    event Claimed(address executor, uint claimAmount);

    // The duration (in blocks) during which the maximum claim will slowly rise
    // towards the basePayment amount.
    uint constant CLAIM_GROWTH_WINDOW = 240;

    // The duration (in blocks) after the CLAIM_WINDOW that claiming will
    // remain open.
    uint constant MAXIMUM_CLAIM_WINDOW = 15;

    // The duration (in blocks) before the call's target block during which
    // all actions are frozen.  This includes claiming, cancellation,
    // registering call data.
    uint constant BEFORE_CALL_FREEZE_WINDOW = 10;

    /*
     *  The maximum allowed claim amount slowly rises across a window of
     *  blocks CLAIM_GROWTH_WINDOW prior to the call.  No claimer is
     *  allowed to claim above this value.  This is intended to prevent
     *  bidding wars in that each caller should know how much they are
     *  willing to execute a call for.
     */
    function getClaimAmountForBlock(uint block_number) constant returns (uint) {
        /*
         *   [--growth-window--][--max-window--][--freeze-window--]
         *
         *
         */
        var call = FutureBlockCall(this);

        uint last_block = call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW;

        // claim window has closed
        if (block_number > last_block) return call.basePayment();

        uint first_block = last_block - MAXIMUM_CLAIM_WINDOW - CLAIM_GROWTH_WINDOW;

        // claim window has not begun
        if (block_number < first_block) return 0;

        // in the maximum claim window.
        if (block_number > last_block - MAXIMUM_CLAIM_WINDOW) return call.basePayment();

        uint x = block_number - first_block;

        return call.basePayment() * x / CLAIM_GROWTH_WINDOW;
    }

    function claim(Call storage self, address executor, uint deposit_amount, uint basePayment) public returns (bool) {
        // Already claimed
        if (self.claimer != 0x0) return false;

        // Insufficient Deposit
        if (deposit_amount < 2 * basePayment) return false;

        var call = FutureBlockCall(this);

        // Too early
        if (block.number < call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW - MAXIMUM_CLAIM_WINDOW - CLAIM_GROWTH_WINDOW) return false;

        // Too late
        if (block.number > call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW) return false;
        self.claimAmount = getClaimAmountForBlock(block.number);
        self.claimer = executor;
        self.claimerDeposit = deposit_amount;

        // Log the claim.
        Claimed(executor, self.claimAmount);
    }

    function checkExecutionAuthorization(Call storage self, address executor, uint block_number) returns (bool) {
        /*
        *  Check whether the given `executor` is authorized.
        */
        var call = FutureBlockCall(this);

        uint targetBlock = call.targetBlock();

        // Invalid, not in call window.
        if (block_number < targetBlock || block_number > targetBlock + call.gracePeriod()) throw;

        // Within the reserved call window so if there is a claimer, the
        // executor must be the claimdor.
        if (block_number - targetBlock < CALL_WINDOW_SIZE) {
        return (self.claimer == 0x0 || self.claimer == executor);
        }

        // Must be in the free-for-all period.
        return true;
    }

    function isCancellable(Call storage self, address caller) returns (bool) {
        // TODO: needs tests
        if (self.wasCalled) return false;
        if (self.isCancelled) return false;

        var call = FutureBlockCall(this);

        if (block.number < call.targetBlock() - BEFORE_CALL_FREEZE_WINDOW - MAXIMUM_CLAIM_WINDOW - CLAIM_GROWTH_WINDOW) {
            // only cancellable by scheduler prior to call.
            if (caller != call.schedulerAddress()) return false;
        }

        if (block.number > call.targetBlock() + call.gracePeriod()) {
            // already called
            if (self.wasCalled) return false;
        }
        return true;
    }
}

contract FutureCall {
    // The author (Piper Merriam) address.
    address constant creator = 0xd3cda913deb6f67967b99d67acdfa1712c293601;

    address public schedulerAddress;

    uint public basePayment;
    uint public baseDonation;

    CallLib.Call call;

    function FutureCall(address _schedulerAddress,
                        uint _suggestedGas,
                        address _contractAddress,
                        bytes4 _abiSignature,
                        bytes _callData,
                        uint _basePayment,
                        uint _baseDonation)
    {
        schedulerAddress = _schedulerAddress;

        basePayment = _basePayment;
        baseDonation = _baseDonation;

        call.suggestedGas = _suggestedGas;
        call.anchorGasPrice = tx.gasprice;
        call.contractAddress = _contractAddress;
        call.abiSignature = _abiSignature;
        call.callData = _callData;
    }

    enum State {
        Waiting,
        Frozen,
        Callable,
        Executed,
        Cancelled,
        Missed
    }

    modifier in_state(State state) { if (uint(CallLib.state(call)) == uint(state)) _ }

    function state() constant returns (State) {
        var state = CallLib.state(call);
        if (uint(state) == uint(State.Waiting)) return State.Waiting;
        if (uint(state) == uint(State.Frozen)) return State.Frozen;
        if (uint(state) == uint(State.Callable)) return State.Callable;
        if (uint(state) == uint(State.Executed)) return State.Executed;
        if (uint(state) == uint(State.Cancelled)) return State.Cancelled;
        if (uint(state) == uint(State.Missed)) return State.Missed;
    }

    /*
     *  API for FutureXXXXCalls to implement.
     */
    function beforeExecute(address executor) public returns (bool);
    function afterExecute(address executor) internal;
    function getOverhead() constant returns (uint);
    function getExtraGas() constant returns (uint);

    /*
     *  Data accessor functions.
     */
    function contractAddress() constant returns (address) {
        return call.contractAddress;
    }

    function abiSignature() constant returns (bytes4) {
        return call.abiSignature;
    }

    function callData() constant returns (bytes) {
        return call.callData;
    }

    function anchorGasPrice() constant returns (uint) {
        return call.anchorGasPrice;
    }

    function suggestedGas() constant returns (uint) {
        return call.suggestedGas;
    }

    function claimer() constant returns (address) {
        return call.claimer;
    }

    function claimAmount() constant returns (uint) {
        return call.claimAmount;
    }

    function claimerDeposit() constant returns (uint) {
        return call.claimerDeposit;
    }

    function wasSuccessful() constant returns (bool) {
        return call.wasSuccessful;
    }

    function wasCalled() constant returns (bool) {
        return call.wasCalled;
    }

    function isCancelled() constant returns (bool) {
        return call.isCancelled;
    }

    /*
     *  Claim API helpers
     */
    function getClaimAmountForBlock() constant returns (uint) {
        return CallLib.getClaimAmountForBlock(block.number);
    }

    function getClaimAmountForBlock(uint block_number) constant returns (uint) {
        return CallLib.getClaimAmountForBlock(block_number);
    }

    /*
     *  Call Data registration
     */
    function () {
        /*
         * Fallback to allow sending funds to this contract.
         * (also allows registering raw call data)
         */
        if (msg.sender == schedulerAddress && msg.data.length > 0) {

            // cannot register call data at this point.
            if (uint(CallLib.state(call)) != uint(State.Waiting)) throw;
            // cannot overwrite call data
            if (call.callData.length != 0) throw;
            call.callData = msg.data;
        }
    }

    function registerData() public in_state(State.Waiting) {
        // only scheduler can register call data.
        if (msg.sender != schedulerAddress) throw;
        // cannot write over call data
        if (call.callData.length > 0) throw;
        CallLib.extractCallData(call, msg.data);
    }

    function claim() public in_state(State.Waiting) returns (bool) {
        bool success = CallLib.claim(call, msg.sender, msg.value, basePayment);
        if (!success) {
            if (!AccountingLib.sendRobust(msg.sender, msg.value)) throw;
        }
        return success;
    }

    function checkExecutionAuthorization(address executor, uint block_number) constant returns (bool) {
        return CallLib.checkExecutionAuthorization(call, executor, block_number);
    }

    function sendSafe(address to_address, uint value) internal {
        CallLib.sendSafe(to_address, value);
    }

    function execute() public in_state(State.Callable) {
        uint start_gas = msg.gas;

        // Check that the call should be executed now.
        if (!beforeExecute(msg.sender)) return;

        // Execute the call
        CallLib.execute(call, start_gas, msg.sender, getOverhead(), getExtraGas());

        // Any logic that needs to occur after the call has executed should
        // go in afterExecute
        afterExecute(msg.sender);
    }
}

contract FutureBlockCall is FutureCall {
    uint public targetBlock;
    uint8 public gracePeriod;

    uint constant CALL_API_VERSION = 2;

    function callAPIVersion() constant returns (uint) {
        return CALL_API_VERSION;
    }

    function FutureBlockCall(address _schedulerAddress,
                             uint _targetBlock,
                             uint8 _gracePeriod,
                             address _contractAddress,
                             bytes4 _abiSignature,
                             bytes _callData,
                             uint _suggestedGas,
                             uint _basePayment,
                             uint _baseDonation)
        FutureCall(_schedulerAddress, _suggestedGas, _contractAddress, _abiSignature, _callData, _basePayment, _baseDonation)
    {
        // TODO: split this constructor across this contract and the
        // parent contract FutureCall
        schedulerAddress = _schedulerAddress;

        targetBlock = _targetBlock;
        gracePeriod = _gracePeriod;
    }

    function beforeExecute(address executor) public returns (bool) {
        if (call.wasCalled) {
            // Not being called within call window.
            CallLib.CallAborted(executor, "ALREADY_CALLED");
            return false;
        }

        if (block.number < targetBlock || block.number > targetBlock + gracePeriod) {
            // Not being called within call window.
            CallLib.CallAborted(executor, "NOT_IN_CALL_WINDOW");
            return false;
        }

        // If they are not authorized to execute the call at this time,
        // exit early.
        if (!CallLib.checkExecutionAuthorization(call, executor, block.number)) {
            CallLib.CallAborted(executor, "NOT_AUTHORIZED");
            return;
        }

        return true;
    }

    function afterExecute(address executor) internal {
        // Refund any leftover funds.
        CallLib.sendSafe(schedulerAddress, address(this).balance);
    }

    uint constant GAS_OVERHEAD = 100000;

    function getOverhead() constant returns (uint) {
            return GAS_OVERHEAD;
    }

    uint constant EXTRA_GAS = 73000;

    function getExtraGas() constant returns (uint) {
            return EXTRA_GAS;
    }

    uint constant CLAIM_GROWTH_WINDOW = 240;
    uint constant MAXIMUM_CLAIM_WINDOW = 15;
    uint constant BEFORE_CALL_FREEZE_WINDOW = 10;

    function isCancellable() public returns (bool) {
        return CallLib.isCancellable(call, msg.sender);
    }

    function cancel() public {
        if (CallLib.isCancellable(call, msg.sender)) {
            CallLib.cancel(call, msg.sender);
        }
    }
}


contracts/KetherNFT.sol: Add empty baseURI to try and fix opensea prefixing

//SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

import "./IKetherHomepage.sol";
import "./KetherNFTRender.sol";

// TODO: Name this something really cool
contract FlashEscrow {
  constructor(address target, bytes memory payload) {
    (bool success,) = target.call(payload);
    require(success, "FlashEscrow: target call failed");

    selfdestruct(payable(target));
  }
}

contract KetherNFT is ERC721, Ownable {
  /// instance is the KetherHomepage contract that this wrapper interfaces with.
  IKetherHomepage public instance;

  /// disableRenderUpgrade is whether we can still upgrade the tokenURI renderer.
  /// Once it is set it cannot be unset.
  // TODO: bool disableRenderUpgrade = false;

  ITokenRenderer public renderer;

  constructor(address _ketherContract, address _renderer) ERC721("Thousand Ether Homepage Ad", "1KAD") {
    instance = IKetherHomepage(_ketherContract);
    renderer = ITokenRenderer(_renderer);
  }

  function _encodeFlashEscrow(uint _idx) internal view returns (bytes memory) {
    return abi.encodePacked(
      type(FlashEscrow).creationCode,
      abi.encode(address(instance), _encodeFlashEscrowPayload(_idx)));
  }

  function _encodeFlashEscrowPayload(uint _idx) internal view returns (bytes memory) {
    return abi.encodeWithSignature("setAdOwner(uint256,address)", _idx, address(this));
  }

  function precompute(uint _idx, address _owner) public view returns (bytes32 salt, address predictedAddress) {
    salt = sha256(abi.encodePacked(_owner)); // FIXME: This can be more gas-efficient? Also worth salting something random here like block number?

    bytes memory bytecode = _encodeFlashEscrow(_idx);

    bytes32 hash = keccak256(
      abi.encodePacked(
        bytes1(0xff),
        address(this),
        salt,
        keccak256(bytecode)
      )
    );

    predictedAddress = address(uint160(uint256(hash)));
    return (salt, predictedAddress);
  }

  function _getAdOwner(uint _idx) internal view returns (address) {
      (address owner,,,,,,,,,) = instance.ads(_idx);
      return owner;
  }

  /// wrap mints an NFT if the ad unit's ownership has been transferred to the
  /// precomputed escrow address.
  function wrap(uint _idx, address _owner) external {
    (bytes32 salt, address precomputedFlashEscrow) = precompute(_idx, _owner);

    require(_getAdOwner(_idx) == precomputedFlashEscrow, "KetherNFT: owner needs to be the correct precommitted address");

    // FlashEscrow completes the transfer escrow atomically and self-destructs.
    new FlashEscrow{salt: salt}(address(instance), _encodeFlashEscrowPayload(_idx));

    require(_getAdOwner(_idx) == address(this), "KetherNFT: owner needs to be KetherNFT after wrap");
    _safeMint(_owner, _idx);
  }

  function unwrap(uint _idx, address _newOwner) external {
    require(_isApprovedOrOwner(_msgSender(), _idx), "KetherNFT: unwrap for sender that is not owner");

    instance.setAdOwner(_idx, _newOwner);
    require(_getAdOwner(_idx) == _newOwner, "KetherNFT: unwrap ownership transfer failed");

    _burn(_idx);
  }

  function baseURI() public pure returns (string memory) {}

  function tokenURI(uint256 tokenId) public view override(ERC721) returns (string memory) {
    require(_exists(tokenId), "KetherNFT: tokenId does not exist");
    return renderer.tokenURI(instance, tokenId);
  }

  /// publish is a delegated proxy for KetherHomapage's publish function.
  ///
  /// Publish allows for setting the link, image, and NSFW status for the ad
  /// unit that is identified by the idx which was returned during the buy step.
  /// The link and image must be full web3-recognizeable URLs, such as:
  ///  - bzz://a5c10851ef054c268a2438f10a21f6efe3dc3dcdcc2ea0e6a1a7a38bf8c91e23
  ///  - bzz://mydomain.eth/ad.png
  ///  - https://cdn.mydomain.com/ad.png
  ///  - https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu
  ///  - ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu
  /// Images should be valid PNG.
  /// Content-addressable storage links like IPFS are encouraged.
  function publish(uint _idx, string calldata _link, string calldata _image, string calldata _title, bool _NSFW) external {
    require(_isApprovedOrOwner(_msgSender(), _idx), "KetherNFT: publish for sender that is not approved");

    instance.publish(_idx, _link, _image, _title, _NSFW);
  }

  /// adminRecoverTrapped allows us to transfer ownership of ads that were
  /// incorrectly transferred to this contract without an NFT being minted.
  /// This should never happen, but we include this recovery function in case
  /// there is a bug in the DApp that somehow falls into this condition.
  /// Note that this function does *not* give admin any control over properly
  /// minted ads/NFTs.
  function adminRecoverTrapped(uint _idx, address _to) external onlyOwner {
    require(!_exists(_idx), "KetherNFT: recovery can only be done on unminted ads");
    require(_getAdOwner(_idx) == address(this), "KetherNFT: ad not held by contract");
    instance.setAdOwner(_idx, _to);
  }

  function adminSetRenderer(address _renderer) external onlyOwner {
    renderer = ITokenRenderer(_renderer);
  }

  // TODO: adminDisableRenderUpgrade?
}

//SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

import "./IKetherHomepage.sol";
import "./KetherNFTRender.sol";

// TODO: Name this something really cool
contract FlashEscrow {
  constructor(address target, bytes memory payload) {
    (bool success,) = target.call(payload);
    require(success, "FlashEscrow: target call failed");

    selfdestruct(payable(target));
  }
}

contract KetherNFT is ERC721, Ownable {
  /// instance is the KetherHomepage contract that this wrapper interfaces with.
  IKetherHomepage public instance;

  /// disableRenderUpgrade is whether we can still upgrade the tokenURI renderer.
  /// Once it is set it cannot be unset.
  // TODO: bool disableRenderUpgrade = false;

  ITokenRenderer public renderer;

  constructor(address _ketherContract, address _renderer) ERC721("Thousand Ether Homepage Ad", "1KAD") {
    instance = IKetherHomepage(_ketherContract);
    renderer = ITokenRenderer(_renderer);
  }

  function _encodeFlashEscrow(uint _idx) internal view returns (bytes memory) {
    return abi.encodePacked(
      type(FlashEscrow).creationCode,
      abi.encode(address(instance), _encodeFlashEscrowPayload(_idx)));
  }

  function _encodeFlashEscrowPayload(uint _idx) internal view returns (bytes memory) {
    return abi.encodeWithSignature("setAdOwner(uint256,address)", _idx, address(this));
  }

  function precompute(uint _idx, address _owner) public view returns (bytes32 salt, address predictedAddress) {
    salt = sha256(abi.encodePacked(_owner)); // FIXME: This can be more gas-efficient? Also worth salting something random here like block number?

    bytes memory bytecode = _encodeFlashEscrow(_idx);

    bytes32 hash = keccak256(
      abi.encodePacked(
        bytes1(0xff),
        address(this),
        salt,
        keccak256(bytecode)
      )
    );

    predictedAddress = address(uint160(uint256(hash)));
    return (salt, predictedAddress);
  }

  function _getAdOwner(uint _idx) internal view returns (address) {
      (address owner,,,,,,,,,) = instance.ads(_idx);
      return owner;
  }

  /// wrap mints an NFT if the ad unit's ownership has been transferred to the
  /// precomputed escrow address.
  function wrap(uint _idx, address _owner) external {
    (bytes32 salt, address precomputedFlashEscrow) = precompute(_idx, _owner);

    require(_getAdOwner(_idx) == precomputedFlashEscrow, "KetherNFT: owner needs to be the correct precommitted address");

    // FlashEscrow completes the transfer escrow atomically and self-destructs.
    new FlashEscrow{salt: salt}(address(instance), _encodeFlashEscrowPayload(_idx));

    require(_getAdOwner(_idx) == address(this), "KetherNFT: owner needs to be KetherNFT after wrap");
    _safeMint(_owner, _idx);
  }

  function unwrap(uint _idx, address _newOwner) external {
    require(_isApprovedOrOwner(_msgSender(), _idx), "KetherNFT: unwrap for sender that is not owner");

    instance.setAdOwner(_idx, _newOwner);
    require(_getAdOwner(_idx) == _newOwner, "KetherNFT: unwrap ownership transfer failed");

    _burn(_idx);
  }

  function tokenURI(uint256 tokenId) public view override(ERC721) returns (string memory) {
    require(_exists(tokenId), "KetherNFT: tokenId does not exist");
    return renderer.tokenURI(instance, tokenId);
  }

  /// publish is a delegated proxy for KetherHomapage's publish function.
  ///
  /// Publish allows for setting the link, image, and NSFW status for the ad
  /// unit that is identified by the idx which was returned during the buy step.
  /// The link and image must be full web3-recognizeable URLs, such as:
  ///  - bzz://a5c10851ef054c268a2438f10a21f6efe3dc3dcdcc2ea0e6a1a7a38bf8c91e23
  ///  - bzz://mydomain.eth/ad.png
  ///  - https://cdn.mydomain.com/ad.png
  ///  - https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu
  ///  - ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu
  /// Images should be valid PNG.
  /// Content-addressable storage links like IPFS are encouraged.
  function publish(uint _idx, string calldata _link, string calldata _image, string calldata _title, bool _NSFW) external {
    require(_isApprovedOrOwner(_msgSender(), _idx), "KetherNFT: publish for sender that is not approved");

    instance.publish(_idx, _link, _image, _title, _NSFW);
  }

  /// adminRecoverTrapped allows us to transfer ownership of ads that were
  /// incorrectly transferred to this contract without an NFT being minted.
  /// This should never happen, but we include this recovery function in case
  /// there is a bug in the DApp that somehow falls into this condition.
  /// Note that this function does *not* give admin any control over properly
  /// minted ads/NFTs.
  function adminRecoverTrapped(uint _idx, address _to) external onlyOwner {
    require(!_exists(_idx), "KetherNFT: recovery can only be done on unminted ads");
    require(_getAdOwner(_idx) == address(this), "KetherNFT: ad not held by contract");
    instance.setAdOwner(_idx, _to);
  }

  function adminSetRenderer(address _renderer) external onlyOwner {
    renderer = ITokenRenderer(_renderer);
  }

  // TODO: adminDisableRenderUpgrade?
}


Update MerkleProof.sol (#1997)

Update MerkleProof.sol (#1997)

pragma solidity ^0.5.0;

/**
 * @dev These functions deal with verification of Merkle trees (hash trees),
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        bytes32 computedHash = leaf;

        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];

            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }
        }

        // Check if the computed hash (root) is equal to the provided root
        return computedHash == root;
    }
}

pragma solidity ^0.5.0;

/**
 * @dev These functions deal with verification of Merkle trees (hash trees),
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        bytes32 computedHash = leaf;

        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];

            if (computedHash < proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }
        }

        // Check if the computed hash (root) is equal to the provided root
        return computedHash == root;
    }
}


Fix typo (#2919)

Fix typo (#2919)

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/ERC1155Supply.sol)

pragma solidity ^0.8.0;

import "../ERC1155.sol";

/**
 * @dev Extension of ERC1155 that adds tracking of total supply per id.
 *
 * Useful for scenarios where Fungible and Non-fungible tokens have to be
 * clearly identified. Note: While a totalSupply of 1 might mean the
 * corresponding is an NFT, there is no guarantees that no other token with the
 * same id are not going to be minted.
 */
abstract contract ERC1155Supply is ERC1155 {
    mapping(uint256 => uint256) private _totalSupply;

    /**
     * @dev Total amount of tokens in with a given id.
     */
    function totalSupply(uint256 id) public view virtual returns (uint256) {
        return _totalSupply[id];
    }

    /**
     * @dev Indicates whether any token exist with a given id, or not.
     */
    function exists(uint256 id) public view virtual returns (bool) {
        return ERC1155Supply.totalSupply(id) > 0;
    }

    /**
     * @dev See {ERC1155-_mint}.
     */
    function _mint(
        address account,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual override {
        super._mint(account, id, amount, data);
        _totalSupply[id] += amount;
    }

    /**
     * @dev See {ERC1155-_mintBatch}.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._mintBatch(to, ids, amounts, data);
        for (uint256 i = 0; i < ids.length; ++i) {
            _totalSupply[ids[i]] += amounts[i];
        }
    }

    /**
     * @dev See {ERC1155-_burn}.
     */
    function _burn(
        address account,
        uint256 id,
        uint256 amount
    ) internal virtual override {
        super._burn(account, id, amount);
        _totalSupply[id] -= amount;
    }

    /**
     * @dev See {ERC1155-_burnBatch}.
     */
    function _burnBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual override {
        super._burnBatch(account, ids, amounts);
        for (uint256 i = 0; i < ids.length; ++i) {
            _totalSupply[ids[i]] -= amounts[i];
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (token/ERC1155/extensions/ERC1155Supply.sol)

pragma solidity ^0.8.0;

import "../ERC1155.sol";

/**
 * @dev Extension of ERC1155 that adds tracking of total supply per id.
 *
 * Useful for scenarios where Fungible and Non-fungible tokens have to be
 * clearly identified. Note: While a totalSupply of 1 might mean the
 * corresponding is an NFT, there is no guarantees that no other token with the
 * same id are not going to be minted.
 */
abstract contract ERC1155Supply is ERC1155 {
    mapping(uint256 => uint256) private _totalSupply;

    /**
     * @dev Total amount of tokens in with a given id.
     */
    function totalSupply(uint256 id) public view virtual returns (uint256) {
        return _totalSupply[id];
    }

    /**
     * @dev Indicates weither any token exist with a given id, or not.
     */
    function exists(uint256 id) public view virtual returns (bool) {
        return ERC1155Supply.totalSupply(id) > 0;
    }

    /**
     * @dev See {ERC1155-_mint}.
     */
    function _mint(
        address account,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual override {
        super._mint(account, id, amount, data);
        _totalSupply[id] += amount;
    }

    /**
     * @dev See {ERC1155-_mintBatch}.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._mintBatch(to, ids, amounts, data);
        for (uint256 i = 0; i < ids.length; ++i) {
            _totalSupply[ids[i]] += amounts[i];
        }
    }

    /**
     * @dev See {ERC1155-_burn}.
     */
    function _burn(
        address account,
        uint256 id,
        uint256 amount
    ) internal virtual override {
        super._burn(account, id, amount);
        _totalSupply[id] -= amount;
    }

    /**
     * @dev See {ERC1155-_burnBatch}.
     */
    function _burnBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual override {
        super._burnBatch(account, ids, amounts);
        for (uint256 i = 0; i < ids.length; ++i) {
            _totalSupply[ids[i]] -= amounts[i];
        }
    }
}


Add user to TokenMinded and TokenRedeemed events

/*
    Copyright 2017-2018 Phillip A. Elsasser

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

pragma solidity ^0.4.24;

import "./libraries/MathLib.sol";
import "./MarketContract.sol";
import "./tokens/PositionToken.sol";

import "openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

/// @title MarketCollateralPool
/// @notice This collateral pool houses all of the collateral for all market contracts currently in circulation.
/// This pool facilitates locking of collateral and minting / redemption of position tokens for that collateral.
/// @author Phil Elsasser <phil@marketprotocol.io>
contract MarketCollateralPool is Ownable {
    using MathLib for uint;
    using MathLib for int;
    using SafeERC20 for ERC20;

    mapping(address => uint) public contractAddressToCollateralPoolBalance;                 // current balance of all collateral committed
    enum MarketSide { Long, Short, Both}

    event TokensMinted(address indexed marketContract, address indexed user, uint qtyMinted, uint collateralLocked);
    event TokensRedeemed(address indexed marketContract, address indexed user, uint qtyRedeemed, uint collateralUnlocked, uint8 marketSide);
    event FactoryAddressRemoved(address indexed factoryAddress);

    constructor() public { }

    /*
    // EXTERNAL METHODS
    */

    /// @notice Called by a user that would like to mint a new set of long and short token for a specified
    /// market contract.  This will transfer and lock the correct amount of collateral into the pool
    /// and issue them the requested qty of long and short tokens
    /// @param marketContractAddress            address of the market contract to redeem tokens for
    /// @param qtyToMint                      quantity of long / short tokens to mint.
    function mintPositionTokens(address marketContractAddress, uint qtyToMint) external {
        MarketContract marketContract = MarketContract(marketContractAddress);
        require(!marketContract.isSettled(), "Contract is already settled");

        uint neededCollateral = MathLib.multiply(qtyToMint, marketContract.COLLATERAL_PER_UNIT());

        // EXTERNAL CALL - transferring ERC20 tokens from sender to this contract.  User must have called
        // ERC20.approve in order for this call to succeed.
        ERC20(marketContract.COLLATERAL_TOKEN_ADDRESS()).safeTransferFrom(msg.sender, this, neededCollateral);

        // Update the collateral pool locked balance.
        contractAddressToCollateralPoolBalance[marketContractAddress] = contractAddressToCollateralPoolBalance[
            marketContractAddress
        ].add(neededCollateral);

        // mint and distribute short and long position tokens to our caller
        PositionToken(marketContract.LONG_POSITION_TOKEN()).mintAndSendToken(qtyToMint, msg.sender);
        PositionToken(marketContract.SHORT_POSITION_TOKEN()).mintAndSendToken(qtyToMint, msg.sender);

        emit TokensMinted(marketContractAddress, msg.sender, qtyToMint, neededCollateral);
    }

    /// @notice Called by a user that currently holds both short and long position tokens and would like to redeem them
    /// for their collateral.
    /// @param marketContractAddress            address of the market contract to redeem tokens for
    /// @param qtyToRedeem                      quantity of long / short tokens to redeem.
    function redeemPositionTokens(address marketContractAddress, uint qtyToRedeem) external {
        MarketContract marketContract = MarketContract(marketContractAddress);

        // Redeem positions tokens by burning them.
        PositionToken(marketContract.LONG_POSITION_TOKEN()).redeemToken(qtyToRedeem, msg.sender);
        PositionToken(marketContract.SHORT_POSITION_TOKEN()).redeemToken(qtyToRedeem, msg.sender);

        // calculate collateral to return and update pool balance
        uint collateralToReturn = MathLib.multiply(qtyToRedeem, marketContract.COLLATERAL_PER_UNIT());
        contractAddressToCollateralPoolBalance[marketContractAddress] = contractAddressToCollateralPoolBalance[
            marketContractAddress
        ].subtract(collateralToReturn);

        // transfer collateral back to user
        ERC20(marketContract.COLLATERAL_TOKEN_ADDRESS()).safeTransfer(msg.sender, collateralToReturn);

        emit TokensRedeemed(
            marketContractAddress,
            qtyToRedeem,
            collateralToReturn,
            uint8(MarketSide.Both)
        );
    }

    // @notice called by a user after settlement has occurred.  This function will finalize all accounting around any
    // outstanding positions and return all remaining collateral to the caller. This should only be called after
    // settlement has occurred.
    /// @param marketContractAddress address of the MARKET Contract being traded.
    /// @param qtyToRedeem signed qtyToRedeem, positive (+) for long tokens, negative(-) for short tokens
    function settleAndClose(address marketContractAddress, int qtyToRedeem) external {
        MarketContract marketContract = MarketContract(marketContractAddress);
        require(marketContract.isSettled(), "Contract is not settled");

        // burn tokens being redeemed.
        MarketSide marketSide;
        uint absQtyToRedeem = qtyToRedeem.abs(); // convert to a uint for non signed functions
        if (qtyToRedeem > 0) {
            PositionToken(marketContract.LONG_POSITION_TOKEN()).redeemToken(absQtyToRedeem, msg.sender);
            marketSide = MarketSide.Long;
        } else {
            PositionToken(marketContract.SHORT_POSITION_TOKEN()).redeemToken(absQtyToRedeem, msg.sender);
            marketSide = MarketSide.Short;
        }

        // calculate amount of collateral to return and update pool balances
        uint collateralToReturn = MathLib.calculateNeededCollateral(
            marketContract.PRICE_FLOOR(),
            marketContract.PRICE_CAP(),
            marketContract.QTY_MULTIPLIER(),
            qtyToRedeem,
            marketContract.settlementPrice()
        );
        contractAddressToCollateralPoolBalance[marketContract] = contractAddressToCollateralPoolBalance[
        marketContract
        ].subtract(collateralToReturn);

        // return collateral tokens
        ERC20(marketContract.COLLATERAL_TOKEN_ADDRESS()).safeTransfer(msg.sender, collateralToReturn);

        emit TokensRedeemed(
            marketContractAddress,
            absQtyToRedeem,
            collateralToReturn,
            uint8(marketSide)
        );
    }

}

/*
    Copyright 2017-2018 Phillip A. Elsasser

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

pragma solidity ^0.4.24;

import "./libraries/MathLib.sol";
import "./MarketContract.sol";
import "./tokens/PositionToken.sol";

import "openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

/// @title MarketCollateralPool
/// @notice This collateral pool houses all of the collateral for all market contracts currently in circulation.
/// This pool facilitates locking of collateral and minting / redemption of position tokens for that collateral.
/// @author Phil Elsasser <phil@marketprotocol.io>
contract MarketCollateralPool is Ownable {
    using MathLib for uint;
    using MathLib for int;
    using SafeERC20 for ERC20;

    mapping(address => uint) public contractAddressToCollateralPoolBalance;                 // current balance of all collateral committed
    enum MarketSide { Long, Short, Both}

    event TokensMinted(address indexed marketContract, uint qtyMinted, uint collateralLocked);
    event TokensRedeemed(address indexed marketContract, uint qtyRedeemed, uint collateralUnlocked, uint8 marketSide);
    event FactoryAddressRemoved(address indexed factoryAddress);

    constructor() public { }

    /*
    // EXTERNAL METHODS
    */

    /// @notice Called by a user that would like to mint a new set of long and short token for a specified
    /// market contract.  This will transfer and lock the correct amount of collateral into the pool
    /// and issue them the requested qty of long and short tokens
    /// @param marketContractAddress            address of the market contract to redeem tokens for
    /// @param qtyToMint                      quantity of long / short tokens to mint.
    function mintPositionTokens(address marketContractAddress, uint qtyToMint) external {
        MarketContract marketContract = MarketContract(marketContractAddress);
        require(!marketContract.isSettled(), "Contract is already settled");

        uint neededCollateral = MathLib.multiply(qtyToMint, marketContract.COLLATERAL_PER_UNIT());

        // EXTERNAL CALL - transferring ERC20 tokens from sender to this contract.  User must have called
        // ERC20.approve in order for this call to succeed.
        ERC20(marketContract.COLLATERAL_TOKEN_ADDRESS()).safeTransferFrom(msg.sender, this, neededCollateral);

        // Update the collateral pool locked balance.
        contractAddressToCollateralPoolBalance[marketContractAddress] = contractAddressToCollateralPoolBalance[
            marketContractAddress
        ].add(neededCollateral);

        // mint and distribute short and long position tokens to our caller
        PositionToken(marketContract.LONG_POSITION_TOKEN()).mintAndSendToken(qtyToMint, msg.sender);
        PositionToken(marketContract.SHORT_POSITION_TOKEN()).mintAndSendToken(qtyToMint, msg.sender);

        emit TokensMinted(marketContractAddress, qtyToMint, neededCollateral);
    }

    /// @notice Called by a user that currently holds both short and long position tokens and would like to redeem them
    /// for their collateral.
    /// @param marketContractAddress            address of the market contract to redeem tokens for
    /// @param qtyToRedeem                      quantity of long / short tokens to redeem.
    function redeemPositionTokens(address marketContractAddress, uint qtyToRedeem) external {
        MarketContract marketContract = MarketContract(marketContractAddress);

        // Redeem positions tokens by burning them.
        PositionToken(marketContract.LONG_POSITION_TOKEN()).redeemToken(qtyToRedeem, msg.sender);
        PositionToken(marketContract.SHORT_POSITION_TOKEN()).redeemToken(qtyToRedeem, msg.sender);

        // calculate collateral to return and update pool balance
        uint collateralToReturn = MathLib.multiply(qtyToRedeem, marketContract.COLLATERAL_PER_UNIT());
        contractAddressToCollateralPoolBalance[marketContractAddress] = contractAddressToCollateralPoolBalance[
            marketContractAddress
        ].subtract(collateralToReturn);

        // transfer collateral back to user
        ERC20(marketContract.COLLATERAL_TOKEN_ADDRESS()).safeTransfer(msg.sender, collateralToReturn);

        emit TokensRedeemed(
            marketContractAddress,
            qtyToRedeem,
            collateralToReturn,
            uint8(MarketSide.Both)
        );
    }

    // @notice called by a user after settlement has occurred.  This function will finalize all accounting around any
    // outstanding positions and return all remaining collateral to the caller. This should only be called after
    // settlement has occurred.
    /// @param marketContractAddress address of the MARKET Contract being traded.
    /// @param qtyToRedeem signed qtyToRedeem, positive (+) for long tokens, negative(-) for short tokens
    function settleAndClose(address marketContractAddress, int qtyToRedeem) external {
        MarketContract marketContract = MarketContract(marketContractAddress);
        require(marketContract.isSettled(), "Contract is not settled");

        // burn tokens being redeemed.
        MarketSide marketSide;
        uint absQtyToRedeem = qtyToRedeem.abs(); // convert to a uint for non signed functions
        if (qtyToRedeem > 0) {
            PositionToken(marketContract.LONG_POSITION_TOKEN()).redeemToken(absQtyToRedeem, msg.sender);
            marketSide = MarketSide.Long;
        } else {
            PositionToken(marketContract.SHORT_POSITION_TOKEN()).redeemToken(absQtyToRedeem, msg.sender);
            marketSide = MarketSide.Short;
        }

        // calculate amount of collateral to return and update pool balances
        uint collateralToReturn = MathLib.calculateNeededCollateral(
            marketContract.PRICE_FLOOR(),
            marketContract.PRICE_CAP(),
            marketContract.QTY_MULTIPLIER(),
            qtyToRedeem,
            marketContract.settlementPrice()
        );
        contractAddressToCollateralPoolBalance[marketContract] = contractAddressToCollateralPoolBalance[
        marketContract
        ].subtract(collateralToReturn);

        // return collateral tokens
        ERC20(marketContract.COLLATERAL_TOKEN_ADDRESS()).safeTransfer(msg.sender, collateralToReturn);

        emit TokensRedeemed(
            marketContractAddress,
            absQtyToRedeem,
            collateralToReturn,
            uint8(marketSide)
        );
    }

}