ETH Price: $3,416.56 (+3.15%)

Contract

0xd3aC493cd5f02A5376849ccb494F233323AABE85
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Remove Spigot183362662023-10-12 18:57:23407 days ago1697137043IN
0xd3aC493c...323AABE85
0 ETH0.0004999511.89599973
Claim Revenue183167562023-10-10 1:23:59410 days ago1696901039IN
0xd3aC493c...323AABE85
0 ETH0.000370524.90728003
Claim Revenue183093472023-10-09 0:30:11411 days ago1696811411IN
0xd3aC493c...323AABE85
0 ETH0.000471036.23840332
Update Owner181289482023-09-13 17:44:11436 days ago1694627051IN
0xd3aC493c...323AABE85
0 ETH0.0005135216.21170254
0x60806040181289152023-09-13 17:37:35436 days ago1694626655IN
 Create: Spigot
0 ETH0.0147360917.4396456

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Spigot

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
File 1 of 13 : Spigot.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {ReentrancyGuard} from "openzeppelin/security/ReentrancyGuard.sol";
import {LineLib} from "../../utils/LineLib.sol";
import {SpigotState, SpigotLib} from "../../utils/SpigotLib.sol";

import {ISpigot} from "../../interfaces/ISpigot.sol";

/**
 * @title   Credit Cooperative Spigot
 * @notice  - a contract allowing the revenue stream of a smart contract to be split between two parties, Owner and Treasury
            - operational control of revenue generating contract belongs to Spigot's Owner and delegated to Operator.
 * @dev     - Should be deployed once per agreement. Multiple revenue contracts can be attached to a Spigot.
 */
contract Spigot is ISpigot, ReentrancyGuard {
    using SpigotLib for SpigotState;

    // Stakeholder variables

    SpigotState private state;

    /**
     * @notice          - Configure data for Spigot stakeholders
     *                  - Owner/operator/treasury can all be the same address when setting up a Spigot
     * @param _owner    - An address that controls the Spigot and owns rights to some or all tokens earned by owned revenue contracts
     * @param _operator - An active address for non-Owner that can execute whitelisted functions to manage and maintain product operations
     *                  - on revenue generating contracts controlled by the Spigot.
     */
    constructor(address _owner, address _operator) {
        state.owner = _owner;
        state.operator = _operator;
    }

    function owner() external view returns (address) {
        return state.owner;
    }

    function operator() external view returns (address) {
        return state.operator;
    }

    // ##########################
    // #####   Claimoooor   #####
    // ##########################

    /**
     * @notice  - Claims revenue tokens from the Spigoted revenue contract and stores them for the Owner and Operator to withdraw later.
     *          - Accepts both push (tokens sent directly to Spigot) and pull payments (Spigot calls revenue contract to claim tokens)
     *          - Calls predefined function in contract settings to claim revenue.
     *          - Automatically sends portion to Treasury and then stores Owner and Operator shares
     *          - There is no conversion or trade of revenue tokens.
     * @dev     - Assumes the only side effect of calling claimFunc on revenueContract is we receive new tokens.
     *          - Any other side effects could be dangerous to the Spigot or upstream contracts.
     * @dev     - callable by anyone
     * @param revenueContract   - Contract with registered settings to claim revenue from
     * @param data              - Transaction data, including function signature, to properly claim revenue on revenueContract
     * @return claimed          -  The amount of revenue tokens claimed from revenueContract and split between `owner` and `treasury`
     */
    function claimRevenue(
        address revenueContract,
        address token,
        bytes calldata data
    ) external nonReentrant returns (uint256 claimed) {
        return state.claimRevenue(revenueContract, token, data);
    }

    /**
     * @notice  - Allows Spigot Owner to claim escrowed revenue tokens
     * @dev     - callable by `owner`
     * @param token     - address of revenue token that is being escrowed by spigot
     * @return claimed  -  The amount of tokens claimed by the `owner`
     */
    function claimOwnerTokens(address token) external nonReentrant returns (uint256 claimed) {
        return state.claimOwnerTokens(token);
    }

    /**
     * @notice - Allows Spigot Operqtor to claim escrowed revenue tokens
     * @dev - callable by `operator`
     * @param token - address of revenue token that is being escrowed by spigot
     * @return claimed -  The amount of tokens claimed by the `operator`
     */
    function claimOperatorTokens(address token) external nonReentrant returns (uint256 claimed) {
        return state.claimOperatorTokens(token);
    }

    // ##########################
    // ##### *ring* *ring*  #####
    // #####  OPERATOOOR    #####
    // #####  OPERATOOOR    #####
    // ##########################

    /**
     * @notice  - Allows Operator to call whitelisted functions on revenue contracts to maintain their product
     *          - while still allowing Spigot Owner to receive its revenue stream
     * @dev     - cannot call revenueContracts claim or transferOwner functions
     * @dev     - callable by `operator`
     * @param revenueContract   - contract to call. Must have existing settings added by Owner
     * @param data              - tx data, including function signature, to call contract with
     */
    function operate(address revenueContract, bytes calldata data) external returns (bool) {
        return state.operate(revenueContract, data);
    }

    // ##########################
    // #####  Maintainooor  #####
    // ##########################

    /**
     * @notice  - allows Owner to add a new revenue stream to the Spigot
     * @dev     - revenueContract cannot be address(this)
     * @dev     - callable by `owner`
     * @param revenueContract   - smart contract to claim tokens from
     * @param setting           - Spigot settings for smart contract
     */
    function addSpigot(address revenueContract, Setting memory setting) external returns (bool) {
        return state.addSpigot(revenueContract, setting);
    }

    /**

     * @notice  - Uses predefined function in revenueContract settings to transfer complete control and ownership from this Spigot to the Operator
     * @dev     - revenuContract's transfer func MUST only accept one paramteter which is the new owner's address.
     * @dev     - callable by `owner`
     * @param revenueContract - smart contract to transfer ownership of
     */
    function removeSpigot(address revenueContract) external returns (bool) {
        return state.removeSpigot(revenueContract);
    }

    /**
     * @notice  - Changes the revenue split between the Treasury and the Owner based upon the status of the Line of Credit
     *          - or otherwise if the Owner and Borrower wish to change the split.
     * @dev     - callable by `owner`
     * @param revenueContract - Address of spigoted revenue generating contract
     * @param ownerSplit - new % split to give owner
     */
    function updateOwnerSplit(address revenueContract, uint8 ownerSplit) external returns (bool) {
        return state.updateOwnerSplit(revenueContract, ownerSplit);
    }

    /**
     * @notice  - Update Owner role of Spigot contract.
     *          - New Owner receives revenue stream split and can control Spigot
     * @dev     - callable by `owner`
     * @param newOwner - Address to give control to
     */
    function updateOwner(address newOwner) external returns (bool) {
        return state.updateOwner(newOwner);
    }

    /**
     * @notice  - Update Operator role of Spigot contract.
     *          - New Operator can interact with revenue contracts.
     * @dev     - callable by `operator`
     * @param newOperator - Address to give control to
     */
    function updateOperator(address newOperator) external returns (bool) {
        return state.updateOperator(newOperator);
    }

    /**
     * @notice  - Allows Owner to whitelist function methods across all revenue contracts for Operator to call.
     *          - Can whitelist "transfer ownership" functions on revenue contracts
     *          - allowing Spigot to give direct control back to Operator.
     * @dev     - callable by `owner`
     * @param func      - smart contract function signature to whitelist
     * @param allowed   - true/false whether to allow this function to be called by Operator
     */
    function updateWhitelistedFunction(bytes4 func, bool allowed) external returns (bool) {
        return state.updateWhitelistedFunction(func, allowed);
    }

    // ##########################
    // #####   GETTOOOORS   #####
    // ##########################

    /**
     * @notice  - Retrieve amount of revenue tokens escrowed waiting for claim
     * @param token - Revenue token that is being garnished from spigots
     */
    function getOwnerTokens(address token) external view returns (uint256) {
        return state.ownerTokens[token];
    }

    /**
     * @notice - Retrieve amount of revenue tokens escrowed waiting for claim
     * @param token - Revenue token that is being garnished from spigots
     */
    function getOperatorTokens(address token) external view returns (uint256) {
        return state.operatorTokens[token];
    }

    /**
     * @notice - Returns if the function is whitelisted for an Operator to call
               - on the spigoted revenue generating smart contracts.
     * @param func - Function signature to check on whitelist
    */
    function isWhitelisted(bytes4 func) external view returns (bool) {
        return state.isWhitelisted(func);
    }

    function getSetting(address revenueContract) external view returns (uint8, bytes4, bytes4) {
        return state.getSetting(revenueContract);
    }

    receive() external payable {
        return;
    }
}

