// File: lib/solmate/src/utils/FixedPointMathLib.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

// File: src/escrows/INVEscrow.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "../interfaces/IERC20.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";

// @dev Caution: We assume all failed transfers cause reverts and ignore the returned bool.
interface IXINV {
    function rewardTreasury() external view returns(address);
    function balanceOf(address) external view returns (uint);
    function exchangeRateStored() external view returns (uint);
    function exchangeRateCurrent() external returns (uint);
    function mint(uint mintAmount) external returns (uint);
    function redeemUnderlying(uint redeemAmount) external returns (uint);
    function syncDelegate(address user) external;
    function accrualBlockNumber() external view returns (uint);
    function getCash() external view returns (uint);
    function totalSupply() external view returns (uint);
    function rewardPerBlock() external view returns (uint);
}

interface IDbrDistributor {
    function stake(uint amount) external;
    function unstake(uint amount) external;
    function claim(address to) external;
    function claimable(address user) external view returns(uint);
}

/**
 * @title INV Escrow
 * @notice Collateral is stored in unique escrow contracts for every user and every market.
 * This escrow allows user to deposit INV collateral directly into the xINV contract, earning APY and allowing them to delegate votes on behalf of the xINV collateral
 * @dev Caution: This is a proxy implementation. Follow proxy pattern best practices
 */
contract INVEscrow {
    using FixedPointMathLib for uint;

    address public market;
    IDelegateableERC20 public token;
    address public beneficiary;
    uint public stakedXINV;
    IXINV public immutable xINV;
    IDbrDistributor public immutable distributor;
    mapping(address => bool) public claimers;

    constructor(IXINV _xINV, IDbrDistributor _distributor) {
        xINV = _xINV;
        distributor = _distributor;
    }

    /**
     * @notice Initialize escrow with a token
     * @dev Must be called right after proxy is created.
     * @param _token The IERC20 token representing the INV governance token
     * @param _beneficiary The beneficiary who may delegate token voting power
     */
    function initialize(IDelegateableERC20 _token, address _beneficiary) public {
        require(market == address(0), "ALREADY INITIALIZED");
        market = msg.sender;
        token = _token;
        beneficiary = _beneficiary;
        _token.delegate(_token.delegates(_beneficiary));
        _token.approve(address(xINV), type(uint).max);
        xINV.syncDelegate(address(this));
    }
    
    /**
     * @notice Transfers the associated ERC20 token to a recipient.
     * @param recipient The address to receive payment from the escrow
     * @param amount The amount of ERC20 token to be transferred.
     */
    function pay(address recipient, uint amount) public {
        require(msg.sender == market, "ONLY MARKET");
        uint invBalance = token.balanceOf(address(this));
        if(invBalance < amount) {
            uint invNeeded = amount - invBalance;
            uint xInvToUnstake = invNeeded * 1 ether / viewExchangeRate();
            stakedXINV -= xInvToUnstake;
            distributor.unstake(xInvToUnstake);
            xINV.redeemUnderlying(invNeeded); // we do not check return value because transfer call will fail if this fails anyway
        }
        token.transfer(recipient, amount);
    }
    
    /**
     * @notice Allows the beneficiary to claim DBR tokens
     * @dev Requires the caller to be the beneficiary
     */
    function claimDBR() public {
        require(msg.sender == beneficiary, "ONLY BENEFICIARY");
        distributor.claim(msg.sender);
    }

    /**
     * @notice Allows the beneficiary or allowed claimers to claim DBR tokens on behalf of another address
     * @param to The address to which the claimed tokens will be sent
     * @dev Requires the caller to be the beneficiary or an allowed claimer
     */
    function claimDBRTo(address to) public {
        require(msg.sender == beneficiary || claimers[msg.sender], "ONLY BENEFICIARY OR ALLOWED CLAIMERS");
        distributor.claim(to);
    }

    /**
     * @notice Returns the amount of claimable DBR tokens for the contract
     * @return The amount of claimable tokens
     */
    function claimable() public view returns (uint) {
        return distributor.claimable(address(this));
    }

    /**
     * @notice Sets or unsets an address as an allowed claimer
     * @param claimer The address of the claimer to set or unset
     * @param allowed A boolean value to determine if the claimer is allowed or not
     * @dev Requires the caller to be the beneficiary
     */
    function setClaimer(address claimer, bool allowed) public {
        require(msg.sender == beneficiary, "ONLY BENEFICIARY");
        claimers[claimer] = allowed;
    }

    /**
    * @notice Get the token balance of the escrow
    * @return Uint representing the INV token balance of the escrow including the additional INV accrued from xINV
    */
    function balance() public view returns (uint) {
        uint invBalance = token.balanceOf(address(this));
        uint invBalanceInXInv = stakedXINV * viewExchangeRate() / 1 ether;
        return invBalance + invBalanceInXInv;
    }
    
    /**
     * @notice Function called by market on deposit. Will deposit INV into xINV 
     * @dev This function should remain callable by anyone to handle direct inbound transfers.
     */
    function onDeposit() public {
        uint invBalance = token.balanceOf(address(this));
        if(invBalance > 0) {
            uint xinvBal = xINV.balanceOf(address(this));
            xINV.mint(invBalance); // we do not check return value because we don't want errors to block this call
            stakedXINV += xINV.balanceOf(address(this)) - xinvBal;
            distributor.stake(stakedXINV - xinvBal);
        }
    }

    /**
     * @notice Delegates voting power of the underlying xINV.
     * @param delegatee The address to be delegated voting power
     */
    function delegate(address delegatee) public {
        require(msg.sender == beneficiary, "ONLY BENEFICIARY");
        token.delegate(delegatee);
        xINV.syncDelegate(address(this));
    }

    /**
     * @notice View function to calculate exact exchangerate for current block
     */
    function viewExchangeRate() internal view returns (uint) {
        uint accrualBlockNumberPrior = xINV.accrualBlockNumber();
        if (accrualBlockNumberPrior == block.number) return xINV.exchangeRateStored();
        uint blockDelta = block.number - accrualBlockNumberPrior;
        uint rewardsAccrued = xINV.rewardPerBlock() * blockDelta;
        uint treasuryInvBalance = token.balanceOf(xINV.rewardTreasury());
        uint treasuryxInvAllowance = token.allowance(xINV.rewardTreasury(), address(xINV));
        if( treasuryInvBalance <= rewardsAccrued || treasuryxInvAllowance <= rewardsAccrued) return xINV.exchangeRateStored();
        return (xINV.getCash() + rewardsAccrued).divWadDown(xINV.totalSupply());
    }

}

// File: src/interfaces/IERC20.sol



pragma solidity ^0.8.13;

interface IERC20 {
    function approve(address,uint) external;
    function transfer(address,uint) external returns (bool);
    function transferFrom(address,address,uint) external returns (bool);
    function balanceOf(address) external view returns (uint);
    function allowance(address from, address to) external view returns (uint);
}

interface IDelegateableERC20 is IERC20 {
    function delegate(address delegatee) external;
    function delegates(address delegator) external view returns (address delegatee);
}