Forwarder

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/Forwarder.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {USDG} from "@/USDG.sol";
import {CounterfactualSwapper} from "@/CounterfactualSwapper.sol";
import {CounterfactualHolderFactory} from "@/v2/CounterfactualHolderFactory.sol";

contract Forwarder is ReentrancyGuard {
    using SafeERC20 for IERC20;

    error MaxLengthExceeded();
    error ZeroAmount();

    uint256 private constant _MAX_LENGTH = 400;

    USDG public immutable i_USDG;
    IERC20 public immutable i_USDC;
    CounterfactualHolderFactory public immutable i_CFHFactory;
    uint256 public nextNonce;

    constructor(USDG _usdg, IERC20 _usdc, CounterfactualHolderFactory _cfhFactory) payable {
        i_USDG = _usdg;
        i_USDC = _usdc;
        i_CFHFactory = _cfhFactory;
    }

    event Forward(address indexed from, address indexed to, address indexed token, uint256 amount, string message);

    function forward(
        address token,
        address to,
        uint256 amount,
        bool sendToCounterfactualWallet,
        string calldata message
    ) external nonReentrant {
        _checkAmountAndLength(amount, message);
        address whoToSendTokensTo =
            sendToCounterfactualWallet ? i_CFHFactory.getCurrentCFH({user: to, token: token}) : to;
        SafeERC20.safeTransferFrom(IERC20(token), msg.sender, whoToSendTokensTo, amount);
        emit Forward(msg.sender, to, token, amount, message);
    }

    /// @dev Swaps USDC for USDG and forwards the USDG to the recipient.
    /// @dev USDG has a `guard` that does not allow it to be transferred to a non-approved contract.
    /// This contract is not approved. We use a counterfactual swapper to swap USDC for USDG.
    /// The counterfactual swapper performs a swap for USDC -> USDG and then forwards the USDG to the recipient in its constructor
    /// This works because contracts have no bytecode during construction.
    /// @param amount The amount of USDC to swap.
    /// @param to The address to forward the USDG to.
    /// @param message The message to forward.
    function swapUSDCAndForwardUSDG(
        uint256 amount,
        address to,
        bool sendToCounterfactualWallet,
        string calldata message
    ) external nonReentrant {
        _checkAmountAndLength(amount, message);
        address whoToSendUSDGTo =
            sendToCounterfactualWallet ? i_CFHFactory.getCurrentCFH({user: to, token: address(i_USDG)}) : to;
        uint256 nonce = nextNonce;
        address counterfactualSwapper = _predictCounterfactualSwapper(nonce, amount, whoToSendUSDGTo);

        i_USDC.safeTransferFrom(msg.sender, counterfactualSwapper, amount);

        new CounterfactualSwapper{salt: bytes32(nonce)}(i_USDG, i_USDC, amount, whoToSendUSDGTo);

        nextNonce = nonce + 1;

        emit Forward(msg.sender, to, address(i_USDG), amount, message);
    }

    /// @dev Predict the CREATE2 address for CounterfactualSwapper with the given salt/args.
    function _predictCounterfactualSwapper(uint256 nonce, uint256 amount, address to)
        internal
        view
        returns (address predicted)
    {
        bytes32 salt = bytes32(nonce);

        // init code = creationCode ++ abi.encode(constructor args)
        bytes memory initCode =
            abi.encodePacked(type(CounterfactualSwapper).creationCode, abi.encode(i_USDG, i_USDC, amount, to));

        bytes32 initCodeHash = keccak256(initCode);

        // EIP-1014: keccak256(0xff ++ deployer ++ salt ++ keccak256(init_code))[12:]
        bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash));

        predicted = address(uint160(uint256(hash)));
    }

    function _checkAmountAndLength(uint256 amount, string calldata message) internal pure {
        if (amount == 0) {
            revert ZeroAmount();
        }
        if (bytes(message).length > _MAX_LENGTH) {
            revert MaxLengthExceeded();
        }
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// 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);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// 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;
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol": {
      "content": "// 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;
    }
}
"
    },
    "src/USDG.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {UniswapV2Library} from "@/libraries/UniswapV2Library.sol";