File 2 of 13 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        if (_status == _ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

File 3 of 13 : LineLib.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;
import {IInterestRateCredit} from "../interfaces/IInterestRateCredit.sol";
import {ILineOfCredit} from "../interfaces/ILineOfCredit.sol";
import {IOracle} from "../interfaces/IOracle.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {Denominations} from "chainlink/Denominations.sol";

/**
 * @title Credit Cooperative Line of Credit Library
 * @notice Core logic and variables to be reused across all Credit Cooperative Marketplace Line of Credit contracts
 */
library LineLib {
    using SafeERC20 for IERC20;

    address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // mainnet WETH

    error EthSentWithERC20();
    error TransferFailed();
    error SendingEthFailed();
    error RefundEthFailed();

    error BadToken();

    event RefundIssued(address indexed recipient, uint256 value);

    enum STATUS {
        UNINITIALIZED,
        ACTIVE,
        LIQUIDATABLE,
        REPAID,
        INSOLVENT
    }

    /**
     * @notice - Send ETH or ERC20 token from this contract to an external contract
     * @param token - address of token to send out. Denominations.ETH for raw ETH
     * @param receiver - address to send tokens to
     * @param amount - amount of tokens to send
     */
    function sendOutTokenOrETH(address token, address receiver, uint256 amount) external returns (bool) {
        if (token == address(0)) {
            revert TransferFailed();
        }

        // both branches revert if call failed
        if (token != Denominations.ETH) {
            // ERC20
            IERC20(token).safeTransfer(receiver, amount);
        } else {
            // ETH
            bool success = _safeTransferFunds(receiver, amount);
            if (!success) {
                revert SendingEthFailed();
            }
        }
        return true;
    }

    /**
     * @notice - Receive ETH or ERC20 token at this contract from an external contract
     * @dev    - If the sender overpays, the difference will be refunded to the sender
     * @dev    - If the sender is unable to receive the refund, it will be diverted to the calling contract
     * @param token - address of token to receive. Denominations.ETH for raw ETH
     * @param sender - address that is sendingtokens/ETH
     * @param amount - amount of tokens to send
     */
    function receiveTokenOrETH(address token, address sender, uint256 amount) external returns (bool) {
        if (token == address(0)) {
            revert TransferFailed();
        }
        if (token != Denominations.ETH) {
            // ERC20
            if (msg.value != 0) {
                revert EthSentWithERC20();
            }
            IERC20(token).safeTransferFrom(sender, address(this), amount);
        } else {
            // ETH
            if (msg.value < amount) {
                revert TransferFailed();
            }

            if (msg.value > amount) {
                uint256 refund = msg.value - amount;

                if (_safeTransferFunds(msg.sender, refund)) {
                    emit RefundIssued(msg.sender, refund);
                }
            }
        }
        return true;
    }

    /**
     * @notice - Helper function to get current balance of this contract for ERC20 or ETH
     * @param token - address of token to check. Denominations.ETH for raw ETH
     */
    function getBalance(address token) external view returns (uint256) {
        if (token == address(0)) return 0;
        return token != Denominations.ETH ? IERC20(token).balanceOf(address(this)) : address(this).balance;
    }

    /**
     * @notice  - Helper function to safely transfer Eth using native call
     * @dev     - Errors should be handled in the calling function
     * @param recipient - address of the recipient
     * @param value - value to be sent (in wei)
     */
    function _safeTransferFunds(address recipient, uint256 value) internal returns (bool success) {
        (success, ) = payable(recipient).call{value: value}("");
    }
}

File 4 of 13 : SpigotLib.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {ReentrancyGuard} from "openzeppelin/security/ReentrancyGuard.sol";
import {LineLib} from "../utils/LineLib.sol";
import {ISpigot} from "../interfaces/ISpigot.sol";

struct SpigotState {
    /// @notice Economic owner of Spigot revenue streams
    address owner;
    /// @notice account in charge of running onchain ops of spigoted contracts on behalf of owner
    address operator;
    /// @notice Total amount of revenue tokens help by the Spigot and available to be claimed by owner
    mapping(address => uint256) ownerTokens; // token -> claimable
    /// @notice Total amount of revenue tokens help by the Spigot and available to be claimed by operator
    mapping(address => uint256) operatorTokens; // token -> claimable
    /// @notice Functions that the operator is allowed to run on all revenue contracts controlled by the Spigot
    mapping(bytes4 => bool) whitelistedFunctions; // function -> allowed
    /// @notice Configurations for revenue contracts related to the split of revenue, access control to claiming revenue tokens and transfer of Spigot ownership
    mapping(address => ISpigot.Setting) settings; // revenue contract -> settings
}

/**
 * @notice - helper lib for Spigot
 * @dev see Spigot docs
 */
library SpigotLib {
    // Maximum numerator for Setting.ownerSplit param to ensure that the Owner can't claim more than 100% of revenue
    uint8 constant MAX_SPLIT = 100;
    // cap revenue per claim to avoid overflows on multiplication when calculating percentages
    uint256 constant MAX_REVENUE = type(uint256).max / MAX_SPLIT;

    function _claimRevenue(
        SpigotState storage self,
        address revenueContract,
        address token,
        bytes calldata data
    ) public returns (uint256 claimed) {
        if (self.settings[revenueContract].transferOwnerFunction == bytes4(0)) {
            revert InvalidRevenueContract();
        }

        uint256 existingBalance = LineLib.getBalance(token);

        if (self.settings[revenueContract].claimFunction == bytes4(0)) {
            // push payments

            // claimed = total balance - already accounted for balance
            claimed = existingBalance - self.ownerTokens[token] - self.operatorTokens[token];

            // underflow revert ensures we have more tokens than we started with and actually claimed revenue
        } else {
            // pull payments
            if (bytes4(data) != self.settings[revenueContract].claimFunction) {
                revert BadFunction();
            }
            (bool claimSuccess, ) = revenueContract.call(data);
            if (!claimSuccess) {
                revert ClaimFailed();
            }

            // claimed = total balance - existing balance
            claimed = LineLib.getBalance(token) - existingBalance;
            // underflow revert ensures we have more tokens than we started with and actually claimed revenue
        }

        if (claimed == 0) {
            revert NoRevenue();
        }

        // cap so uint doesnt overflow in split calculations.
        // can sweep by "attaching" a push payment spigot with same token
        if (claimed > MAX_REVENUE) claimed = MAX_REVENUE;

        return claimed;
    }

    /** see Spigot.claimRevenue */
    function claimRevenue(
        SpigotState storage self,
        address revenueContract,
        address token,
        bytes calldata data
    ) external returns (uint256 claimed) {
        claimed = _claimRevenue(self, revenueContract, token, data);

        // splits revenue stream according to Spigot settings
        uint256 ownerTokens = (claimed * self.settings[revenueContract].ownerSplit) / 100;
        // update escrowed balance
        self.ownerTokens[token] = self.ownerTokens[token] + ownerTokens;

        // update operator amount
        if (claimed > ownerTokens) {
            self.operatorTokens[token] = self.operatorTokens[token] + (claimed - ownerTokens);
        }

        emit ClaimRevenue(token, claimed, ownerTokens, revenueContract);

        return claimed;
    }

    /** see Spigot.operate */
    function operate(SpigotState storage self, address revenueContract, bytes calldata data) external returns (bool) {
        if (msg.sender != self.operator) {
            revert CallerAccessDenied();
        }

        // extract function signature from tx data and check whitelist
        bytes4 func = bytes4(data);

        if (!self.whitelistedFunctions[func]) {
            revert OperatorFnNotWhitelisted();
        }

        // cant claim revenue via operate() because that fucks up accounting logic. Owner shouldn't whitelist it anyway but just in case
        // also can't transfer ownership so Owner retains control of revenue contract
        if (
            func == self.settings[revenueContract].claimFunction ||
            func == self.settings[revenueContract].transferOwnerFunction
        ) {
            revert OperatorFnNotValid();
        }

        (bool success, ) = revenueContract.call(data);
        if (!success) {
            revert OperatorFnCallFailed();
        }

        return true;
    }

    /** see Spigot.claimOwnerTokens */
    function claimOwnerTokens(SpigotState storage self, address token) external returns (uint256 claimed) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }

        claimed = self.ownerTokens[token];

        if (claimed == 0) {
            revert ClaimFailed();
        }

        self.ownerTokens[token] = 0; // reset before send to prevent reentrancy

        LineLib.sendOutTokenOrETH(token, self.owner, claimed);

        emit ClaimOwnerTokens(token, claimed, self.owner);

        return claimed;
    }

    /** see Spigot.claimOperatorTokens */
    function claimOperatorTokens(SpigotState storage self, address token) external returns (uint256 claimed) {
        if (msg.sender != self.operator) {
            revert CallerAccessDenied();
        }

        claimed = self.operatorTokens[token];

        if (claimed == 0) {
            revert ClaimFailed();
        }

        self.operatorTokens[token] = 0; // reset before send to prevent reentrancy

        LineLib.sendOutTokenOrETH(token, self.operator, claimed);

        emit ClaimOperatorTokens(token, claimed, self.operator);

        return claimed;
    }

    /** see Spigot.addSpigot */
    function addSpigot(
        SpigotState storage self,
        address revenueContract,
        ISpigot.Setting memory setting
    ) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }

        if (revenueContract == address(this)) {
            revert InvalidRevenueContract();
        }

        // spigot setting already exists
        if (self.settings[revenueContract].transferOwnerFunction != bytes4(0)) {
            revert SpigotSettingsExist();
        }

        // must set transfer func
        if (setting.transferOwnerFunction == bytes4(0)) {
            revert BadSetting();
        }
        if (setting.ownerSplit > MAX_SPLIT) {
            revert BadSetting();
        }

        self.settings[revenueContract] = setting;
        emit AddSpigot(revenueContract, setting.ownerSplit, setting.claimFunction, setting.transferOwnerFunction);

        return true;
    }

    /** see Spigot.removeSpigot */
    function removeSpigot(SpigotState storage self, address revenueContract) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }

        (bool success, ) = revenueContract.call(
            abi.encodeWithSelector(
                self.settings[revenueContract].transferOwnerFunction,
                self.operator // assume function only takes one param that is new owner address
            )
        );
        require(success);

        delete self.settings[revenueContract];
        emit RemoveSpigot(revenueContract);

        return true;
    }

    /** see Spigot.updateOwnerSplit */
    function updateOwnerSplit(
        SpigotState storage self,
        address revenueContract,
        uint8 ownerSplit
    ) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }
        if (ownerSplit > MAX_SPLIT) {
            revert BadSetting();
        }

        self.settings[revenueContract].ownerSplit = ownerSplit;
        emit UpdateOwnerSplit(revenueContract, ownerSplit);

        return true;
    }

    /** see Spigot.updateOwner */
    function updateOwner(SpigotState storage self, address newOwner) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }
        require(newOwner != address(0));
        self.owner = newOwner;
        emit UpdateOwner(newOwner);
        return true;
    }

    /** see Spigot.updateOperator */
    function updateOperator(SpigotState storage self, address newOperator) external returns (bool) {
        if (msg.sender != self.operator && msg.sender != self.owner) {
            revert CallerAccessDenied();
        }
        require(newOperator != address(0));
        self.operator = newOperator;
        emit UpdateOperator(newOperator);
        return true;
    }

    /** see Spigot.updateWhitelistedFunction*/
    function updateWhitelistedFunction(SpigotState storage self, bytes4 func, bool allowed) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }
        self.whitelistedFunctions[func] = allowed;
        emit UpdateWhitelistFunction(func, allowed);
        return true;
    }

    /** see Spigot.isWhitelisted*/
    function isWhitelisted(SpigotState storage self, bytes4 func) external view returns (bool) {
        return self.whitelistedFunctions[func];
    }

    /** see Spigot.getSetting*/
    function getSetting(
        SpigotState storage self,
        address revenueContract
    ) external view returns (uint8, bytes4, bytes4) {
        return (
            self.settings[revenueContract].ownerSplit,
            self.settings[revenueContract].claimFunction,
            self.settings[revenueContract].transferOwnerFunction
        );
    }

    // Spigot Events
    event AddSpigot(address indexed revenueContract, uint256 ownerSplit, bytes4 claimFnSig, bytes4 trsfrFnSig);

    event RemoveSpigot(address indexed revenueContract);

    event UpdateWhitelistFunction(bytes4 indexed func, bool indexed allowed);

    event UpdateOwnerSplit(address indexed revenueContract, uint8 indexed split);

    event ClaimRevenue(address indexed token, uint256 indexed amount, uint256 ownerTokens, address revenueContract);

    event ClaimOwnerTokens(address indexed token, uint256 indexed amount, address owner);

    event ClaimOperatorTokens(address indexed token, uint256 indexed amount, address ooperator);

    // Stakeholder Events

    event UpdateOwner(address indexed newOwner);

    event UpdateOperator(address indexed newOperator);

    event UpdateTreasury(address indexed newTreasury);

    // Errors

    error BadFunction();

    error OperatorFnNotWhitelisted();

    error OperatorFnNotValid();

    error OperatorFnCallFailed();

    error ClaimFailed();

    error NoRevenue();

    error UnclaimedRevenue();

    error CallerAccessDenied();

    error BadSetting();

    error InvalidRevenueContract();

    error SpigotSettingsExist();
}

