creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/external/ClaimTransfer.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0\npragma solidity 0.8.15;\n\n// Import dependencies\nimport {IPOLY} from \"policies/interfaces/IPOLY.sol\";\nimport {IgOHM} from \"interfaces/IgOHM.sol\";\nimport {ERC20} from \"solmate/tokens/ERC20.sol\";\n\n// Import libraries\nimport {TransferHelper} from \"libraries/TransferHelper.sol\";\n\n/// @title Olympus Claim Transfer Contract\n/// @dev This contract is used to fractionalize pOLY claims and transfer portions of a user's claim to other addresses\ncontract ClaimTransfer {\n    using TransferHelper for ERC20;\n\n    // ========= ERRORS ========= //\n\n    error CT_IllegalClaim();\n\n    // ========= DATA STRUCTURES ========= //\n\n    struct Term {\n        uint256 percent; // PRECISION = 1_000_000 (i.e. 5000 = 0.5%)\n        uint256 gClaimed; // Rebase agnostic # of tokens claimed\n        uint256 max; // Maximum nominal OHM amount claimable\n    }\n\n    // ========= STATE VARIABLES ========= //\n\n    // Olympus Contracts\n    IPOLY public pOLY;\n\n    // Tokens\n    ERC20 public OHM;\n    ERC20 public DAI;\n    IgOHM public gOHM;\n\n    // Accounting\n    mapping(address => Term) public fractionalizedTerms;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    constructor(\n        address poly_,\n        address ohm_,\n        address dai_,\n        address gohm_\n    ) {\n        pOLY = IPOLY(poly_);\n        OHM = ERC20(ohm_);\n        DAI = ERC20(dai_);\n        gOHM = IgOHM(gohm_);\n\n        // Approve pOLY to spend DAI\n        DAI.approve(poly_, type(uint256).max);\n    }\n\n    // ========= CORE FUNCTIONS ========= //\n\n    /// @notice Convert pOLY claim that can only be fully transfered to a new wallet to a fractionalized claim\n    function fractionalizeClaim() external {\n        (uint256 percent, uint256 gClaimed, uint256 max) = pOLY.terms(msg.sender);\n        fractionalizedTerms[msg.sender] = Term(percent, gClaimed, max);\n\n        pOLY.pullWalletChange(msg.sender);\n    }\n\n    /// @notice Claim OHM from the pOLY contract via your fractionalized claim\n    /// @param amount_ Amount of DAI to send to the pOLY contract\n    function claim(uint256 amount_) external {\n        uint256 toSend = (amount_ * 1e9) / 1e18;\n\n        // Get fractionalized terms and validate claim\n        Term memory terms = fractionalizedTerms[msg.sender];\n        pOLY.validateClaim(\n            amount_,\n            IPOLY.Term({percent: terms.percent, gClaimed: terms.gClaimed, max: terms.max})\n        );\n\n        // Update fractionalized terms\n        fractionalizedTerms[msg.sender].gClaimed += gOHM.balanceTo(toSend);\n\n        DAI.transferFrom(msg.sender, address(this), amount_);\n        pOLY.claim(msg.sender, amount_);\n    }\n\n    // ========= TRANSFER FUNCTIONS ========= //\n\n    /// @notice Approve a spender to spend a certain amount of your fractionalized claim (denominated in the `percent` value of a Term)\n    /// @param spender_ Address of the spender\n    /// @param amount_ Amount of your fractionalized claim to approve\n    /// @return bool\n    function approve(address spender_, uint256 amount_) external returns (bool) {\n        allowance[msg.sender][spender_] = amount_;\n        return true;\n    }\n\n    /// @notice Transfer a portion of your fractionalized claim to another address\n    /// @param to_ Address of the recipient\n    /// @param amount_ Amount of your fractionalized claim to transfer\n    /// @return bool\n    /// @dev    Transferring a portion of your claim transfers both based on the percentage and claimable amount of OHM.\n    ///         The recipient will receive a claimable amount of OHM commensurate to the percentage of the sender's max claim\n    ///         ignoring what the sender has already claimed. Say the sender has a percent of 10_000 and a max claim of 100 OHM.\n    ///         They claim 10 OHM, leaving 90 OHM claimable. If they transfer 50% of their claim (5_000), the recipient gets a\n    ///         max value of 55 and the commensurate gClaimed so they have a true claimable amount of 50 OHM. The sender's\n    ///         fractionalized claim is updated to reflect the transfer.\n    function transfer(address to_, uint256 amount_) external returns (bool) {\n        // Transfer\n        _transfer(msg.sender, to_, amount_);\n\n        return true;\n    }\n\n    /// @notice Transfer a portion of a fractionalized claim from another address to a recipient (must have approval)\n    /// @param from_ Address of the sender\n    /// @param to_ Address of the recipient\n    /// @param amount_ Amount of the sender's fractionalized claim to transfer\n    /// @return bool\n    /// @dev    Transferring a portion of your claim transfers both based on the percentage and claimable amount of OHM.\n    ///         The recipient will receive a claimable amount of OHM commensurate to the percentage of the sender's max claim\n    ///         ignoring what the sender has already claimed. Say the sender has a percent of 10_000 and a max claim of 100 OHM.\n    ///         They claim 10 OHM, leaving 90 OHM claimable. If they transfer 50% of their claim (5_000), the recipient gets a\n    ///         max value of 55 and the commensurate gClaimed so they have a true claimable amount of 50 OHM. The sender's\n    ///         fractionalized claim is updated to reflect the transfer.\n    function transferFrom(\n        address from_,\n        address to_,\n        uint256 amount_\n    ) external returns (bool) {\n        // Check that allowance is sufficient\n        allowance[from_][msg.sender] -= amount_;\n\n        // Transfer\n        _transfer(from_, to_, amount_);\n\n        return true;\n    }\n\n    // ========= INTERNAL FUNCTIONS ========= //\n\n    function _transfer(\n        address from_,\n        address to_,\n        uint256 amount_\n    ) internal {\n        // Get fractionalized terms\n        Term memory terms = fractionalizedTerms[from_];\n\n        uint256 gClaimedToTransfer = (amount_ * terms.gClaimed) / terms.percent;\n        uint256 maxToTransfer = (amount_ * terms.max) / terms.percent;\n        uint256 maxAdjustment = gOHM.balanceFrom(gClaimedToTransfer);\n        maxToTransfer += maxAdjustment;\n\n        // Balance updates\n        fractionalizedTerms[from_].percent -= amount_;\n        fractionalizedTerms[from_].gClaimed -= gClaimedToTransfer;\n        fractionalizedTerms[from_].max -= maxToTransfer;\n\n        fractionalizedTerms[to_].percent += amount_;\n        fractionalizedTerms[to_].gClaimed += gClaimedToTransfer;\n        fractionalizedTerms[to_].max += maxToTransfer;\n    }\n}\n"
      },
      "src/policies/interfaces/IPOLY.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0\npragma solidity >=0.8.0;\n\ninterface IPOLY {\n    // ========= ERRORS ========= //\n\n    error POLY_NoClaim();\n    error POLY_AlreadyHasClaim();\n    error POLY_NoWalletChange();\n    error POLY_AllocationLimitViolation();\n    error POLY_ClaimMoreThanVested(uint256 vested_);\n    error POLY_ClaimMoreThanMax(uint256 max_);\n\n    // ========= EVENTS ========= //\n\n    event Claim(address indexed account, address indexed to, uint256 amount);\n    event Transfer(address indexed from, address indexed to, uint256 amount);\n    event WalletChange(address indexed account, address indexed newAddress, bool isPull);\n    event TermsSet(address indexed account, uint256 percent, uint256 gClaimed, uint256 max);\n\n    // ========= DATA STRUCTURES ========= //\n\n    struct GenesisTerm {\n        uint256 percent; // PRECISION = 1_000_000 (i.e. 5000 = 0.5%)\n        uint256 claimed; // Static number\n        uint256 gClaimed; // Rebasing agnostic # of tokens claimed\n        uint256 max; // Maximum nominal OHM amount can claim\n    }\n\n    struct Term {\n        uint256 percent; // PRECISION = 1_000_000 (i.e. 5000 = 0.5%)\n        uint256 gClaimed; // Rebase agnostic # of tokens claimed\n        uint256 max; // Maximum nominal OHM amount claimable\n    }\n\n    // ========= STATE VARIABLES ========= //\n\n    function terms(address account_)\n        external\n        view\n        returns (\n            uint256,\n            uint256,\n            uint256\n        );\n\n    //============================================================================================//\n    //                                       CORE FUNCTIONS                                       //\n    //============================================================================================//\n\n    /// @notice Claims vested OHM by exchanging DAI\n    /// @param  to_ Address to send OHM to\n    /// @param  amount_ DAI amount to exchange for OHM\n    function claim(address to_, uint256 amount_) external;\n\n    //============================================================================================//\n    //                                   MANAGEMENT FUNCTIONS                                     //\n    //============================================================================================//\n\n    /// @notice Pushes entirety of a user's claim to a new address\n    /// @param  newAddress_ Address to send claim to\n    function pushWalletChange(address newAddress_) external;\n\n    /// @notice Pulls a queued wallet change\n    /// @param  oldAddress_ Address to pull change from\n    function pullWalletChange(address oldAddress_) external;\n\n    //============================================================================================//\n    //                                       VIEW FUNCTIONS                                       //\n    //============================================================================================//\n\n    /// @notice Calculates the current amount a user is eligible to redeem\n    /// @param  account_ The account to check the redeemable amount for\n    /// @return uint256 The amount of OHM the account can redeem\n    function redeemableFor(address account_) external view returns (uint256);\n\n    /// @notice Calculates the current amount a user is eligible to redeem\n    /// @param  accountTerms_ The terms of the account to check the redeemable amount for\n    /// @return uint256 The amount of OHM the account can redeem\n    function redeemableFor(Term memory accountTerms_) external view returns (uint256);\n\n    /// @notice Returns a calculation of OHM circulating supply to be used to determine vested positions\n    /// @return uint256 OHM circulating supply\n    function getCirculatingSupply() external view returns (uint256);\n\n    /// @notice Calculates the effective amount of OHM claimed taking into consideration rebasing since claim\n    /// @param  account_ The account to check the claim for\n    /// @return uint256 The amount of OHM the account has claimed\n    function getAccountClaimed(address account_) external returns (uint256);\n\n    /// @notice Calculates the effective amount of OHM claimed taking into consideration rebasing since claim\n    /// @param  accountTerms_ The terms of the account to check the claim for\n    /// @return uint256 The amount of OHM the account has claimed\n    function getAccountClaimed(Term memory accountTerms_) external returns (uint256);\n\n    /// @notice Calculates the amount of OHM to send to the user and validates the claim\n    /// @param  amount_ The amount of DAI to exchange for OHM\n    /// @param  accountTerms_ The terms to check the claim against\n    /// @return uint256 The amount of OHM to send to the user\n    function validateClaim(uint256 amount_, Term memory accountTerms_)\n        external\n        view\n        returns (uint256);\n\n    //============================================================================================//\n    //                                       ADMIN FUNCTIONS                                      //\n    //============================================================================================//\n\n    /// @notice Migrates claim data from old pOLY contracts to this one\n    /// @notice Can only be called by the poly_admin role\n    /// @param  accounts_ Array of accounts to migrate\n    function migrate(address[] calldata accounts_) external;\n\n    /// @notice Migrates claim data from the Genesis Claim contract to this one\n    /// @notice Can only be called by the poly_admin role\n    /// @dev    The Genesis Claim contract originally did not count the full claimed amount as staked,\n    ///         only 90% of it, given that there is no longer a staking rate, we can combine the two\n    ///         claim contracts by counting the 10% that wasn't considered staked as staked at the current index\n    /// @param  accounts_ Array of accounts to migrate\n    function migrateGenesis(address[] calldata accounts_) external;\n\n    /// @notice Sets the claim terms for an account\n    /// @notice Can only be called by the poly_admin role\n    /// @param  account_ The account to set the terms for\n    /// @param  percent_ The percent of the circulating supply the account is entitled to\n    /// @param  gClaimed_ The amount of gOHM the account has claimed\n    /// @param  max_ The maximum amount of OHM the account can claim\n    function setTerms(\n        address account_,\n        uint256 percent_,\n        uint256 gClaimed_,\n        uint256 max_\n    ) external;\n}\n\ninterface IPreviousPOLY {\n    function terms(address account_) external view returns (IPOLY.Term memory);\n}\n\ninterface IGenesisClaim{\n    function terms(address account_) external view returns (IPOLY.GenesisTerm memory);\n}\n"
      },
      "src/interfaces/IgOHM.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0\npragma solidity >=0.8.0;\n\ninterface IgOHM {\n    function balanceFrom(uint256 gohmAmount_) external view returns (uint256);\n\n    function balanceTo(uint256 ohmAmount_) external view returns (uint256);\n}\n"
      },
      "lib/solmate/src/tokens/ERC20.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event Transfer(address indexed from, address indexed to, uint256 amount);\n\n    event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n    /*//////////////////////////////////////////////////////////////\n                            METADATA STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    string public name;\n\n    string public symbol;\n\n    uint8 public immutable decimals;\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC20 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 public totalSupply;\n\n    mapping(address => uint256) public balanceOf;\n\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    /*//////////////////////////////////////////////////////////////\n                            EIP-2612 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal immutable INITIAL_CHAIN_ID;\n\n    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n    mapping(address => uint256) public nonces;\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(\n        string memory _name,\n        string memory _symbol,\n        uint8 _decimals\n    ) {\n        name = _name;\n        symbol = _symbol;\n        decimals = _decimals;\n\n        INITIAL_CHAIN_ID = block.chainid;\n        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               ERC20 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function approve(address spender, uint256 amount) public virtual returns (bool) {\n        allowance[msg.sender][spender] = amount;\n\n        emit Approval(msg.sender, spender, amount);\n\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) public virtual returns (bool) {\n        balanceOf[msg.sender] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(msg.sender, to, amount);\n\n        return true;\n    }\n\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) public virtual returns (bool) {\n        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n        balanceOf[from] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        return true;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                             EIP-2612 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) public virtual {\n        require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n        // Unchecked because the only math done is incrementing\n        // the owner's nonce which cannot realistically overflow.\n        unchecked {\n            address recoveredAddress = ecrecover(\n                keccak256(\n                    abi.encodePacked(\n                        \"\\x19\\x01\",\n                        DOMAIN_SEPARATOR(),\n                        keccak256(\n                            abi.encode(\n                                keccak256(\n                                    \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n                                ),\n                                owner,\n                                spender,\n                                value,\n                                nonces[owner]++,\n                                deadline\n                            )\n                        )\n                    )\n                ),\n                v,\n                r,\n                s\n            );\n\n            require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n            allowance[recoveredAddress][spender] = value;\n        }\n\n        emit Approval(owner, spender, value);\n    }\n\n    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n    }\n\n    function computeDomainSeparator() internal view virtual returns (bytes32) {\n        return\n            keccak256(\n                abi.encode(\n                    keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n                    keccak256(bytes(name)),\n                    keccak256(\"1\"),\n                    block.chainid,\n                    address(this)\n                )\n            );\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        INTERNAL MINT/BURN LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function _mint(address to, uint256 amount) internal virtual {\n        totalSupply += amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(address(0), to, amount);\n    }\n\n    function _burn(address from, uint256 amount) internal virtual {\n        balanceOf[from] -= amount;\n\n        // Cannot underflow because a user's balance\n        // will never be larger than the total supply.\n        unchecked {\n            totalSupply -= amount;\n        }\n\n        emit Transfer(from, address(0), amount);\n    }\n}\n"
      },
      "src/libraries/TransferHelper.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"solmate/tokens/ERC20.sol\";\n\n/// @notice Safe ERC20 and ETH transfer library that safely handles missing return values.\n/// @author Modified from Uniswap & old Solmate (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/libraries/TransferHelper.sol)\nlibrary TransferHelper {\n    function safeTransferFrom(ERC20 token, address from, address to, uint256 amount) internal {\n        (bool success, bytes memory data) = address(token).call(\n            abi.encodeWithSelector(ERC20.transferFrom.selector, from, to, amount)\n        );\n\n        require(success && (data.length == 0 || abi.decode(data, (bool))), \"TRANSFER_FROM_FAILED\");\n    }\n\n    function safeTransfer(ERC20 token, address to, uint256 amount) internal {\n        (bool success, bytes memory data) = address(token).call(\n            abi.encodeWithSelector(ERC20.transfer.selector, to, amount)\n        );\n\n        require(success && (data.length == 0 || abi.decode(data, (bool))), \"TRANSFER_FAILED\");\n    }\n\n    function safeApprove(ERC20 token, address to, uint256 amount) internal {\n        (bool success, bytes memory data) = address(token).call(\n            abi.encodeWithSelector(ERC20.approve.selector, to, amount)\n        );\n\n        require(success && (data.length == 0 || abi.decode(data, (bool))), \"APPROVE_FAILED\");\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "ds-test/=lib/ds-test/src/",
        "forge-std/=lib/forge-std/src/",
        "interfaces/=src/interfaces/",
        "modules/=src/modules/",
        "policies/=src/policies/",
        "libraries/=src/libraries/",
        "solmate/=lib/solmate/src/",
        "layer-zero/=lib/solidity-examples/contracts/",
        "bonds/=lib/bonds/src/",
        "test/=src/test/",
        "clones/=lib/clones-with-immutable-args/src/",
        "balancer-v2/=lib/balancer-v2/",
        "@openzeppelin/=lib/openzeppelin-contracts/",
        "@addresses/=lib/forge-proposal-simulator/addresses/",
        "@examples/=lib/forge-proposal-simulator/examples/",
        "@proposals/=lib/forge-proposal-simulator/proposals/",
        "@script/=lib/forge-proposal-simulator/script/",
        "@test/=lib/forge-proposal-simulator/test/",
        "@utils/=lib/forge-proposal-simulator/utils/",
        "clones-with-immutable-args/=lib/clones-with-immutable-args/src/",
        "comp-governance/=lib/forge-proposal-simulator/lib/compound-governance/contracts/",
        "compound-governance/=lib/forge-proposal-simulator/lib/compound-governance/contracts/",
        "erc4626-tests/=lib/forge-proposal-simulator/lib/openzeppelin-contracts/lib/erc4626-tests/",
        "forge-proposal-simulator/=lib/forge-proposal-simulator/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/",
        "openzeppelin/=lib/forge-proposal-simulator/lib/openzeppelin-contracts/contracts/",
        "solidity-code-metrics/=node_modules/solidity-code-metrics/",
        "solidity-examples/=lib/solidity-examples/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 10
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs"
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "london",
      "libraries": {}
    }
  }}