import {IVetoCouncil} from "@/interfaces/IVetoCouncil.sol";
import {Glow} from "@/GLOW.sol";
/**
 * @title USDG
 * @notice A contract for swapping USDC for USDG
 *         - the contract takes in USDC and mints USDG
 *         - the contract can only be used EOA's and by allowlisted contracts
 *         - Allow listed contracts include Core Glow Contracts and a GCC/USDG Pair
 *         - USDG is part of the Glow Protocol's Guarded Launch program.
 *         - After the Glow Protocol's Guarded Launch program, USDG will be replaced with USDC
 */

contract USDG is ERC20Permit, Ownable {
    error ErrIsContract();
    error ErrNotVetoCouncilMember();
    error ErrPermanentlyFrozen();
    error ToCannotBeUSDCReceiver();
    error ErrCannotSwapZero();

    /* -------------------------------------------------------------------------- */
    /*                                  immutables                                */
    /* -------------------------------------------------------------------------- */
    /**
     * @notice the USDC token
     */
    ERC20Permit public immutable USDC;

    /**
     * @notice the address to receive USDC
     */
    address public immutable USDC_RECEIVER;

    /**
     * @notice the uniswap v2 factory
     */
    address public immutable UNISWAP_V2_FACTORY;

    /**
     * @notice the veto council contract
     */
    IVetoCouncil public immutable vetoCouncilContract;

    /* -------------------------------------------------------------------------- */
    /*                                 state vars                                */
    /* -------------------------------------------------------------------------- */

    /**
     * @notice if true, transfers are permanently frozen
     * @dev - only veto council agents can set this to true
     */
    bool public permanentlyFreezeTransfers;

    /* -------------------------------------------------------------------------- */
    /*                                   mappings                                  */
    /* -------------------------------------------------------------------------- */
    /**
     * @notice the list of contracts that can receive USDG
     * @dev contracts must be added to this list before they can receive or send USDG
     *             - EOA's can always receive and send USDG
     */
    mapping(address => bool) public allowlistedContracts;

    /* -------------------------------------------------------------------------- */
    /*                                   events                                   */
    /* -------------------------------------------------------------------------- */

    /**
     * @notice Emitted when the contract is permanently frozen
     */
    event PermanentFreeze();

    /* -------------------------------------------------------------------------- */
    /*                                 constructor                                */
    /* -------------------------------------------------------------------------- */

    /**
     * @param _usdc the USDC token
     * @param _usdcReceiver the address to receive USDC from the `swap` function
     * @param _owner the owner of the contract
     * @param _univ2Factory the uniswap v2 factory
     * @param _glow the glow token
     * @param _gcc the gcc token
     * @param _holdingContract the holding contract
     * @param _vetoCouncilContract the veto council contract
     * @param _impactCatalyst the impact catalyst contract
     */
    constructor(
        address _usdc,
        address _usdcReceiver,
        address _owner,
        address _univ2Factory,
        address _glow,
        address _gcc,
        address _holdingContract,
        address _vetoCouncilContract,
        address _impactCatalyst
    ) payable Ownable(_owner) ERC20("Guarded USDC ", "USDG-GLOW") ERC20Permit("Guarded USDC") {
        USDC = ERC20Permit(_usdc);
        USDC_RECEIVER = _usdcReceiver;
        UNISWAP_V2_FACTORY = _univ2Factory;
        allowlistedContracts[_usdcReceiver] = true;
        allowlistedContracts[_holdingContract] = true;
        //Allowlist the glow/usdg and the gcc/usdg pair
        address glowUSDGPair = getPair(UNISWAP_V2_FACTORY, address(this), _glow);
        allowlistedContracts[glowUSDGPair] = true;
        address gccUSDGPair = getPair(UNISWAP_V2_FACTORY, address(this), _gcc);
        allowlistedContracts[gccUSDGPair] = true;
        vetoCouncilContract = IVetoCouncil(_vetoCouncilContract);
        allowlistedContracts[_impactCatalyst] = true;
    }

    /* -------------------------------------------------------------------------- */
    /*                                     mint                                   */
    /* -------------------------------------------------------------------------- */

    /**
     * @notice Mints USDG {to}
     * @param to address to mint USDG
     * @param amount amount of USDG to mint
     * @dev only allowlisted contracts and EOA's can mint USDG
     * @dev USDG is minted 1:1 with USDC
     */
    function swap(address to, uint256 amount) public {
        if (to == USDC_RECEIVER) revert ToCannotBeUSDCReceiver();
        if (amount == 0) revert ErrCannotSwapZero();
        uint256 balBefore = USDC.balanceOf(USDC_RECEIVER);
        USDC.transferFrom(msg.sender, USDC_RECEIVER, amount);
        uint256 balAfter = USDC.balanceOf(USDC_RECEIVER);
        amount = balAfter - balBefore;
        _mint(to, amount);
    }

    /* -------------------------------------------------------------------------- */
    /*                                  veto council                              */
    /* -------------------------------------------------------------------------- */

    /**
     * @notice Freezes transfers permanently
     * @dev only veto council members can call this function
     * @dev after this function is called, all transfers are permanently frozen
     */
    function freezeContract() external {
        if (!vetoCouncilContract.isCouncilMember(msg.sender)) {
            revert ErrNotVetoCouncilMember();
        }
        permanentlyFreezeTransfers = true;
        emit PermanentFreeze();
    }

    /* -------------------------------------------------------------------------- */
    /*                                  overrides                                 */
    /* -------------------------------------------------------------------------- */

    /**
     * @notice the decimals of USDG
     * @dev matches the decimals of USDC
     * @return decimals - the decimals of USDG
     */
    function decimals() public view virtual override returns (uint8) {
        return 6;
    }

    /**
     * @dev override transfers to make sure that only EOA's and allowlisted contracts can send or receive USDG
     * @param from the address to send USDG from
     * @param to the address to send USDG to
     * @param value the amount of USDG to send
     */
    function _update(address from, address to, uint256 value) internal override(ERC20) {
        if (permanentlyFreezeTransfers) {
            revert ErrPermanentlyFrozen();
        }
        _revertIfNotAllowlistedContract(from);
        _revertIfNotAllowlistedContract(to);
        super._update(from, to, value);
    }

    /**
     * @dev reverts if the address is a contract and not allowlisted
     */
    function _revertIfNotAllowlistedContract(address _address) internal view {
        if (_isContract(_address)) {
            if (!allowlistedContracts[_address]) {
                revert ErrIsContract();
            }
        }
    }

    /**
     * @dev returns true if the address is a contract
     * @param _address the address to check
     * @return isContract - true if the address is a contract
     */
    function _isContract(address _address) internal view returns (bool isContract) {
        assembly {
            isContract := gt(extcodesize(_address), 0)
        }
    }

    /**
     * @notice Returns the univ2 pair for a given factory and token
     * @param factory the univ2 factory
     * @param _tokenA the first token
     * @param _tokenB the second token
     * @return pair - the univ2 pair
     */
    function getPair(address factory, address _tokenA, address _tokenB) internal view virtual returns (address pair) {
        pair = UniswapV2Library.pairFor(factory, _tokenA, _tokenB);
    }
}
"
    },
    "src/CounterfactualSwapper.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {USDG} from "@/USDG.sol";