File 5 of 13 : ISpigot.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

interface ISpigot {
    struct Setting {
        uint8 ownerSplit; // x/100 % to Owner, rest to Operator
        bytes4 claimFunction; // function signature on contract to call and claim revenue
        bytes4 transferOwnerFunction; // function signature on contract to call and transfer ownership
    }

    // Spigot Events
    event AddSpigot(address indexed revenueContract, uint256 ownerSplit, bytes4 claimFnSig, bytes4 trsfrFnSig);

    event RemoveSpigot(address indexed revenueContract, address token);

    event UpdateWhitelistFunction(bytes4 indexed func, bool indexed allowed);

    event UpdateOwnerSplit(address indexed revenueContract, uint8 indexed split);

    event ClaimRevenue(address indexed token, uint256 indexed amount, uint256 escrowed, address revenueContract);

    event ClaimOwnerTokens(address indexed token, uint256 indexed amount, address owner);

    event ClaimOperatorTokens(address indexed token, uint256 indexed amount, address operator);

    // Stakeholder Events

    event UpdateOwner(address indexed newOwner);

    event UpdateOperator(address indexed newOperator);

    // Errors
    error BadFunction();

    error OperatorFnNotWhitelisted();

    error OperatorFnNotValid();

    error OperatorFnCallFailed();

    error ClaimFailed();

    error NoRevenue();

    error UnclaimedRevenue();

    error CallerAccessDenied();

    error BadSetting();

    error InvalidRevenueContract();

    // ops funcs

    function claimRevenue(
        address revenueContract,
        address token,
        bytes calldata data
    ) external returns (uint256 claimed);

    function operate(address revenueContract, bytes calldata data) external returns (bool);

    // owner funcs

    function claimOwnerTokens(address token) external returns (uint256 claimed);

    function claimOperatorTokens(address token) external returns (uint256 claimed);

    function addSpigot(address revenueContract, Setting memory setting) external returns (bool);

    function removeSpigot(address revenueContract) external returns (bool);

    // stakeholder funcs

    function updateOwnerSplit(address revenueContract, uint8 ownerSplit) external returns (bool);

    function updateOwner(address newOwner) external returns (bool);

    function updateOperator(address newOperator) external returns (bool);

    function updateWhitelistedFunction(bytes4 func, bool allowed) external returns (bool);

    // Getters
    function owner() external view returns (address);

    function operator() external view returns (address);

    function isWhitelisted(bytes4 func) external view returns (bool);

    function getOwnerTokens(address token) external view returns (uint256);

    function getOperatorTokens(address token) external view returns (uint256);

    function getSetting(
        address revenueContract
    ) external view returns (uint8 split, bytes4 claimFunc, bytes4 transferFunc);
}

File 6 of 13 : IInterestRateCredit.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

interface IInterestRateCredit {
    struct Rate {
        // The interest rate charged to a Borrower on borrowed / drawn down funds
        // in bps, 4 decimals
        uint128 dRate;
        // The interest rate charged to a Borrower on the remaining funds available, but not yet drawn down (rate charged on the available headroom)
        // in bps, 4 decimals
        uint128 fRate;
        // The time stamp at which accrued interest was last calculated on an ID and then added to the overall interestAccrued (interest due but not yet repaid)
        uint256 lastAccrued;
    }

    /**
     * @notice - allows `lineContract to calculate how much interest is owed since it was last calculated charged at time `lastAccrued`
     * @dev    - pure function that only calculates interest owed. Line is responsible for actually updating credit balances with returned value
     * @dev    - callable by `lineContract`
     * @param id - position id on Line to look up interest rates for
     * @param drawnBalance the balance of funds that a Borrower has drawn down on the credit line
     * @param facilityBalance the remaining balance of funds that a Borrower can still drawn down on a credit line (aka headroom)
     *
     * @return - the amount of interest to be repaid for this interest period
     */

    function accrueInterest(bytes32 id, uint256 drawnBalance, uint256 facilityBalance) external returns (uint256);

    /**
     * @notice - updates interest rates on a lender's position. Updates lastAccrued time to block.timestamp
     * @dev    - MUST call accrueInterest() on Line before changing rates. If not, lender will not accrue interest over previous interest period.
     * @dev    - callable by `line`
     * @return - if call was successful or not
     */
    function setRate(bytes32 id, uint128 dRate, uint128 fRate) external returns (bool);

    function getInterestAccrued(
        bytes32 id,
        uint256 drawnBalance,
        uint256 facilityBalance
    ) external view returns (uint256);

    function getRates(bytes32 id) external view returns (uint128, uint128);
}

File 7 of 13 : ILineOfCredit.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {LineLib} from "../utils/LineLib.sol";
import {IOracle} from "../interfaces/IOracle.sol";

interface ILineOfCredit {
    // Lender data
    struct Credit {
        //  all denominated in token, not USD
        uint256 deposit; // The total liquidity provided by a Lender in a given token on a Line of Credit
        uint256 principal; // The amount of a Lender's Deposit on a Line of Credit that has actually been drawn down by the Borrower (in Tokens)
        uint256 interestAccrued; // Interest due by a Borrower but not yet repaid to the Line of Credit contract
        uint256 interestRepaid; // Interest repaid by a Borrower to the Line of Credit contract but not yet withdrawn by a Lender
        uint8 decimals; // Decimals of Credit Token for calcs
        address token; // The token being lent out (Credit Token)
        address lender; // The person to repay
        bool isOpen; // Status of position
    }

    // General Events
    event UpdateStatus(uint256 indexed status); // store as normal uint so it can be indexed in subgraph

    event DeployLine(address indexed oracle, address indexed arbiter, address indexed borrower);

    event SortedIntoQ(bytes32 indexed id, uint256 indexed newIdx, uint256 indexed oldIdx, bytes32 oldId);

    // MutualConsent borrower/lender events

    event AddCredit(address indexed lender, address indexed token, uint256 indexed deposit, bytes32 id);
    // can only reference id once AddCredit is emitted because it will be indexed offchain

    event SetRates(bytes32 indexed id, uint128 indexed dRate, uint128 indexed fRate);

    event IncreaseCredit(bytes32 indexed id, uint256 indexed deposit);

    // Lender Events

    // Emits data re Lender removes funds (principal) - there is no corresponding function, just withdraw()
    event WithdrawDeposit(bytes32 indexed id, uint256 indexed amount);

    // Emits data re Lender withdraws interest - there is no corresponding function, just withdraw()
    event WithdrawProfit(bytes32 indexed id, uint256 indexed amount);

    // Emitted when any credit line is closed by the line's borrower or the position's lender
    event CloseCreditPosition(bytes32 indexed id);

    // After accrueInterest runs, emits the amount of interest added to a Borrower's outstanding balance of interest due
    // but not yet repaid to the Line of Credit contract
    event InterestAccrued(bytes32 indexed id, uint256 indexed amount);

    // Borrower Events

    // receive full line or drawdown on credit
    event Borrow(bytes32 indexed id, uint256 indexed amount);

    // Emits that a Borrower has repaid an amount of interest Results in an increase in interestRepaid, i.e. interest not yet withdrawn by a Lender). There is no corresponding function
    event RepayInterest(bytes32 indexed id, uint256 indexed amount);

    // Emits that a Borrower has repaid an amount of principal - there is no corresponding function
    event RepayPrincipal(bytes32 indexed id, uint256 indexed amount);

    event Default(bytes32 indexed id);

    // Access Errors
    error NotActive();
    error NotBorrowing();
    error CallerAccessDenied();