contract CounterfactualSwapper {
    constructor(USDG _usdg, IERC20 _usdc, uint256 amount, address to) payable {
        _usdc.approve(address(_usdg), type(uint256).max);
        _usdg.swap(to, amount);
    }
}
"
    },
    "src/v2/CounterfactualHolderFactory.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {CounterfactualHolder} from "./CounterfactualHolder.sol";
import {Call} from "./Structs.sol";
import {TransientBytes} from "./utils/TransientBytes/TransientBytes.sol";
import {ICounterfactualHolderFactory} from "./ICounterfactualHolderFactory.sol";
import {TransientSlot} from "./utils/TransientBytes/TransientSlot.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract CounterfactualHolderFactory is ICounterfactualHolderFactory, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using TransientBytes for *;
    using TransientSlot for *;

    error NotApproved(address from, address operator);

    event TransferToCFH(
        address indexed from, address indexed toUser, address indexed token, address cfh, uint256 amount
    );

    event Execute(address indexed user, address indexed cfh, address indexed token, Call[] calls);
    event Approval(address indexed from, address indexed operator, bool status);

    struct UserTokenData {
        uint256 nextSalt;
    }

    mapping(address user => mapping(address token => UserTokenData)) public userTokenData;
    mapping(address owner => mapping(address operator => bool status)) public approvals;

    function transferCFHToCFH(address toUser, address token, uint256 amount) external nonReentrant {
        _executeCFHTransfer(msg.sender, toUser, token, amount);
    }

    function transferFromCFHToCFH(address fromUser, address toUser, address token, uint256 amount)
        external
        nonReentrant
    {
        if (!isApproved(fromUser, msg.sender)) {
            revert NotApproved(fromUser, msg.sender);
        }
        _executeCFHTransfer(fromUser, toUser, token, amount);
    }

    function _executeCFHTransfer(address fromUser, address toUser, address token, uint256 amount) internal {
        UserTokenData storage d = userTokenData[toUser][token];
        address currentHolder = _predictCFH(token, deriveUserNonce(toUser, token, d.nextSalt));

        Call[] memory calls = new Call[](1);
        calls[0] = Call({
            target: address(token),
            data: abi.encodeWithSelector(IERC20.transfer.selector, currentHolder, amount)
        });
        _execute(fromUser, token, calls);

        emit TransferToCFH(fromUser, toUser, token, currentHolder, amount);
    }

    function transferToCFH(address user, address token, uint256 amount) external nonReentrant {
        UserTokenData storage d = userTokenData[user][token];
        address currentHolder = _predictCFH(token, deriveUserNonce(user, token, d.nextSalt));
        IERC20(token).safeTransferFrom(msg.sender, currentHolder, amount);
        emit TransferToCFH(msg.sender, user, token, currentHolder, amount);
    }

    function executeFrom(address from, address token, Call[] memory calls) external nonReentrant {
        if (!isApproved(from, msg.sender)) {
            revert NotApproved(from, msg.sender);
        }

        _execute(from, token, calls);
    }

    function execute(address token, Call[] memory calls) external nonReentrant {
        _execute(msg.sender, token, calls);
    }

    function setApprovalStatus(address operator, bool status) external {
        approvals[msg.sender][operator] = status;
        emit Approval(msg.sender, operator, status);
    }

    function _execute(address from, address token, Call[] memory calls) internal {
        bytes32 baseCallsSlot = deriveCallsBaseSlot();
        bytes memory dataCalls = abi.encode(calls);
        baseCallsSlot.tstoreBytes(dataCalls);

        UserTokenData storage d = userTokenData[from][token];

        uint256 nextSalt = d.nextSalt;
        address nextHolder = _predictCFH(token, deriveUserNonce(from, token, nextSalt + 1));
        bytes32 baseNextHolderSlot = deriveNextHolderBaseSlot();
        baseNextHolderSlot.asAddress().tstore(nextHolder);

        bytes32 nonce = deriveUserNonce(from, token, nextSalt);
        address cfh = address(new CounterfactualHolder{salt: nonce}(IERC20(token)));
        d.nextSalt = nextSalt + 1;

        emit Execute(from, cfh, token, calls);
    }

    function isApproved(address from, address operator) public view returns (bool) {
        return approvals[from][operator];
    }

    function getCurrentCFH(address user, address token) public view returns (address) {
        UserTokenData storage d = userTokenData[user][token];
        return _predictCFH(token, deriveUserNonce(user, token, d.nextSalt));
    }

    function balanceOfCFH(address user, address token) external view returns (uint256) {
        return IERC20(token).balanceOf(getCurrentCFH(user, token));
    }

    function deriveUserNonce(address user, address token, uint256 nonce) internal view returns (bytes32) {
        return keccak256(abi.encodePacked(user, token, nonce, address(this)));
    }

    function getTransientCalls() external view returns (Call[] memory) {
        bytes32 baseCallsSlot = deriveCallsBaseSlot();
        bytes memory dataCalls = baseCallsSlot.tloadBytes();
        return abi.decode(dataCalls, (Call[]));
    }

    function getTransientNextHolder() external view returns (address) {
        bytes32 baseNextHolderSlot = deriveNextHolderBaseSlot();
        return baseNextHolderSlot.asAddress().tload();
    }

    function deriveCallsBaseSlot() internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("CALLS"));
    }

    function deriveNextHolderBaseSlot() internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("NEXT_HOLDER"));
    }

    /// @dev Predict the create2
    function _predictCFH(address token, bytes32 salt) internal view returns (address currentHolder) {
        bytes32 initCodeHash = keccak256(abi.encodePacked(type(CounterfactualHolder).creationCode, abi.encode(token)));
        // EIP-1014: keccak256(0xff ++ deployer ++ salt ++ keccak256(init_code))[12:]
        bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash));

        currentHolder = address(uint160(uint256(hash)));
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol": {
      "content": "// 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);
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/Address.sol": {
      "content": "// 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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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);
    }

    /**
     * @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) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // 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 (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

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

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // 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 {
            revert FailedInnerCall();
        }
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC20Permit.sol)

pragma solidity ^0.8.20;

import {IERC20Permit} from "./IERC20Permit.sol";
import {ERC20} from "../ERC20.sol";
import {ECDSA} from "../../../utils/cryptography/ECDSA.sol";
import {EIP712} from "../../../utils/cryptography/EIP712.sol";
import {Nonces} from "../../../utils/Nonces.sol";

/**
 * @dev Implementation 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.
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces {
    // solhint-disable-next-line var-name-mixedcase
    bytes32 private constant _PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    /**
     * @dev Permit deadline has expired.
     */
    error ERC2612ExpiredSignature(uint256 deadline);

    /**
     * @dev Mismatched signature.
     */
    error ERC2612InvalidSigner(address signer, address owner);

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @dev See {IERC20Permit-permit}.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        if (block.timestamp > deadline) {
            revert ERC2612ExpiredSignature(deadline);
        }

        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        if (signer != owner) {
            revert ERC2612InvalidSigner(signer, owner);
        }

        _approve(owner, spender, value);
    }

    /**
     * @dev See {IERC20Permit-nonces}.
     */
    function nonces(address owner) public view virtual override(IERC20Permit, Nonces) returns (uint256) {
        return super.nonces(owner);
    }

    /**
     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
        return _domainSeparatorV4();
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
"
    },
    "src/libraries/UniswapV2Library.sol": {
      "content": "pragma solidity ^0.8.19;

import {IUniswapV2Pair} from "@/interfaces/IUniswapV2Pair.sol";

library UniswapV2Library {
    // returns sorted token addresses, used to handle return values from pairs sorted in this order
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
    }

    // calculates the CREATE2 address for a pair without making any external calls
    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex"ff",
                            factory,
                            keccak256(abi.encodePacked(token0, token1)),
                            hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash
                        )
                    )
                )
            )
        );
    }

    // fetches and sorts the reserves for a pair
    function getReserves(address factory, address tokenA, address tokenB)
        internal
        view
        returns (uint256 reserveA, uint256 reserveB)
    {
        (address token0,) = sortTokens(tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
    function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) internal pure returns (uint256 amountB) {
        require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT");
        require(reserveA > 0 && reserveB > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
        amountB = amountA * (reserveB) / reserveA;
    }

    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
        internal
        pure
        returns (uint256 amountOut)
    {
        require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
        uint256 amountInWithFee = amountIn * (997);
        uint256 numerator = amountInWithFee * (reserveOut);
        uint256 denominator = reserveIn * (1000) + (amountInWithFee);
        amountOut = numerator / denominator;
    }

    // given an output amount of an asset and pair reserx   ves, returns a required input amount of the other asset
    function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)
        internal
        pure
        returns (uint256 amountIn)
    {
        require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
        uint256 numerator = reserveIn * (amountOut) * (1000);
        uint256 denominator = reserveOut - (amountOut) * (997);
        amountIn = (numerator / denominator) + (1);
    }

    // // performs chained getAmountOut calculations on any number of pairs
    // function getAmountsOut(address factory, uint256 amountIn, address[] memory path)
    //     internal
    //     view
    //     returns (uint256[] memory amounts)
    // {
    //     require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
    //     amounts = new uint[](path.length);
    //     amounts[0] = amountIn;
    //     for (uint256 i; i < path.length - 1; i++) {
    //         (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i], path[i + 1]);
    //         amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
    //     }
    // }

    // // performs chained getAmountIn calculations on any number of pairs
    // function getAmountsIn(address factory, uint256 amountOut, address[] memory path)
    //     internal
    //     view
    //     returns (uint256[] memory amounts)
    // {
    //     require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
    //     amounts = new uint[](path.length);
    //     amounts[amounts.length - 1] = amountOut;
    //     for (uint256 i = path.length - 1; i > 0; i--) {
    //         (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i - 1], path[i]);
    //         amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
    //     }
    // }
}
"
    },
    "src/interfaces/IVetoCouncil.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IVetoCouncil {
    /* -------------------------------------------------------------------------- */
    /*                                   errors                                    */
    /* -------------------------------------------------------------------------- */
    error CallerNotGovernance();
    error NoRewards();
    error ZeroAddressInConstructor();
    error MaxCouncilMembersExceeded();

    /* -------------------------------------------------------------------------- */
    /*                                   events                                    */
    /* -------------------------------------------------------------------------- */

    /**
     * @param oldMember The address of the member to be slashed or removed
     * @param newMember The address of the new member (0 = no new member)
     * @param slashOldMember Whether to slash the member or not
     */
    event VetoCouncilSeatsEdited(address indexed oldMember, address indexed newMember, bool slashOldMember);

    /**
     * @dev emitted when a council member is paid out
     * @param account The address of the council member
     * @param amountNow The amount paid out now
     * @param amountToBeVested The amount to be vested
     */
    event CouncilMemberPayout(address indexed account, uint256 amountNow, uint256 amountToBeVested);
    /* -------------------------------------------------------------------------- */
    /*                                 state-changing                             */
    /* -------------------------------------------------------------------------- */
    /**
     * @notice Add or remove a council member
     * @param oldMember The address of the member to be slashed or removed
     * @param newMember The address of the new member (0 = no new member)
     * @param slashOldMember Whether to slash the member or not
     * @return - true if the council member was added or removed, false if nothing was done
     *                 - the function should return false if the new member is already a council member
     *                 - if the old member is not a council member, the function should return false
     *                 - if the old member is a council member and the new member is the same as the old member, the function should return false
     *                 - by adding a new member there would be more than 7 council members, the function should return false
     */

    function addAndRemoveCouncilMember(address oldMember, address newMember, bool slashOldMember)
        external
        returns (bool);

    /**
     * @notice Payout the council member
     * @param member The address of the council member
     * @param nonce The payout nonce to claim from
     * @param sync Whether to sync the vesting schedule or not
     * @param members The addresses of the council members that were active at `nonce`
     */
    function claimPayout(address member, uint256 nonce, bool sync, address[] memory members) external;

    /* -------------------------------------------------------------------------- */
    /*                                   view                                    */
    /* -------------------------------------------------------------------------- */
    /**
     * @notice returns true if the member is a council member
     * @param member The address of the member to be checked
     * @return - true if the member is a council member
     */
    function isCouncilMember(address member) external view returns (bool);
}
"
    },
    "src/GLOW.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IGlow} from "./interfaces/IGlow.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {_GENESIS_TIMESTAMP} from "@/Constants/Constants.sol";