    // Tokens
    error TokenTransferFailed();
    error NoTokenPrice();

    // Line
    error BadModule(address module);
    error NoLiquidity();
    error PositionExists();
    error CloseFailedWithPrincipal();
    error NotInsolvent(address module);
    error NotLiquidatable();
    error AlreadyInitialized();
    error PositionIsClosed();
    error RepayAmountExceedsDebt(uint256 totalAvailable);
    error CantStepQ();
    error EthSupportDisabled();
    error BorrowFailed();

    // Fully public functions

    /**
     * @notice - Runs logic to ensure Line owns all modules are configured properly - collateral, interest rates, arbiter, etc.
     *          - Changes `status` from UNINITIALIZED to ACTIVE
     * @dev     - Reverts on failure to update status
     */
    function init() external;

    // MutualConsent functions

    /**
    * @notice        - On first call, creates proposed terms and emits MutualConsentRegistsered event. No position is created.
                      - On second call, creates position and stores in Line contract, sets interest rates, and starts accruing facility rate fees.
    * @dev           - Requires mutualConsent participants send EXACT same params when calling addCredit
    * @dev           - Fully executes function after a Borrower and a Lender have agreed terms, both Lender and borrower have agreed through mutualConsent
    * @dev           - callable by `lender` and `borrower`
    * @param drate   - The interest rate charged to a Borrower on borrowed / drawn down funds. In bps, 4 decimals.
    * @param frate   - The interest rate charged to a Borrower on the remaining funds available, but not yet drawn down
                        (rate charged on the available headroom). In bps, 4 decimals.
    * @param amount  - The amount of Credit Token to initially deposit by the Lender
    * @param token   - The Credit Token, i.e. the token to be lent out
    * @param lender  - The address that will manage credit line
    * @return id     - Lender's position id to look up in `credits`
  */
    function addCredit(
        uint128 drate,
        uint128 frate,
        uint256 amount,
        address token,
        address lender
    ) external payable returns (bytes32);

    /**
     * @notice           - lets Lender and Borrower update rates on the lender's position
     *                   - accrues interest before updating terms, per InterestRate docs
     *                   - can do so even when LIQUIDATABLE for the purpose of refinancing and/or renego
     * @dev              - callable by Borrower or Lender
     * @param id         - position id that we are updating
     * @param drate      - new drawn rate. In bps, 4 decimals
     * @param frate      - new facility rate. In bps, 4 decimals
     */
    function setRates(bytes32 id, uint128 drate, uint128 frate) external;

    /**
     * @notice           - Lets a Lender and a Borrower increase the credit limit on a position
     * @dev              - line status must be ACTIVE
     * @dev              - callable by borrower
     * @dev              - The function retains the `payable` designation, despite not accepting Eth via mutualConsent modifier, as a gas-optimization
     * @param id         - position id that we are updating
     * @param amount     - amount to deposit by the Lender
     */
    function increaseCredit(bytes32 id, uint256 amount) external payable;

    // Borrower functions

    /**
     * @notice       - Borrower chooses which lender position draw down on and transfers tokens from Line contract to Borrower
     * @dev          - callable by borrower
     * @param id     - the position to draw down on
     * @param amount - amount of tokens the borrower wants to withdraw
     */
    function borrow(bytes32 id, uint256 amount) external;

    /**
     * @notice       - Transfers token used in position id from msg.sender to Line contract.
     * @dev          - Available for anyone to deposit Credit Tokens to be available to be withdrawn by Lenders
     * @dev          - The function retains the `payable` designation, despite reverting with a non-zero msg.value, as a gas-optimization
     * @notice       - see LineOfCredit._repay() for more details
     * @param amount - amount of `token` in `id` to pay back
     */
    function depositAndRepay(uint256 amount) external payable;

    /**
     * @notice       - A Borrower deposits enough tokens to repay and close a credit line.
     * @dev          - callable by borrower
     * @dev          - The function retains the `payable` designation, despite reverting with a non-zero msg.value, as a gas-optimization
     */
    function depositAndClose() external payable;

    /**
     * @notice - Removes and deletes a position, preventing any more borrowing or interest.
     *         - Requires that the position principal has already been repais in full
     * @dev      - MUST repay accrued interest from facility fee during call
     * @dev - callable by `borrower` or Lender
     * @dev          - The function retains the `payable` designation, despite reverting with a non-zero msg.value, as a gas-optimization
     * @param id -the position id to be closed
     */
    function close(bytes32 id) external payable;

    // Lender functions

    /**
     * @notice - Withdraws liquidity from a Lender's position available to the Borrower.
     *         - Lender is only allowed to withdraw tokens not already lent out
     *         - Withdraws from repaid interest (profit) first and then deposit is reduced
     * @dev - can only withdraw tokens from their own position. If multiple lenders lend DAI, the lender1 can't withdraw using lender2's tokens
     * @dev - callable by Lender on `id`
     * @param id - the position id that Lender is withdrawing from
     * @param amount - amount of tokens the Lender would like to withdraw (withdrawn amount may be lower)
     */
    function withdraw(bytes32 id, uint256 amount) external;

    // Arbiter functions
    /**
     * @notice - Allow the Arbiter to signify that the Borrower is incapable of repaying debt permanently.
     *         - Recoverable funds for Lender after declaring insolvency = deposit + interestRepaid - principal
     * @dev    - Needed for onchain impairment accounting e.g. updating ERC4626 share price
     *         - MUST NOT have collateral left for call to succeed. Any collateral must already have been liquidated.
     * @dev    - Callable only by Arbiter.
     */
    function declareInsolvent() external;

    /**
     *
     * @notice - Updates accrued interest for the whole Line of Credit facility (i.e. for all credit lines)
     * @dev    - Loops over all position ids and calls related internal functions during which InterestRateCredit.sol
     *           is called with the id data and then 'interestAccrued' is updated.
     * @dev    - The related internal function _accrue() is called by other functions any time the balance on an individual
     *           credit line changes or if the interest rates of a credit line are changed by mutual consent
     *           between a Borrower and a Lender.
     */
    function accrueInterest() external;

    function healthcheck() external returns (LineLib.STATUS);

    /**
     * @notice - Cycles through position ids andselects first position with non-null principal to the zero index
     * @dev - Only works if the first element in the queue is null
     */
    function stepQ() external;

    /**
     * @notice - Returns the total debt of a Borrower across all positions for all Lenders.
     * @dev    - Denominated in USD, 8 decimals.
     * @dev    - callable by anyone
     * @return totalPrincipal - total amount of principal, in USD, owed across all positions
     * @return totalInterest - total amount of interest, in USD,  owed across all positions
     */
    function updateOutstandingDebt() external returns (uint256, uint256);

    // State getters

    function status() external returns (LineLib.STATUS);

    function borrower() external returns (address);

    function arbiter() external returns (address);

    function oracle() external returns (IOracle);

    /**
     * @notice - getter for amount of active ids + total ids in list
     * @return - (uint256, uint256) - active credit lines, total length
     */
    function counts() external view returns (uint256, uint256);

    /**
     * @notice - getter for amount of active ids + total ids in list
     * @return - (uint256, uint256) - active credit lines, total length
     */

    function interestAccrued(bytes32 id) external returns (uint256);

    /**
     * @notice - info on the next lender position that must be repaid
     * @return - (bytes32, address, address, uint, uint) - id, lender, token, principal, interestAccrued
     */
    function nextInQ() external view returns (bytes32, address, address, uint256, uint256, uint256, uint128, uint128);

    /**
     * @notice - how many tokens can be withdrawn from positions by borrower or lender
     * @return - (uint256, uint256) - remaining deposit, claimable interest
     */
    function available(bytes32 id) external returns (uint256, uint256);
}

File 8 of 13 : IOracle.sol
pragma solidity ^0.8.16;

interface IOracle {
    /** current price for token asset. denominated in USD */
    function getLatestAnswer(address token) external returns (int);

    /** Readonly function providing the current price for token asset. denominated in USD */
    function _getLatestAnswer(address token) external view returns (int);
}

File 9 of 13 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 10 of 13 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        if (nonceAfter != nonceBefore + 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

File 11 of 13 : Denominations.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library Denominations {
  address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
  address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;

  // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217
  address public constant USD = address(840);
  address public constant GBP = address(826);
  address public constant EUR = address(978);
  address public constant JPY = address(392);
  address public constant KRW = address(410);
  address public constant CNY = address(156);
  address public constant AUD = address(36);
  address public constant CAD = address(124);
  address public constant CHF = address(756);
  address public constant ARS = address(32);
  address public constant PHP = address(608);
  address public constant NZD = address(554);
  address public constant SGD = address(702);
  address public constant NGN = address(566);
  address public constant ZAR = address(710);
  address public constant RUB = address(643);
  address public constant INR = address(356);
  address public constant BRL = address(986);
}

File 12 of 13 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 13 of 13 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, defaultRevert);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with a
     * `customRevert` function as a fallback when `target` reverts.
     *
     * Requirements:
     *
     * - `customRevert` must be a reverting function.
     */
    function functionCall(
        address target,
        bytes memory data,
        function() internal view customRevert
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, customRevert);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, defaultRevert);
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with a `customRevert` function as a fallback revert reason when `target` reverts.
     *
     * Requirements:
     *
     * - `customRevert` must be a reverting function.
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        function() internal view customRevert
    ) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, customRevert);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, defaultRevert);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        function() internal view customRevert
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, customRevert);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, defaultRevert);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        function() internal view customRevert
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, customRevert);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided `customRevert`) in case of unsuccessful call or if target was not a contract.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        function() internal view customRevert
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check if target is a contract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                if (target.code.length == 0) {
                    revert AddressEmptyCode(target);
                }
            }
            return returndata;
        } else {
            _revert(returndata, customRevert);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or with a default revert error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal view returns (bytes memory) {
        return verifyCallResult(success, returndata, defaultRevert);
    }

    /**
     * @dev Same as {xref-Address-verifyCallResult-bool-bytes-}[`verifyCallResult`], but with a
     * `customRevert` function as a fallback when `success` is `false`.
     *
     * Requirements:
     *
     * - `customRevert` must be a reverting function.
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        function() internal view customRevert
    ) internal view returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, customRevert);
        }
    }

    /**
     * @dev Default reverting function when no `customRevert` is provided in a function call.
     */
    function defaultRevert() internal pure {
        revert FailedInnerCall();
    }

    function _revert(bytes memory returndata, function() internal view customRevert) private view {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            customRevert();
            revert FailedInnerCall();
        }
    }
}