/**
 * @title Glow
 * @author DavidVorick
 * @author 0xSimon(twitter) - OxSimbo(github)
 * @notice The Glow token is the backbone of the protocol
 *         - Solar farms are rewarded with glow tokens as they produce solar
 *         - GCA's (Glow Certification Agents) and Veto Council Members are rewarded in GLOW
 *             - for their contributions
 *         - The Grants Treasury is rewarded in GLOW for their contributions
 *         - Holders can anchor (stake) glow to earn voting power in governance
 *             - anchoring lasts 5 years from the point of unstaking
 */

contract Glow is ERC20, ERC20Permit, IGlow {
    /* -------------------------------------------------------------------------- */
    /*                                  constants                                 */
    /* -------------------------------------------------------------------------- */
    /// @notice The cooldown period after unstaking before a user can claim their tokens
    uint256 private constant _STAKE_COOLDOWN_PERIOD = 365 days * 5;

    /// @notice The amount of GLW that is minted per second for the GCA and Miner Pool
    /// @notice 185,000 GLW per week
    /// @dev 175,000 to miners
    /// @dev 10,000 to the GCAs
    uint256 public constant GCA_AND_MINER_POOL_INFLATION_PER_SECOND = 185_000 * 1 ether / uint256(7 days);

    /// @notice The amount of GLW that is minted per second for the Veto Council
    /// @notice 5,000 GLW per week
    uint256 public constant VETO_COUNCIL_INFLATION_PER_SECOND = 5_000 * 1 ether / uint256(7 days);

    /// @notice The amount of GLW that is minted per second for the Grants Treasury
    /// @notice 40,000 GLW per week
    uint256 public constant GRANTS_TREASURY_INFLATION_PER_SECOND = 40_000 * 1 ether / uint256(7 days);

    /// @notice the maximum number of times a user can unstake without clearing their unstaked positions
    /// @notice before they are forced to wait 1 day before staking again
    uint256 public constant MAX_UNSTAKES_BEFORE_EMERGENCY_COOLDOWN = 100;

    /// @notice the cooldown period once users stake over 100 times
    uint256 public constant EMERGENCY_COOLDOWN_PERIOD = 1 days;

    /* -------------------------------------------------------------------------- */
    /*                                  immutables                                */
    /* -------------------------------------------------------------------------- */

    /// @notice The address of the Early Liquidity Contract
    //  solhint-disable-next-line var-name-mixedcase
    address public immutable EARLY_LIQUIDITY_ADDRESS;

    /// @notice the GCA And Miner Pool address
    address public immutable GCA_AND_MINER_POOL_ADDRESS;

    /// @notice the Veto Council address
    address public immutable VETO_COUNCIL_ADDRESS;

    /// @notice the Grants Treasury address
    address public immutable GRANTS_TREASURY_ADDRESS;

    /* -------------------------------------------------------------------------- */
    /*                                 state vars                                */
    /* -------------------------------------------------------------------------- */
    /// @notice The last time the GCA and Miner Pool claimed GLW
    uint256 public gcaAndMinerPoolLastClaimedTimestamp;

    /// @notice The last time the Veto Council claimed GLW
    uint256 public vetoCouncilLastClaimedTimestamp;

    /// @notice The last time the Grants Treasury claimed GLW
    uint256 public grantsTreasuryLastClaimedTimestamp;

    /* -------------------------------------------------------------------------- */
    /*                                   mappings                                  */
    /* -------------------------------------------------------------------------- */
    /// @notice stores the total amount of GLOW staked by a user
    mapping(address => uint256) public numStaked;

    /// @notice stores the unstaked positions of a user
    mapping(address => mapping(uint256 => UnstakedPosition)) private _unstakedPositions;

    /// @notice stores the head of the unstaked positions of a user
    /// @dev the head is the last index with data. If we need to push, we push at head + 1
    /// @dev if the head is zero, there may or may not be data.
    mapping(address => Pointers) private _unstakedPositionPointers;

    /// @notice stores the last time a user staked in case the user has over 100 staked positions
    mapping(address => uint256) public emergencyLastUnstakeTimestamp;

    /* -------------------------------------------------------------------------- */
    /*                                 constructor                                */
    /* -------------------------------------------------------------------------- */

    /*
     * @notice Sets the immutable variables (GENESIS_TIMESTAMP, EARLY_LIQUIDITY_ADDRESS)
    * @notice sends 12 million GLW to the Early Liquidity Contract and 96 million GLW to the unlocker contract
    * @param _earlyLiquidityAddress The address of the Early Liquidity Contract
    * @param _vestingContract The address of the vesting contract
    * @param _gcaAndMinerPoolAddress The address of the GCA and Miner Pool
    * @param _vetoCouncilAddress The address of the Veto Council
    * @param _grantsTreasuryAddress The address of the Grants Treasury
    */
    constructor(
        address _earlyLiquidityAddress,
        address _vestingContract,
        address _gcaAndMinerPoolAddress,
        address _vetoCouncilAddress,
        address _grantsTreasuryAddress
    ) payable ERC20("Glow", "GLW-BETA") ERC20Permit("Glow") {
        EARLY_LIQUIDITY_ADDRESS = _earlyLiquidityAddress;
        GCA_AND_MINER_POOL_ADDRESS = _gcaAndMinerPoolAddress;
        VETO_COUNCIL_ADDRESS = _vetoCouncilAddress;
        GRANTS_TREASURY_ADDRESS = _grantsTreasuryAddress;
        _handleConstructorMint(_earlyLiquidityAddress, _vestingContract, _grantsTreasuryAddress);
    }

    /* -------------------------------------------------------------------------- */
    /*                                  staking                                   */
    /* -------------------------------------------------------------------------- */
    /**
     * @inheritdoc IGlow
     * @dev if the user has unstaked positions that have already expired,
     *         -   the function will auto claim those tokens for the user
     */
    function stake(uint256 stakeAmount) external {
        //Cannot stake zero tokens
        if (stakeAmount == 0) _revert(IGlow.CannotStakeZeroTokens.selector);

        //Find head tail in the mapping
        IGlow.Pointers memory pointers = _unstakedPositionPointers[msg.sender];
        uint256 head = pointers.head;

        //Init the unstakedTotal
        uint256 amountInUserUnstakePool;

        //Init the new head
        uint256 newHead = head;

        uint256 tail = pointers.tail;

        //We need to loop through starting from the head (newest positions)
        for (uint256 i = head; i >= tail; --i) {
            //load the posiiton from storage into memory
            UnstakedPosition memory position = _unstakedPositions[msg.sender][i];

            //increase the amount in the user unstake pool
            //by the amount that is in the position we are on
            amountInUserUnstakePool += position.amount;

            //If it's exactly equal, that means the data will be fully cleared
            //And the head moves to i-1 or 0(if fully empty now)
            if (amountInUserUnstakePool == stakeAmount) {
                //If i is 0 and the amount is exactly zero,
                //that means we c

Tags:
ERC20, Multisig, Mintable, Burnable, Swap, Liquidity, Staking, Voting, Upgradeable, Multi-Signature, Factory|addr:0x1519a8fe33acf8c164578789629146278541506a|verified:true|block:23482976|tx:0x5be5c6e04f6964d9ebfca8259fb542384246b5b1f1b1f6539a47e042a4f9664a|first_check:1759331431

Submitted on: 2025-10-01 17:10:33

Comments

Log in to comment.

No comments yet.