Settings
{
  "remappings": [
    "chainlink/=lib/chainlink/contracts/src/v0.8/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/src/",
    "lib/openzeppelin-contracts:ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "lib/openzeppelin-contracts:erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "lib/openzeppelin-contracts:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/",
    "lib/openzeppelin-contracts:openzeppelin/=lib/openzeppelin-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {
    "contracts/utils/CreditLib.sol": {
      "CreditLib": "0x713cCD56D4353224880Bdf4aB5BC1f9eB0947a5B"
    },
    "contracts/utils/CreditListLib.sol": {
      "CreditListLib": "0x9f48ccF9BA57ce89bb50ba9960a6B7C320183F19"
    },
    "contracts/utils/EscrowLib.sol": {
      "EscrowLib": "0xA5C5f0c572c0f843d4234C6316D75fBD5A9C5bf1"
    },
    "contracts/utils/LineFactoryLib.sol": {
      "LineFactoryLib": "0x1C75806d3C160671a7620ca236C6B0203d0fCD13"
    },
    "contracts/utils/LineLib.sol": {
      "LineLib": "0xe639a9c07f39ED07F04f348D06f76dc80DC3EDeb"
    },
    "contracts/utils/SpigotLib.sol": {
      "SpigotLib": "0x87fc87EC34C290963aE55CB42B4A481Ad3850E31"
    },
    "contracts/utils/SpigotedLineLib.sol": {
      "SpigotedLineLib": "0x06e5E43210e76149996f344eDfC09D432FCaeBc9"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BadFunction","type":"error"},{"inputs":[],"name":"BadSetting","type":"error"},{"inputs":[],"name":"CallerAccessDenied","type":"error"},{"inputs":[],"name":"ClaimFailed","type":"error"},{"inputs":[],"name":"InvalidRevenueContract","type":"error"},{"inputs":[],"name":"NoRevenue","type":"error"},{"inputs":[],"name":"OperatorFnCallFailed","type":"error"},{"inputs":[],"name":"OperatorFnNotValid","type":"error"},{"inputs":[],"name":"OperatorFnNotWhitelisted","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"UnclaimedRevenue","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"revenueContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"ownerSplit","type":"uint256"},{"indexed":false,"internalType":"bytes4","name":"claimFnSig","type":"bytes4"},{"indexed":false,"internalType":"bytes4","name":"trsfrFnSig","type":"bytes4"}],"name":"AddSpigot","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"ClaimOperatorTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"ClaimOwnerTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowed","type":"uint256"},{"indexed":false,"internalType":"address","name":"revenueContract","type":"address"}],"name":"ClaimRevenue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"revenueContract","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RemoveSpigot","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOperator","type":"address"}],"name":"UpdateOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"UpdateOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"revenueContract","type":"address"},{"indexed":true,"internalType":"uint8","name":"split","type":"uint8"}],"name":"UpdateOwnerSplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"func","type":"bytes4"},{"indexed":true,"internalType":"bool","name":"allowed","type":"bool"}],"name":"UpdateWhitelistFunction","type":"event"},{"inputs":[{"internalType":"address","name":"revenueContract","type":"address"},{"components":[{"internalType":"uint8","name":"ownerSplit","type":"uint8"},{"internalType":"bytes4","name":"claimFunction","type":"bytes4"},{"internalType":"bytes4","name":"transferOwnerFunction","type":"bytes4"}],"internalType":"struct ISpigot.Setting","name":"setting","type":"tuple"}],"name":"addSpigot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"claimOperatorTokens","outputs":[{"internalType":"uint256","name":"claimed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"claimOwnerTokens","outputs":[{"internalType":"uint256","name":"claimed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"revenueContract","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"claimRevenue","outputs":[{"internalType":"uint256","name":"claimed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getOperatorTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getOwnerTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"revenueContract","type":"address"}],"name":"getSetting","outputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"bytes4","name":"","type":"bytes4"},{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"func","type":"bytes4"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"revenueContract","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"operate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"revenueContract","type":"address"}],"name":"removeSpigot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"updateOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"updateOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"revenueContract","type":"address"},{"internalType":"uint8","name":"ownerSplit","type":"uint8"}],"name":"updateOwnerSplit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"func","type":"bytes4"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"updateWhitelistedFunction","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b50604051610dd1380380610dd183398101604081905261002f91610081565b6001600081905580546001600160a01b039384166001600160a01b031991821617909155600280549290931691161790556100b4565b80516001600160a01b038116811461007c57600080fd5b919050565b6000806040838503121561009457600080fd5b61009d83610065565b91506100ab60208401610065565b90509250929050565b610d0e806100c36000396000f3fe6080604052600436106100f75760003560e01c8063880cdc311161008a578063d2ee198611610059578063d2ee1986146102f6578063d403e9ec14610316578063d63d4af014610336578063f07c57091461036c57600080fd5b8063880cdc311461024e5780638da5cb5b1461026e578063ac7475ed1461028c578063c2ae60d8146102ac57600080fd5b806353ad370d116100c657806353ad370d146101bc578063570ca735146101dc5780635beb0deb1461020e578063744790511461022e57600080fd5b806312879a52146101035780631846f4791461013857806318906bf7146101585780634e6dcd901461017857600080fd5b366100fe57005b600080fd5b34801561010f57600080fd5b5061012361011e366004610954565b61038c565b60405190151581526020015b60405180910390f35b34801561014457600080fd5b5061012361015336600461098b565b610423565b34801561016457600080fd5b506101236101733660046109bc565b6104b0565b34801561018457600080fd5b506101ae61019336600461098b565b6001600160a01b031660009081526004602052604090205490565b60405190815260200161012f565b3480156101c857600080fd5b506101236101d7366004610aac565b610527565b3480156101e857600080fd5b506002546001600160a01b03165b6040516001600160a01b03909116815260200161012f565b34801561021a57600080fd5b506101ae61022936600461098b565b6105b1565b34801561023a57600080fd5b506101ae610249366004610aff565b610650565b34801561025a57600080fd5b5061012361026936600461098b565b6106e7565b34801561027a57600080fd5b506001546001600160a01b03166101f6565b34801561029857600080fd5b506101236102a736600461098b565b610731565b3480156102b857600080fd5b506102cc6102c736600461098b565b61077b565b6040805160ff90941684526001600160e01b0319928316602085015291169082015260600161012f565b34801561030257600080fd5b50610123610311366004610b60565b610812565b34801561032257600080fd5b50610123610331366004610b8b565b61085d565b34801561034257600080fd5b506101ae61035136600461098b565b6001600160a01b031660009081526003602052604090205490565b34801561037857600080fd5b506101ae61038736600461098b565b6108b0565b6040516324c4684d60e21b8152600160048201526001600160a01b038316602482015260ff821660448201526000907387fc87ec34c290963ae55cb42b4a481ad3850e3190639311a134906064015b602060405180830381865af41580156103f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041c9190610bb9565b9392505050565b60405163d72efba960e01b8152600160048201526001600160a01b03821660248201526000907387fc87ec34c290963ae55cb42b4a481ad3850e319063d72efba9906044015b602060405180830381865af4158015610486573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104aa9190610bb9565b92915050565b6040805163f51fdc4b60e01b8152600160048201526001600160a01b0384166024820152825160ff16604482015260208301516001600160e01b031990811660648301529183015190911660848201526000907387fc87ec34c290963ae55cb42b4a481ad3850e319063f51fdc4b9060a4016103db565b604051637f6b516f60e01b81526000907387fc87ec34c290963ae55cb42b4a481ad3850e3190637f6b516f9061056890600190889088908890600401610bff565b602060405180830381865af4158015610585573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a99190610bb9565b949350505050565b60006105bb610901565b60405163eacd6e0960e01b8152600160048201526001600160a01b03831660248201527387fc87ec34c290963ae55cb42b4a481ad3850e319063eacd6e09906044015b602060405180830381865af415801561061b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063f9190610c34565b905061064b6001600055565b919050565b600061065a610901565b604051637796f76360e01b81527387fc87ec34c290963ae55cb42b4a481ad3850e3190637796f7639061069a906001908990899089908990600401610c4d565b602060405180830381865af41580156106b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106db9190610c34565b90506105a96001600055565b604051633f279f2760e11b8152600160048201526001600160a01b03821660248201526000907387fc87ec34c290963ae55cb42b4a481ad3850e3190637e4f3e4e90604401610469565b60405163939ee34160e01b8152600160048201526001600160a01b03821660248201526000907387fc87ec34c290963ae55cb42b4a481ad3850e319063939ee34190604401610469565b604051634f94f74160e01b8152600160048201526001600160a01b0382166024820152600090819081907387fc87ec34c290963ae55cb42b4a481ad3850e3190634f94f74190604401606060405180830381865af41580156107e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108059190610c8b565b9250925092509193909250565b6040516320a7a3e560e01b8152600160048201526001600160e01b0319821660248201526000907387fc87ec34c290963ae55cb42b4a481ad3850e31906320a7a3e590604401610469565b604051633f28cbf160e01b8152600160048201526001600160e01b03198316602482015281151560448201526000907387fc87ec34c290963ae55cb42b4a481ad3850e3190633f28cbf1906064016103db565b60006108ba610901565b604051631bb69fcd60e21b8152600160048201526001600160a01b03831660248201527387fc87ec34c290963ae55cb42b4a481ad3850e3190636eda7f34906044016105fe565b60026000540361092457604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b80356001600160a01b038116811461064b57600080fd5b60ff8116811461095157600080fd5b50565b6000806040838503121561096757600080fd5b6109708361092b565b9150602083013561098081610942565b809150509250929050565b60006020828403121561099d57600080fd5b61041c8261092b565b6001600160e01b03198116811461095157600080fd5b60008082840360808112156109d057600080fd5b6109d98461092b565b92506060601f19820112156109ed57600080fd5b506040516060810181811067ffffffffffffffff82111715610a1f57634e487b7160e01b600052604160045260246000fd5b6040526020840135610a3081610942565b81526040840135610a40816109a6565b60208201526060840135610a53816109a6565b6040820152919491935090915050565b60008083601f840112610a7557600080fd5b50813567ffffffffffffffff811115610a8d57600080fd5b602083019150836020828501011115610aa557600080fd5b9250929050565b600080600060408486031215610ac157600080fd5b610aca8461092b565b9250602084013567ffffffffffffffff811115610ae657600080fd5b610af286828701610a63565b9497909650939450505050565b60008060008060608587031215610b1557600080fd5b610b1e8561092b565b9350610b2c6020860161092b565b9250604085013567ffffffffffffffff811115610b4857600080fd5b610b5487828801610a63565b95989497509550505050565b600060208284031215610b7257600080fd5b813561041c816109a6565b801515811461095157600080fd5b60008060408385031215610b9e57600080fd5b8235610ba9816109a6565b9150602083013561098081610b7d565b600060208284031215610bcb57600080fd5b815161041c81610b7d565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481526001600160a01b0384166020820152606060408201819052600090610c2a9083018486610bd6565b9695505050505050565b600060208284031215610c4657600080fd5b5051919050565b8581526001600160a01b03858116602083015284166040820152608060608201819052600090610c809083018486610bd6565b979650505050505050565b600080600060608486031215610ca057600080fd5b8351610cab81610942565b6020850151909350610cbc816109a6565b6040850151909250610ccd816109a6565b80915050925092509256fea2646970667358221220997a95047870c17d6f3def59fc76e39091a08a0cba2dbf0c73353bd78b802e8464736f6c6343000814003300000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4700000000000000000000000098e37f18527aee67b0f73bf791a67cc7a9b40db5

Deployed Bytecode

0x6080604052600436106100f75760003560e01c8063880cdc311161008a578063d2ee198611610059578063d2ee1986146102f6578063d403e9ec14610316578063d63d4af014610336578063f07c57091461036c57600080fd5b8063880cdc311461024e5780638da5cb5b1461026e578063ac7475ed1461028c578063c2ae60d8146102ac57600080fd5b806353ad370d116100c657806353ad370d146101bc578063570ca735146101dc5780635beb0deb1461020e578063744790511461022e57600080fd5b806312879a52146101035780631846f4791461013857806318906bf7146101585780634e6dcd901461017857600080fd5b366100fe57005b600080fd5b34801561010f57600080fd5b5061012361011e366004610954565b61038c565b60405190151581526020015b60405180910390f35b34801561014457600080fd5b5061012361015336600461098b565b610423565b34801561016457600080fd5b506101236101733660046109bc565b6104b0565b34801561018457600080fd5b506101ae61019336600461098b565b6001600160a01b031660009081526004602052604090205490565b60405190815260200161012f565b3480156101c857600080fd5b506101236101d7366004610aac565b610527565b3480156101e857600080fd5b506002546001600160a01b03165b6040516001600160a01b03909116815260200161012f565b34801561021a57600080fd5b506101ae61022936600461098b565b6105b1565b34801561023a57600080fd5b506101ae610249366004610aff565b610650565b34801561025a57600080fd5b5061012361026936600461098b565b6106e7565b34801561027a57600080fd5b506001546001600160a01b03166101f6565b34801561029857600080fd5b506101236102a736600461098b565b610731565b3480156102b857600080fd5b506102cc6102c736600461098b565b61077b565b6040805160ff90941684526001600160e01b0319928316602085015291169082015260600161012f565b34801561030257600080fd5b50610123610311366004610b60565b610812565b34801561032257600080fd5b50610123610331366004610b8b565b61085d565b34801561034257600080fd5b506101ae61035136600461098b565b6001600160a01b031660009081526003602052604090205490565b34801561037857600080fd5b506101ae61038736600461098b565b6108b0565b6040516324c4684d60e21b8152600160048201526001600160a01b038316602482015260ff821660448201526000907387fc87ec34c290963ae55cb42b4a481ad3850e3190639311a134906064015b602060405180830381865af41580156103f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041c9190610bb9565b9392505050565b60405163d72efba960e01b8152600160048201526001600160a01b03821660248201526000907387fc87ec34c290963ae55cb42b4a481ad3850e319063d72efba9906044015b602060405180830381865af4158015610486573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104aa9190610bb9565b92915050565b6040805163f51fdc4b60e01b8152600160048201526001600160a01b0384166024820152825160ff16604482015260208301516001600160e01b031990811660648301529183015190911660848201526000907387fc87ec34c290963ae55cb42b4a481ad3850e319063f51fdc4b9060a4016103db565b604051637f6b516f60e01b81526000907387fc87ec34c290963ae55cb42b4a481ad3850e3190637f6b516f9061056890600190889088908890600401610bff565b602060405180830381865af4158015610585573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a99190610bb9565b949350505050565b60006105bb610901565b60405163eacd6e0960e01b8152600160048201526001600160a01b03831660248201527387fc87ec34c290963ae55cb42b4a481ad3850e319063eacd6e09906044015b602060405180830381865af415801561061b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063f9190610c34565b905061064b6001600055565b919050565b600061065a610901565b604051637796f76360e01b81527387fc87ec34c290963ae55cb42b4a481ad3850e3190637796f7639061069a906001908990899089908990600401610c4d565b602060405180830381865af41580156106b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106db9190610c34565b90506105a96001600055565b604051633f279f2760e11b8152600160048201526001600160a01b03821660248201526000907387fc87ec34c290963ae55cb42b4a481ad3850e3190637e4f3e4e90604401610469565b60405163939ee34160e01b8152600160048201526001600160a01b03821660248201526000907387fc87ec34c290963ae55cb42b4a481ad3850e319063939ee34190604401610469565b604051634f94f74160e01b8152600160048201526001600160a01b0382166024820152600090819081907387fc87ec34c290963ae55cb42b4a481ad3850e3190634f94f74190604401606060405180830381865af41580156107e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108059190610c8b565b9250925092509193909250565b6040516320a7a3e560e01b8152600160048201526001600160e01b0319821660248201526000907387fc87ec34c290963ae55cb42b4a481ad3850e31906320a7a3e590604401610469565b604051633f28cbf160e01b8152600160048201526001600160e01b03198316602482015281151560448201526000907387fc87ec34c290963ae55cb42b4a481ad3850e3190633f28cbf1906064016103db565b60006108ba610901565b604051631bb69fcd60e21b8152600160048201526001600160a01b03831660248201527387fc87ec34c290963ae55cb42b4a481ad3850e3190636eda7f34906044016105fe565b60026000540361092457604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b80356001600160a01b038116811461064b57600080fd5b60ff8116811461095157600080fd5b50565b6000806040838503121561096757600080fd5b6109708361092b565b9150602083013561098081610942565b809150509250929050565b60006020828403121561099d57600080fd5b61041c8261092b565b6001600160e01b03198116811461095157600080fd5b60008082840360808112156109d057600080fd5b6109d98461092b565b92506060601f19820112156109ed57600080fd5b506040516060810181811067ffffffffffffffff82111715610a1f57634e487b7160e01b600052604160045260246000fd5b6040526020840135610a3081610942565b81526040840135610a40816109a6565b60208201526060840135610a53816109a6565b6040820152919491935090915050565b60008083601f840112610a7557600080fd5b50813567ffffffffffffffff811115610a8d57600080fd5b602083019150836020828501011115610aa557600080fd5b9250929050565b600080600060408486031215610ac157600080fd5b610aca8461092b565b9250602084013567ffffffffffffffff811115610ae657600080fd5b610af286828701610a63565b9497909650939450505050565b60008060008060608587031215610b1557600080fd5b610b1e8561092b565b9350610b2c6020860161092b565b9250604085013567ffffffffffffffff811115610b4857600080fd5b610b5487828801610a63565b95989497509550505050565b600060208284031215610b7257600080fd5b813561041c816109a6565b801515811461095157600080fd5b60008060408385031215610b9e57600080fd5b8235610ba9816109a6565b9150602083013561098081610b7d565b600060208284031215610bcb57600080fd5b815161041c81610b7d565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481526001600160a01b0384166020820152606060408201819052600090610c2a9083018486610bd6565b9695505050505050565b600060208284031215610c4657600080fd5b5051919050565b8581526001600160a01b03858116602083015284166040820152608060608201819052600090610c809083018486610bd6565b979650505050505050565b600080600060608486031215610ca057600080fd5b8351610cab81610942565b6020850151909350610cbc816109a6565b6040850151909250610ccd816109a6565b80915050925092509256fea2646970667358221220997a95047870c17d6f3def59fc76e39091a08a0cba2dbf0c73353bd78b802e8464736f6c63430008140033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4700000000000000000000000098e37f18527aee67b0f73bf791a67cc7a9b40db5

-----Decoded View---------------
Arg [0] : _owner (address): 0x06dae7Ba3958EF288adB0B9b3732eC204E48BC47
Arg [1] : _operator (address): 0x98e37F18527aEE67B0F73bF791a67cC7a9b40db5

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47
Arg [1] : 00000000000000000000000098e37f18527aee67b0f73bf791a67cc7a9b40db5


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.