Contract 0x951cab249cA907313c9a510Ae1B7A0Dfb24dCBBd

 
 
Txn Hash
Method
Block
From
To
Value
0xaa06e4972425e09d8909da6a59e729ccd1dc1327e70356b8f4d7ff19a9868804Set New Admin142053152022-02-14 16:22:24346 days 15 hrs agoPaladin: Deployer IN  0x951cab249ca907313c9a510ae1b7a0dfb24dcbbd0 Ether0.00167814 59.41816135
0x782714c01647a6043fb5e0f25836c2b59eaef04945bf07d4d119c0d12c93a0e90x60806040142052662022-02-14 16:13:29346 days 15 hrs agoPaladin: Deployer IN  Create: PaladinController0 Ether0.25261498 54.61176909
[ Download CSV Export 
View more zero value Internal Transactions in Advanced View mode
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PaladinController

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 999999 runs

Other Settings:
default evmVersion
File 1 of 21 : PaladinController.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

import "./utils/SafeMath.sol";
import "./IPaladinController.sol";
import "./ControllerStorage.sol";
import "./ControllerProxy.sol";
import "./PalPool.sol";
import "./IPalPool.sol";
import "./IPalToken.sol";
import "./utils/IERC20.sol";
import "./utils/SafeERC20.sol";
import "./utils/Errors.sol";

/** @title Paladin Controller contract  */
/// @author Paladin
contract PaladinController is IPaladinController, ControllerStorage {
    using SafeMath for uint;
    using SafeERC20 for IERC20;

    // Prevent reentry in deposit & withdraw
    modifier lock() {
        require(!locked, Errors.LOCKED);
        locked = true;
        _;
        locked = false;
    }

    constructor(){
        admin = msg.sender;
    }

    /**
    * @notice Check if an address is a valid palPool
    * @return bool : result
    */
    function isPalPool(address _pool) public view override returns(bool){
        //Check if the given address is in the palPools list
        address[] memory _pools = palPools;
        for(uint i = 0; i < _pools.length; i++){
            if(_pools[i] == _pool){
                return true;
            }
        }
        return false;
    }


    /**
    * @notice Get all the PalTokens listed in the controller
    * @return address[] : List of PalToken addresses
    */
    function getPalTokens() external view override returns(address[] memory){
        return palTokens;
    }


    /**
    * @notice Get all the PalPools listed in the controller
    * @return address[] : List of PalPool addresses
    */
    function getPalPools() external view override returns(address[] memory){
        return palPools;
    }

    /**
    * @notice Set the basic PalPools/PalTokens controller list
    * @param _palTokens array of address of PalToken contracts
    * @param _palPools array of address of PalPool contracts
    * @return bool : Success
    */ 
    function setInitialPools(address[] memory _palTokens, address[] memory _palPools) external override adminOnly returns(bool){
        require(!initialized, Errors.POOL_LIST_ALREADY_SET);
        require(_palTokens.length == _palPools.length, Errors.LIST_SIZES_NOT_EQUAL);
        palPools = _palPools;
        palTokens = _palTokens;
        initialized = true;

        for(uint i = 0; i < _palPools.length; i++){
            //Update the Reward State for the new Pool
            PoolRewardsState storage supplyState = supplyRewardState[_palPools[i]];
            if(supplyState.index == 0){
                supplyState.index = initialRewardsIndex;
            }
            supplyState.blockNumber = safe32(block.number);

            //Link PalToken with PalPool
            palTokenToPalPool[_palTokens[i]] = _palPools[i];
        }

        return true;
    }
    
    /**
    * @notice Add a new PalPool/PalToken couple to the controller list
    * @param _palToken address of the PalToken contract
    * @param _palPool address of the PalPool contract
    * @return bool : Success
    */ 
    function addNewPool(address _palToken, address _palPool) external override adminOnly returns(bool){
        //Add a new address to the palToken & palPool list
        require(!isPalPool(_palPool), Errors.POOL_ALREADY_LISTED);

        palTokens.push(_palToken);
        palPools.push(_palPool);
        palTokenToPalPool[_palToken] = _palPool;

        //Update the Reward State for the new Pool
        PoolRewardsState storage supplyState = supplyRewardState[_palPool];
        if(supplyState.index == 0){
            supplyState.index = initialRewardsIndex;
        }
        supplyState.blockNumber = safe32(block.number);

        //The other Reward values should already be set at 0 :
        //BorrowRatio : 0
        //SupplySpeed : 0
        //If not set as 0, we want to use last values (or change them with the adequate function beforehand)
        
        emit NewPalPool(_palPool, _palToken);

        return true;
    }

    
    /**
    * @notice Remove a PalPool from the list (& the related PalToken)
    * @param palPool address of the PalPool contract to remove
    * @return bool : Success
    */ 
    function removePool(address palPool) external override adminOnly returns(bool){
        //Remove a palToken & palPool from the list
        require(isPalPool(palPool), Errors.POOL_NOT_LISTED);

        address[] memory _pools = palPools;
        
        uint lastIndex = (_pools.length).sub(1);
        for(uint i = 0; i < _pools.length; i++){
            if(_pools[i] == palPool){
                //get the address of the PalToken for the Event
                address _palToken = palTokens[i];

                delete palTokenToPalPool[_palToken];

                //Replace the address to remove with the last one of the array
                palPools[i] = palPools[lastIndex];
                palTokens[i] = palTokens[lastIndex];

                //And pop the last item of the array
                palPools.pop();
                palTokens.pop();

                emit RemovePalPool(palPool, _palToken);
             
                return true;
            }
        }
        return false;
    }


    /**
    * @notice Check if the given PalPool has enough cash to make a withdraw
    * @param palPool address of PalPool
    * @param amount amount withdrawn
    * @return bool : true if possible
    */
    function withdrawPossible(address palPool, uint amount) external view override returns(bool){
        //Get the underlying balance of the palPool contract to check if the action is possible
        PalPool _palPool = PalPool(palPool);
        return(_palPool.underlyingBalance() >= amount);
    }
    

    /**
    * @notice Check if the given PalPool has enough cash to borrow
    * @param palPool address of PalPool
    * @param amount amount ot borrow
    * @return bool : true if possible
    */
    function borrowPossible(address palPool, uint amount) external view override returns(bool){
        //Get the underlying balance of the palPool contract to check if the action is possible
        PalPool _palPool = PalPool(palPool);
        return(_palPool.underlyingBalance() >= amount);
    }
    

    /**
    * @notice Check if Deposit was correctly done
    * @param palPool address of PalPool
    * @param dest address to send the minted palTokens
    * @param amount amount of palTokens minted
    * @return bool : Verification Success
    */
    function depositVerify(address palPool, address dest, uint amount) external view override returns(bool){
        require(isPalPool(msg.sender), Errors.CALLER_NOT_POOL);
        
        palPool;
        dest;

        //Check the amount sent isn't null 
        return amount > 0;
    }


    /**
    * @notice Check if Withdraw was correctly done
    * @param palPool address of PalPool
    * @param dest address to send the underlying tokens
    * @param amount amount of underlying token returned
    * @return bool : Verification Success
    */
    function withdrawVerify(address palPool, address dest, uint amount) external view override returns(bool){
        require(isPalPool(msg.sender), Errors.CALLER_NOT_POOL);

        palPool;
        dest;

        //Check the amount sent isn't null
        return amount > 0;
    }
    

    /**
    * @notice Check if Borrow was correctly done
    * @param palPool address of PalPool
    * @param borrower borrower's address 
    * @param amount amount of token borrowed
    * @param feesAmount amount of fees paid by the borrower
    * @param loanAddress address of the new deployed PalLoan
    * @return bool : Verification Success
    */
    function borrowVerify(address palPool, address borrower, address delegatee, uint amount, uint feesAmount, address loanAddress) external override returns(bool){
        require(isPalPool(msg.sender), Errors.CALLER_NOT_POOL);
        
        borrower;
        delegatee;
        amount;
        feesAmount;

        // Set the borrowRatio for this new Loan
        if(autoBorrowRewards[palPool]) setLoanBorrowRewards(palPool, loanAddress); //Because some PalPool call this method as view
        //So we need to know if the call can update storage or not
        //If not, will need to use the Manual Borrow Reward claim system
        
        //no method yet 
        return true;
    }

    /**
    * @notice Check if Expand Borrow was correctly done
    * @param loanAddress address of the PalLoan contract
    * @param newFeesAmount new amount of fees in the PalLoan
    * @return bool : Verification Success
    */
    function expandBorrowVerify(address palPool, address loanAddress, uint newFeesAmount) external override returns(bool){
        require(isPalPool(msg.sender), Errors.CALLER_NOT_POOL);
        
        newFeesAmount;

        // In case the Loan is expanded, the new ratio is used (in case the ratio changed)
        if(autoBorrowRewards[palPool]) setLoanBorrowRewards(palPool, loanAddress); //Because some PalPool call this method as view
        //So we need to know if the call can update storage or not
        //If not, will need to use the Manual Borrow Reward claim system
        
        //no method yet 
        return true;
    }


    /**
    * @notice Check if Borrow Closing was correctly done
    * @param palPool address of PalPool
    * @param borrower borrower's address
    * @param loanAddress address of the PalLoan contract to close
    * @return bool : Verification Success
    */
    function closeBorrowVerify(address palPool, address borrower, address loanAddress) external override returns(bool){
        require(isPalPool(msg.sender), Errors.CALLER_NOT_POOL);
        
        borrower;

        //Accrue Rewards to the Loan's owner
        if(autoBorrowRewards[palPool]) accrueBorrowRewards(palPool, loanAddress); //Because some PalPool call this method as view
        //So we need to know if the call can update storage or not
        //If not, will need to use the Manual Borrow Reward claim system
        
        //no method yet 
        return true;
    }


    /**
    * @notice Check if Borrow Killing was correctly done
    * @param palPool address of PalPool
    * @param killer killer's address
    * @param loanAddress address of the PalLoan contract to kill
    * @return bool : Verification Success
    */
    function killBorrowVerify(address palPool, address killer, address loanAddress) external override returns(bool){
        require(isPalPool(msg.sender), Errors.CALLER_NOT_POOL);
        
        killer;

        //Accrue Rewards to the Loan's owner
        if(autoBorrowRewards[palPool]) accrueBorrowRewards(palPool, loanAddress); //Because some PalPool call this method as view
        //So we need to know if the call can update storage or not
        //If not, will need to use the Manual Borrow Reward claim system
        
        //no method yet 
        return true;
    }



    // PalToken Deposit/Withdraw functions

    function deposit(address palToken, uint amount) external lock override returns(bool){
        address palPool = palTokenToPalPool[palToken];
        require(isPalPool(palPool), Errors.POOL_NOT_LISTED);
        address user = msg.sender;
        IERC20 token = IERC20(palToken);

        require(amount <= token.balanceOf(user), Errors.INSUFFICIENT_BALANCE);

        updateSupplyIndex(palPool);
        accrueSupplyRewards(palPool, user);

        supplierDeposits[palPool][user] = supplierDeposits[palPool][user].add(amount);
        totalSupplierDeposits[palPool] = totalSupplierDeposits[palPool].add(amount);

        token.safeTransferFrom(user, address(this), amount);

        emit Deposit(user, palToken, amount);

        return true;
    }


    function withdraw(address palToken, uint amount) external lock override returns(bool){
        address palPool = palTokenToPalPool[palToken];
        require(isPalPool(palPool), Errors.POOL_NOT_LISTED);
        address user = msg.sender;

        require(amount <= supplierDeposits[palPool][user], Errors.INSUFFICIENT_DEPOSITED);

        updateSupplyIndex(palPool);
        accrueSupplyRewards(palPool, user);

        IERC20 token = IERC20(palToken);

        supplierDeposits[palPool][user] = supplierDeposits[palPool][user].sub(amount);
        totalSupplierDeposits[palPool] = totalSupplierDeposits[palPool].sub(amount);

        token.safeTransfer(user, amount);

        emit Withdraw(user, palToken, amount);

        return true;
    }


    // Rewards functions
    
    /**
    * @notice Internal - Updates the Supply Index of a Pool for reward distribution
    * @param palPool address of the Pool to update the Supply Index for
    */
    function updateSupplyIndex(address palPool) internal {
        // Get last Pool Supply Rewards state
        PoolRewardsState storage state = supplyRewardState[palPool];
        // Get the current block number, and the Supply Speed for the given Pool
        uint currentBlock = block.number;
        uint supplySpeed = supplySpeeds[palPool];

        // Calculate the number of blocks since last update
        uint ellapsedBlocks = currentBlock.sub(uint(state.blockNumber));

        // If an update is needed : block ellapsed & non-null speed (rewards to distribute)
        if(ellapsedBlocks > 0 && supplySpeed > 0){
            // Get the Total Amount deposited in the Controller of PalToken associated to the Pool
            uint totalDeposited = totalSupplierDeposits[palPool];

            // Calculate the amount of rewards token accrued since last update
            uint accruedAmount = ellapsedBlocks.mul(supplySpeed);

            // And the new ratio for reward distribution to user
            // Based on the amount of rewards accrued, and the change in the TotalSupply
            uint ratio = totalDeposited > 0 ? accruedAmount.mul(1e36).div(totalDeposited) : 0;

            // Write new Supply Rewards values in the storage
            state.index = safe224(uint(state.index).add(ratio));
            state.blockNumber = safe32(currentBlock);
        }
        else if(ellapsedBlocks > 0){
            // If blocks ellapsed, but no rewards to distribute (speed == 0),
            // just write the last update block number
            state.blockNumber = safe32(currentBlock);
        }

    }

    /**
    * @notice Internal - Accrues rewards token to the user claimable balance, depending on the Pool SupplyRewards state
    * @param palPool address of the PalPool the user interracted with
    * @param user address of the user to accrue rewards to
    */
    function accrueSupplyRewards(address palPool, address user) internal {
        // Get the Pool current SupplyRewards state
        PoolRewardsState storage state = supplyRewardState[palPool];

        // Get the current reward index for the Pool
        // And the user last reward index
        uint currentSupplyIndex = state.index;
        uint userSupplyIndex = supplierRewardIndex[palPool][user];

        // Update the Index in the mapping, the local value is used after
        supplierRewardIndex[palPool][user] = currentSupplyIndex;

        if(userSupplyIndex == 0 && currentSupplyIndex >= initialRewardsIndex){
            // Set the initial Index for the user
            userSupplyIndex = initialRewardsIndex;
        }

        // Get the difference of index with the last one for user
        uint indexDiff = currentSupplyIndex.sub(userSupplyIndex);

        if(indexDiff > 0){
            // And using the user PalToken balance deposited in the Controller,
            // we can get how much rewards where accrued
            uint userBalance = supplierDeposits[palPool][user];

            uint userAccruedRewards = userBalance.mul(indexDiff).div(1e36);

            // Add the new amount of rewards to the user total claimable balance
            accruedRewards[user] = accruedRewards[user].add(userAccruedRewards);
        }

    }

    /**
    * @notice Internal - Saves the BorrowRewards Ratio for a PalLoan, depending on the PalPool
    * @param palPool address of the PalPool the Loan comes from
    * @param loanAddress address of the PalLoan contract
    */
    function setLoanBorrowRewards(address palPool, address loanAddress) internal {
        // Saves the current Borrow Reward Ratio to use for that Loan rewards at Closing/Killing
        loansBorrowRatios[loanAddress] = borrowRatios[palPool];
    }

    /**
    * @notice Internal - Accrues reward to the PalLoan owner when the Loan is closed (if the auto-accrue rewards is on for the Pool)
    * @param palPool address of the PalPool the Loan comes from
    * @param loanAddress address of the PalLoan contract
    */
    function accrueBorrowRewards(address palPool, address loanAddress) internal {
        // Get the PalLoan BorrowRatio for rewards
        uint loanBorrowRatio = loansBorrowRatios[loanAddress];

        // Skip if no rewards set for the PalLoan OR if rewards were already claimed for the PalLoan
        if(loanBorrowRatio > 0 && !isLoanRewardClaimed[loanAddress]){
            IPalPool pool = IPalPool(palPool);

            // Get the Borrower, and the amount of fees used by the Loan
            // And using the borrowRatio, accrue rewards for the borrower
            // The amount ot be accrued is calculated as feesUsed * borrowRatio
            (address borrower,,,,,,,uint feesUsedAmount,,,,) = pool.getBorrowData(loanAddress);

            uint userAccruedRewards = feesUsedAmount.mul(loanBorrowRatio).div(1e18);

            // Add the new amount of rewards to the user total claimable balance
            accruedRewards[borrower] = accruedRewards[borrower].add(userAccruedRewards);

            // Set this Loan rewards as claimed/distributed
            isLoanRewardClaimed[loanAddress] = true;
        }

    }


    function _calculateLoanRewards(address palPool, address loanAddress) internal view returns(uint){
        // Rewards already claimed
        if(isLoanRewardClaimed[loanAddress]) return 0;

        IPalPool pool = IPalPool(palPool);
        (,,,,,,,uint feesUsedAmount,uint loanStartBlock,,bool closed,) = pool.getBorrowData(loanAddress);

        // Need the Loan to be closed before accruing rewards
        if(!closed) return 0;

        // Loan as taken before Borrow Rewards were set
        if(borrowRewardsStartBlock[palPool] == 0 || borrowRewardsStartBlock[palPool] > loanStartBlock) return 0;

        // Calculate the amount of rewards based on the Pool ratio & the amount of usedFees in the Loan
        uint poolBorrowRatio = loansBorrowRatios[loanAddress] > 0 ? loansBorrowRatios[loanAddress] : borrowRatios[palPool];

        return feesUsedAmount.mul(poolBorrowRatio).div(1e18);
    }

    /**
    * @notice Returns the current amount of reward tokens the user can claim for a PalLoan
    * @param palPool address of the PalPool
    * @param loanAddress address of the PalLoan
    */
    function claimableLoanRewards(address palPool, address loanAddress) external view override returns(uint) {
        return _calculateLoanRewards(palPool, loanAddress);
    }

    function claimLoanRewards(address palPool, address loanAddress) external lock override {
        IPalPool pool = IPalPool(palPool);
        (address borrower,,,,,,,,,,bool closed,) = pool.getBorrowData(loanAddress);

        // Check if the PalLoan has some claimable rewards, and if it was not claimed already
        require(msg.sender == borrower, Errors.NOT_LOAN_OWNER);
        uint claimableAmount = _calculateLoanRewards(palPool, loanAddress);

        require(closed && claimableAmount != 0, Errors.NOT_CLAIMABLE);

        // Set this Loan rewards as claimed/distributed
        isLoanRewardClaimed[loanAddress] = true;
        
        // Transfer the rewards to the borrower
        IERC20 token = IERC20(rewardToken());
        require(claimableAmount <= token.balanceOf(address(this)), Errors.REWARDS_CASH_TOO_LOW);

        token.safeTransfer(borrower, claimableAmount);

        emit ClaimRewards(borrower, claimableAmount);

    }

    /**
    * @notice Returns the current amount of reward tokens the user can claim
    * @param user address of user
    */
    function claimable(address user) external view override returns(uint) {
        return accruedRewards[user];
    }

    /**
    * @notice Returns the current amount of reward tokens the user can claim + the estimation from last Supplier Index updates
    * @param user address of user
    */
    function estimateClaimable(address user) external view override returns(uint){
        //All the rewards already accrued and not claimed
        uint _total = accruedRewards[user];

        //Calculate the estimated pending rewards for all Pools for the user
        //(depending on the last Pool's updateSupplyIndex)
        address[] memory _pools = palPools;
        for(uint i = 0; i < _pools.length; i++){
            // Get the current reward index for the Pool
            // And the user last reward index
            uint currentSupplyIndex = supplyRewardState[_pools[i]].index;
            uint userSupplyIndex = supplierRewardIndex[_pools[i]][user];

            if(userSupplyIndex == 0 && currentSupplyIndex >= initialRewardsIndex){
                // Set the initial Index for the user
                userSupplyIndex = initialRewardsIndex;
            }

            // Get the difference of index with the last one for user
            uint indexDiff = currentSupplyIndex.sub(userSupplyIndex);

            if(indexDiff > 0){
                // And using the user PalToken balance deposited in the Controller,
                // we can get how much rewards where accrued
                uint userBalance = supplierDeposits[_pools[i]][user];

                uint userAccruedRewards = userBalance.mul(indexDiff).div(1e36);

                // Add the new amount of rewards to the user total claimable balance
                _total = _total.add(userAccruedRewards);
            }
        }

        return _total;
    }

    /**
    * @notice Update the claimable rewards for a given user
    * @param user address of user
    */
    function updateUserRewards(address user) public override {
        address[] memory _pools = palPools;
        for(uint i = 0; i < _pools.length; i++){
            // Need to update the Supply Index
            updateSupplyIndex(_pools[i]);
            // To then accrue the user rewards for that Pool
            //set at 0 & true for amount & positive, since no change in user LP position
            accrueSupplyRewards(_pools[i], user);
            // No need to do it for the Borrower rewards
        }
    }

    /**
    * @notice Accrues rewards for the user, then send all rewards tokens claimable
    * @param user address of user
    */
    function claim(address user) external lock override {
        // Accrue any claimable rewards for all the Pools for the user
        updateUserRewards(user);

        // Get current amount claimable for the user
        uint toClaim = accruedRewards[user];

        // If there is a claimable amount
        if(toClaim > 0){
            IERC20 token = IERC20(rewardToken());
            require(toClaim <= token.balanceOf(address(this)), Errors.REWARDS_CASH_TOO_LOW);

            // All rewards were accrued and sent to the user, reset the counter
            accruedRewards[user] = 0;

            // Transfer the tokens to the user
            token.safeTransfer(user, toClaim);

            emit ClaimRewards(user, toClaim);
        }
    }

    /**
    * @notice Returns the global Supply distribution speed
    * @return uint : Total Speed
    */
    function totalSupplyRewardSpeed() external view override returns(uint) {
        // Sum up the SupplySpeed for all the listed PalPools
        address[] memory _pools = palPools;
        uint totalSpeed = 0;
        for(uint i = 0; i < _pools.length; i++){
            totalSpeed = totalSpeed.add(supplySpeeds[_pools[i]]);
        }
        return totalSpeed;
    }



    /** @notice Address of the reward Token (PAL token) */
    function rewardToken() public view returns(address) {
        return rewardTokenAddress;
    }
        
    
    //Admin function

    function becomeImplementation(ControllerProxy proxy) external override adminOnly {
        // Only to call after the contract was set as Pending Implementation in the Proxy contract
        // To accept the delegatecalls, and update the Implementation address in the Proxy
        require(proxy.acceptImplementation(), Errors.FAIL_BECOME_IMPLEMENTATION);
    }

    function updateRewardToken(address newRewardTokenAddress) external override adminOnly {
        rewardTokenAddress = newRewardTokenAddress;
    }

    // Allows to withdraw reward tokens from the Controller if rewards are removed or changed
    function withdrawRewardToken(uint256 amount, address recipient) external override adminOnly {
        require(recipient != address(0), Errors.ZERO_ADDRESS);
        require(amount > 0, Errors.INVALID_PARAMETERS);

        IERC20 token = IERC20(rewardToken());

        require(amount <= token.balanceOf(address(this)), Errors.BALANCE_TOO_LOW);

        token.safeTransfer(recipient, amount);
    }


    function setPoolsNewController(address newController) external override adminOnly returns(bool){
        address[] memory _pools = palPools;
        for(uint i = 0; i < _pools.length; i++){
            IPalPool _palPool = IPalPool(_pools[i]);
            _palPool.setNewController(newController);
        }
        return true;
    }


    function withdrawFromPool(address pool, uint amount, address recipient) external override adminOnly returns(bool){
        IPalPool _palPool = IPalPool(pool);
        _palPool.withdrawFees(amount, recipient);
        return true;
    }

    // set a pool rewards values (admin)
    function updatePoolRewards(address palPool, uint newSupplySpeed, uint newBorrowRatio, bool autoBorrowReward) external override adminOnly {
        require(isPalPool(palPool), Errors.POOL_NOT_LISTED);

        if(newSupplySpeed != supplySpeeds[palPool]){
            //Make sure it's updated before setting the new speed
            updateSupplyIndex(palPool);

            supplySpeeds[palPool] = newSupplySpeed;
        }

        if(newBorrowRatio != borrowRatios[palPool]){
            borrowRatios[palPool] = newBorrowRatio;
        }

        if(borrowRewardsStartBlock[palPool] == 0 && newBorrowRatio != 0){
            borrowRewardsStartBlock[palPool] = block.number;
        }
        else if(newBorrowRatio == 0){
            borrowRewardsStartBlock[palPool] = 0;
        }

        autoBorrowRewards[palPool] = autoBorrowReward;

        emit PoolRewardsUpdated(palPool, newSupplySpeed, newBorrowRatio, autoBorrowReward);
    }



    //Math utils

    function safe224(uint n) internal pure returns (uint224) {
        require(n < 2**224, "Number is over 224 bits");
        return uint224(n);
    }

    function safe32(uint n) internal pure returns (uint32) {
        require(n < 2**32, "Number is over 32 bits");
        return uint32(n);
    }


}

File 2 of 21 : SafeMath.sol
pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol
// Subject to the MIT license.

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, errorMessage);

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot underflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction underflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot underflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, errorMessage);

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers.
     * Reverts on division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers.
     * Reverts with custom message on division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

File 3 of 21 : IPaladinController.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

import "./ControllerProxy.sol";

/** @title Paladin Controller Interface  */
/// @author Paladin
interface IPaladinController {
    
    //Events

    /** @notice Event emitted when a new token & pool are added to the list */
    event NewPalPool(address palPool, address palToken);
    /** @notice Event emitted when a token & pool are removed from the list */
    event RemovePalPool(address palPool, address palToken);

    event Deposit(address indexed user, address palToken, uint amount);
    event Withdraw(address indexed user, address palToken, uint amount);

    event ClaimRewards(address indexed user, uint amount);

    event PoolRewardsUpdated(address palPool, uint newSupplySpeed, uint newBorrowRatio, bool autoBorrowReward);


    //Functions
    function isPalPool(address pool) external view returns(bool);
    function getPalTokens() external view returns(address[] memory);
    function getPalPools() external view returns(address[] memory);
    function setInitialPools(address[] memory palTokens, address[] memory palPools) external returns(bool);
    function addNewPool(address palToken, address palPool) external returns(bool);
    function removePool(address palPool) external returns(bool);

    function withdrawPossible(address palPool, uint amount) external view returns(bool);
    function borrowPossible(address palPool, uint amount) external view returns(bool);

    function depositVerify(address palPool, address dest, uint amount) external returns(bool);
    function withdrawVerify(address palPool, address dest, uint amount) external returns(bool);
    function borrowVerify(address palPool, address borrower, address delegatee, uint amount, uint feesAmount, address loanAddress) external returns(bool);
    function expandBorrowVerify(address palPool, address loanAddress, uint newFeesAmount) external returns(bool);
    function closeBorrowVerify(address palPool, address borrower, address loanAddress) external returns(bool);
    function killBorrowVerify(address palPool, address killer, address loanAddress) external returns(bool);

    // PalToken Deposit/Withdraw functions
    function deposit(address palToken, uint amount) external returns(bool);
    function withdraw(address palToken, uint amount) external returns(bool);

    // Rewards functions
    function totalSupplyRewardSpeed() external view returns(uint);
    function claimable(address user) external view returns(uint);
    function estimateClaimable(address user) external view returns(uint);
    function updateUserRewards(address user) external;
    function claim(address user) external;
    function claimableLoanRewards(address palPool, address loanAddress) external view returns(uint);
    function claimLoanRewards(address palPool, address loanAddress) external;

    //Admin functions
    function becomeImplementation(ControllerProxy proxy) external;
    function updateRewardToken(address newRewardTokenAddress) external;
    function withdrawRewardToken(uint256 amount, address recipient) external;
    function updatePoolRewards(address palPool, uint newSupplyspeed, uint newBorrowRatio, bool autoBorrowReward) external;
    function setPoolsNewController(address newController) external returns(bool);
    function withdrawFromPool(address pool, uint amount, address recipient) external returns(bool);

}

File 4 of 21 : ControllerStorage.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

import "./utils/Admin.sol";

/** @title Paladin Controller contract  */
/// @author Paladin
contract ControllerStorage is Admin {

    /** @notice Layout for the Proxy contract */
    address public currentImplementation;
    address public pendingImplementation;

    /** @notice List of current active palToken Pools */
    address[] public palTokens;
    address[] public palPools;
    mapping(address => address) public palTokenToPalPool;

    bool internal initialized;

    /** @notice Struct with current SupplyIndex for a Pool, and the block of the last update */
    struct PoolRewardsState {
        uint224 index;
        uint32 blockNumber;
    }

    /** @notice Initial index for Rewards */
    uint224 public constant initialRewardsIndex = 1e36;

    address public rewardTokenAddress; // PAL token address to put here

    /** @notice State of the Rewards for each Pool */
    mapping(address => PoolRewardsState) public supplyRewardState;

    /** @notice Amount of reward tokens to distribute each block */
    mapping(address => uint) public supplySpeeds;

    /** @notice Last reward index for each Pool for each user */
    /** PalPool => User => Index */
    mapping(address => mapping(address => uint)) public supplierRewardIndex;

    /** @notice Deposited amounts by user for each palToken (indexed by corresponding PalPool address) */
    /** PalPool => User => Amount */
    mapping(address => mapping(address => uint)) public supplierDeposits;

    /** @notice Total amount of each palToken deposited (indexed by corresponding PalPool address) */
    /** PalPool => Total Amount */
    mapping(address => uint) public totalSupplierDeposits;

    /** @notice Ratio to distribute Borrow Rewards */
    mapping(address => uint) public borrowRatios; // scaled 1e18

    /** @notice Ratio for each PalLoan (set at PalLoan creation) */
    mapping(address => uint) public loansBorrowRatios; // scaled 1e18

    /** @notice Amount of reward Tokens accrued by the user, and claimable */
    mapping(address => uint) public accruedRewards;

    /** @notice Is Auto Borrow Rewards is activated for the PalPool  */
    mapping(address => bool) public autoBorrowRewards;

    /** @notice Was PalLoan Borrow Rewards distributed & claimed */
    mapping(address => bool) public isLoanRewardClaimed;

    /** @notice Block at which Borrow Rewards Ratio where set for the PalPool (if Ratio is put back to 0, this block number is set back to 0 too) */
    /** So PalLoan started when no Borrow Rewards where set do not receive rewards */
    /** PalPool => Block Number */
    mapping(address => uint) public borrowRewardsStartBlock;

    /** @dev Prevent reentry in some functions */
    bool internal locked;

    /*
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    !!!!!!!!!!!!!!!!!! ALWAYS PUT NEW STORAGE AT THE BOTTOM !!!!!!!!!!!!!!!!!!
    !!!!!!!!! WE DON'T WANT COLLISION WHEN SWITCHING IMPLEMENTATIONS !!!!!!!!!
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    */


}

File 5 of 21 : ControllerProxy.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

import "./utils/Errors.sol";
import "./ControllerStorage.sol";

/** @title Paladin Controller contract  */
/// @author Paladin
contract ControllerProxy is ControllerStorage {

    event NewPendingImplementation(address oldPendingImplementation, address newPendingImplementation);

    event NewImplementation(address oldImplementation, address newImplementation);

    constructor(){
        admin = msg.sender;
    }

    /**
     * @dev Proposes the address of a new Implementation (the new Controller contract)
     */
    function proposeImplementation(address newPendingImplementation) public adminOnly {

        address oldPendingImplementation = pendingImplementation;

        pendingImplementation = newPendingImplementation;

        emit NewPendingImplementation(oldPendingImplementation, newPendingImplementation);
    }

    /**
     * @dev Accepts the Pending Implementation as new Current Implementation
     * Only callable by the Pending Implementation contract
     */
    function acceptImplementation() public returns(bool) {
        require(msg.sender == pendingImplementation, Errors.CALLER_NOT_IMPLEMENTATION);

        address oldImplementation = currentImplementation;
        address oldPendingImplementation = pendingImplementation;

        currentImplementation = pendingImplementation;
        pendingImplementation = address(0);

        emit NewImplementation(oldImplementation, currentImplementation);
        emit NewPendingImplementation(oldPendingImplementation, pendingImplementation);

        return true;
    }

    /**
     * @dev Delegates execution to an implementation contract.
     * It returns to the external caller whatever the implementation returns
     * or forwards reverts.
     */
    fallback() external payable {
        // delegate all other functions to current implementation
        (bool success, ) = currentImplementation.delegatecall(msg.data);

        assembly {
              let free_mem_ptr := mload(0x40)
              returndatacopy(free_mem_ptr, 0, returndatasize())

              switch success
              case 0 { revert(free_mem_ptr, returndatasize()) }
              default { return(free_mem_ptr, returndatasize()) }
        }
    }

}

File 6 of 21 : PalPool.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
pragma abicoder v2;
//SPDX-License-Identifier: MIT

import "./utils/SafeMath.sol";
import "./utils/SafeERC20.sol";
import "./utils/Clones.sol";
import "./IPalPool.sol";
import "./PalPoolStorage.sol";
import "./IPalLoan.sol";
//import "./PalLoan.sol";
import "./IPalToken.sol";
import "./IPaladinController.sol";
import "./IPalLoanToken.sol";
import "./interests/InterestInterface.sol";
import "./utils/IERC20.sol";
import "./utils/Admin.sol";
import "./utils/ReentrancyGuard.sol";
import {Errors} from  "./utils/Errors.sol";



/** @title PalPool contract  */
/// @author Paladin
contract PalPool is IPalPool, PalPoolStorage, Admin, ReentrancyGuard {
    using SafeMath for uint;
    using SafeERC20 for IERC20;


    modifier controllerOnly() {
        //allows only the Controller and the admin to call the function
        require(msg.sender == admin || msg.sender == address(controller), Errors.CALLER_NOT_CONTROLLER);
        _;
    }



    //Functions

    constructor(
        address _palToken,
        address _controller, 
        address _underlying,
        address _interestModule,
        address _delegator,
        address _palLoanToken
    ){
        //Set admin
        admin = msg.sender;

        //Set inital values & modules
        palToken = IPalToken(_palToken);
        controller = IPaladinController(_controller);
        underlying = IERC20(_underlying);
        accrualBlockNumber = block.number;
        interestModule = InterestInterface(_interestModule);
        borrowIndex = 1e18;
        delegator = _delegator;
        palLoanToken = IPalLoanToken(_palLoanToken);
    }

    /**
    * @notice Get the underlying balance for this Pool
    * @dev Get the underlying balance of this Pool
    * @return uint : balance of this pool in the underlying token
    */
    function underlyingBalance() public view returns(uint){
        //Return the balance of this contract for the underlying asset
        return underlying.balanceOf(address(this));
    }

    /**
    * @notice Deposit underlying in the Pool
    * @dev Deposit underlying, and mints palToken for the user
    * @param _amount Amount of underlying to deposit
    * @return bool : amount of minted palTokens
    */
    function deposit(uint _amount) public virtual override nonReentrant returns(uint){
        require(_updateInterest());

        //Retrieve the current exchange rate palToken:underlying
        uint _exchRate = _exchangeRate();


        //Find the amount to mint depending on the amount to transfer
        uint _num = _amount.mul(mantissaScale);
        uint _toMint = _num.div(_exchRate);

        //Transfer the underlying to this contract
        //The amount of underlying needs to be approved before
        underlying.safeTransferFrom(msg.sender, address(this), _amount);

        //Mint the palToken
        require(palToken.mint(msg.sender, _toMint), Errors.FAIL_MINT);

        //Emit the Deposit event
        emit Deposit(msg.sender, _amount, address(this));

        //Use the controller to check if the minting was successfull
        require(controller.depositVerify(address(this), msg.sender, _toMint), Errors.FAIL_DEPOSIT);

        return _toMint;
    }

    /**
    * @notice Withdraw underliyng token from the Pool
    * @dev Transfer underlying token to the user, and burn the corresponding palToken amount
    * @param _amount Amount of palToken to return
    * @return uint : amount of underlying returned
    */
    function withdraw(uint _amount) public virtual override nonReentrant returns(uint){
        require(_updateInterest());
        require(balanceOf(msg.sender) >= _amount, Errors.INSUFFICIENT_BALANCE);

        //Retrieve the current exchange rate palToken:underlying
        uint _exchRate = _exchangeRate();

        //Find the amount to return depending on the amount of palToken to burn
        uint _num = _amount.mul(_exchRate);
        uint _toReturn = _num.div(mantissaScale);

        //Check if the pool has enough underlying to return
        require(_toReturn <= underlyingBalance(), Errors.INSUFFICIENT_CASH);

        //Burn the corresponding palToken amount
        require(palToken.burn(msg.sender, _amount), Errors.FAIL_BURN);

        //Make the underlying transfer
        underlying.safeTransfer(msg.sender, _toReturn);

        //Use the controller to check if the burning was successfull
        require(controller.withdrawVerify(address(this), msg.sender, _toReturn), Errors.FAIL_WITHDRAW);

        //Emit the Withdraw event
        emit Withdraw(msg.sender, _amount, address(this));

        return _toReturn;
    }

    /**
    * @dev Create a Borrow, deploy a Loan Pool and delegate voting power
    * @param _delegatee Address to delegate the voting power to
    * @param _amount Amount of underlying to borrow
    * @param _feeAmount Amount of fee to pay to start the loan
    * @return uint : new PalLoanToken Id
    */
    function borrow(address _delegatee, uint _amount, uint _feeAmount) public virtual override nonReentrant returns(uint){
        //Need the pool to have enough liquidity, and the interests to be up to date
        require(_amount < underlyingBalance(), Errors.INSUFFICIENT_CASH);
        require(_delegatee != address(0), Errors.ZERO_ADDRESS);
        require(_amount > 0, Errors.ZERO_BORROW);
        require(_feeAmount >= minBorrowFees(_amount), Errors.BORROW_INSUFFICIENT_FEES);
        require(_updateInterest());

        address _borrower = msg.sender;

        //Update Total Borrowed & number active Loans
        numberActiveLoans = numberActiveLoans.add(1);
        totalBorrowed = totalBorrowed.add(_amount);

        IPalLoan _newLoan = IPalLoan(Clones.clone(delegator));

        //Send the borrowed amount of underlying tokens to the Loan
        underlying.safeTransfer(address(_newLoan), _amount);

        //And transfer the fees from the Borrower to the Loan
        underlying.safeTransferFrom(_borrower, address(_newLoan), _feeAmount);

        //Start the Loan (and delegate voting power)
        require(_newLoan.initiate(
            address(this),
            _borrower,
            address(underlying),
            _delegatee,
            _amount,
            _feeAmount
        ), Errors.FAIL_LOAN_INITIATE);

        //Add the new Loan to mappings
        loans.push(address(_newLoan));

        //Mint the palLoanToken linked to this new Loan
        uint256 _newTokenId = palLoanToken.mint(_borrower, address(this), address(_newLoan));

        //New Borrow struct for this Loan
        loanToBorrow[address(_newLoan)] = Borrow(
            _newTokenId,
            _delegatee,
            address(_newLoan),
            _amount,
            address(underlying),
            _feeAmount,
            0,
            borrowIndex,
            block.number,
            0,
            false,
            false
        );

        //Check the borrow succeeded
        require(
            controller.borrowVerify(address(this), _borrower, _delegatee, _amount, _feeAmount, address(_newLoan)), 
            Errors.FAIL_BORROW
        );

        //Emit the NewLoan Event
        emit NewLoan(
            _borrower,
            _delegatee,
            address(underlying),
            _amount,
            address(this),
            address(_newLoan),
            _newTokenId,
            block.number
        );

        //Return the PalLoanToken Id
        return _newTokenId;
    }

    /**
    * @notice Transfer the new fees to the Loan, and expand the Loan
    * @param _loan Address of the Loan
    * @param _feeAmount New amount of fees to pay
    * @return bool : Amount of fees paid
    */
    function expandBorrow(address _loan, uint _feeAmount) public virtual override nonReentrant returns(uint){
        //Fetch the corresponding Borrow
        //And check that the caller is the Borrower, and the Loan is still active
        Borrow storage _borrow = loanToBorrow[_loan];
        require(!_borrow.closed, Errors.LOAN_CLOSED);
        require(isLoanOwner(_loan, msg.sender), Errors.NOT_LOAN_OWNER);
        require(_feeAmount > 0);
        require(_updateInterest());
        
        //Load the Loan Pool contract & get Loan owner
        IPalLoan _palLoan = IPalLoan(_borrow.loan);

        address _loanOwner = palLoanToken.ownerOf(_borrow.tokenId);

        _borrow.feesAmount = _borrow.feesAmount.add(_feeAmount);

        //Transfer the new fees to the Loan
        //If success, call the expand function of the Loan
        underlying.safeTransferFrom(_loanOwner, _borrow.loan, _feeAmount);

        require(_palLoan.expand(_feeAmount), Errors.FAIL_LOAN_EXPAND);

        require(
            controller.expandBorrowVerify(address(this), _loan, _feeAmount), 
            Errors.FAIL_LOAN_EXPAND
        );

        emit ExpandLoan(
            _loanOwner,
            _borrow.delegatee,
            address(underlying),
            address(this),
            _borrow.feesAmount,
            _borrow.loan,
            _borrow.tokenId
        );

        return _feeAmount;
    }

    /**
    * @notice Close a Loan, and return the non-used fees to the Borrower.
    * If closed before the minimum required length, penalty fees are taken to the non-used fees
    * @dev Close a Loan, and return the non-used fees to the Borrower
    * @param _loan Address of the Loan
    */
    function closeBorrow(address _loan) public virtual override nonReentrant {
        //Fetch the corresponding Borrow
        //And check that the caller is the Borrower, and the Loan is still active
        Borrow storage _borrow = loanToBorrow[_loan];
        require(!_borrow.closed, Errors.LOAN_CLOSED);
        require(isLoanOwner(_loan, msg.sender), Errors.NOT_LOAN_OWNER);
        require(_updateInterest());

        //Get Loan owner from the ERC721 contract
        address _loanOwner = palLoanToken.ownerOf(_borrow.tokenId);

        //Load the Loan contract
        IPalLoan _palLoan = IPalLoan(_borrow.loan);

        //Calculates the amount of fees used
        uint _feesUsed = (_borrow.amount.mul(borrowIndex).div(_borrow.borrowIndex)).sub(_borrow.amount);
        uint _penaltyFees = 0;
        uint _totalFees = _feesUsed;

        //If the Borrow is closed before the minimum length, calculates the penalty fees to pay
        // -> Number of block remaining to complete the minimum length * current Borrow Rate
        if(block.number < (_borrow.startBlock.add(minBorrowLength))){
            uint _currentBorrowRate = interestModule.getBorrowRate(address(this), underlyingBalance(), totalBorrowed, totalReserve);
            uint _missingBlocks = (_borrow.startBlock.add(minBorrowLength)).sub(block.number);
            _penaltyFees = _missingBlocks.mul(_borrow.amount.mul(_currentBorrowRate)).div(mantissaScale);
            _totalFees = _totalFees.add(_penaltyFees);
        }
    
        //Security so the Borrow can be closed if there are no more fees
        //(if the Borrow wasn't Killed yet, or the loan is closed before minimum time, and already paid fees aren't enough)
        if(_totalFees > _borrow.feesAmount){
            _totalFees = _borrow.feesAmount;
        }

        //Set the Borrow as closed
        _borrow.closed = true;
        _borrow.feesUsed = _totalFees;
        _borrow.closeBlock = block.number;

        //Remove the borrowed tokens + fees from the TotalBorrowed
        //Add to the Reserve the reserveFactor of Penalty Fees (if there is Penalty Fees)
        //And add the fees counted as potential Killer Fees to the Accrued Fees, since no killing was necessary

        numberActiveLoans = numberActiveLoans.sub(1);

        totalBorrowed = numberActiveLoans == 0 ? 0 : totalBorrowed.sub((_borrow.amount).add(_feesUsed)); //If not current active Loan, we can just reset the totalBorrowed to 0

        uint _realPenaltyFees = _totalFees.sub(_feesUsed);
        uint _killerFees = _feesUsed.mul(killerRatio).div(mantissaScale);
        totalReserve = totalReserve.add(reserveFactor.mul(_realPenaltyFees).div(mantissaScale));
        accruedFees = accruedFees.add(_killerFees).add(reserveFactor.mul(_realPenaltyFees).div(mantissaScale));
        
        //Close and destroy the loan
        _palLoan.closeLoan(_totalFees, _loanOwner);

        //Burn the palLoanToken for this Loan
        require(palLoanToken.burn(_borrow.tokenId), Errors.FAIL_LOAN_TOKEN_BURN);

        require(controller.closeBorrowVerify(address(this), _loanOwner, _borrow.loan), Errors.FAIL_CLOSE_BORROW);

        //Emit the CloseLoan Event
        emit CloseLoan(
            _loanOwner,
            _borrow.delegatee,
            address(underlying),
            _borrow.amount,
            address(this),
            _totalFees,
            _loan,
            _borrow.tokenId,
            false
        );
    }

    /**
    * @notice Kill a non-healthy Loan to collect rewards
    * @dev Kill a non-healthy Loan to collect rewards
    * @param _loan Address of the Loan
    */
    function killBorrow(address _loan) public virtual override nonReentrant {
        address killer = msg.sender;
        //Fetch the corresponding Borrow
        //And check that the killer is not the Borrower, and the Loan is still active
        Borrow storage _borrow = loanToBorrow[_loan];
        require(!_borrow.closed, Errors.LOAN_CLOSED);
        require(!isLoanOwner(_loan, killer), Errors.LOAN_OWNER);
        require(_updateInterest());

        //Get the owner of the Loan through the ERC721 contract
        address _loanOwner = palLoanToken.ownerOf(_borrow.tokenId);

        //Calculate the amount of fee used, and check if the Loan is killable
        uint _feesUsed = (_borrow.amount.mul(borrowIndex).div(_borrow.borrowIndex)).sub(_borrow.amount);
        uint _loanHealthFactor = _feesUsed.mul(uint(1e18)).div(_borrow.feesAmount);
        require(_loanHealthFactor >= killFactor, Errors.NOT_KILLABLE);

        //Load the Loan
        IPalLoan _palLoan = IPalLoan(_borrow.loan);

        //Close the Loan, and update storage variables
        _borrow.closed = true;
        _borrow.killed = true;
        _borrow.feesUsed = _borrow.feesAmount;
        _borrow.closeBlock = block.number;

        //Remove the borrowed tokens + fees from the TotalBorrowed
        //Remove the amount paid as killer fees from the Reserve, and any over accrued interest in the Reserve & AccruedFees
        uint _overAccruedInterest = _loanHealthFactor <= mantissaScale ? 0 : _feesUsed.sub(_borrow.feesAmount);
        uint _killerFees = (_borrow.feesAmount).mul(killerRatio).div(mantissaScale);

        numberActiveLoans = numberActiveLoans.sub(1);

        totalBorrowed = numberActiveLoans == 0 ? 0 : totalBorrowed.sub((_borrow.amount).add(_feesUsed)); //If not current active Loan, we can just reset the totalBorrowed to 0

        totalReserve = totalReserve.sub(_killerFees).sub(_overAccruedInterest.mul(reserveFactor).div(mantissaScale));
        accruedFees = accruedFees.sub(_overAccruedInterest.mul(reserveFactor.sub(killerRatio)).div(mantissaScale));

        //Kill the Loan
        _palLoan.killLoan(killer, killerRatio);

        //Burn the palLoanToken for this Loan
        require(palLoanToken.burn(_borrow.tokenId), Errors.FAIL_LOAN_TOKEN_BURN);

        require(controller.killBorrowVerify(address(this), killer, _borrow.loan), Errors.FAIL_KILL_BORROW);

        //Emit the CloseLoan Event
        emit CloseLoan(
            _loanOwner,
            _borrow.delegatee,
            address(underlying),
            _borrow.amount,
            address(this),
            _borrow.feesAmount,
            _loan,
            _borrow.tokenId,
            true
        );
    }



    /**
    * @notice Change the delegatee of a Loan, and delegate him the voting power
    * @dev Change the delegatee in the Borrow struct and in the palLoan, then change the voting power delegation recipient
    * @param _loan Address of the Loan
    * @param _newDelegatee Address of the new voting power recipient
    */
    function changeBorrowDelegatee(address _loan, address _newDelegatee) public virtual override nonReentrant {
        //Fetch the corresponding Borrow
        //And check that the caller is the Borrower, and the Loan is still active
        Borrow storage _borrow = loanToBorrow[_loan];
        require(!_borrow.closed, Errors.LOAN_CLOSED);
        require(_newDelegatee != address(0), Errors.ZERO_ADDRESS);
        require(isLoanOwner(_loan, msg.sender), Errors.NOT_LOAN_OWNER);
        require(_updateInterest());

        //Load the Loan Pool contract
        IPalLoan _palLoan = IPalLoan(_borrow.loan);

        //Update storage data
        _borrow.delegatee = _newDelegatee;

        //Call the delegation logic in the palLoan to change the votong power recipient
        require(_palLoan.changeDelegatee(_newDelegatee), Errors.FAIL_LOAN_DELEGATEE_CHANGE);

        //Emit the Event
        emit ChangeLoanDelegatee(
            palLoanToken.ownerOf(_borrow.tokenId),
            _newDelegatee,
            address(underlying),
            address(this),
            _borrow.loan,
            _borrow.tokenId
        );

    }


    /**
    * @notice Return the user's palToken balance
    * @dev Links the PalToken balanceOf() method
    * @param _account User address
    * @return uint256 : user palToken balance (in wei)
    */
    function balanceOf(address _account) public view override returns(uint){
        return palToken.balanceOf(_account);
    }


    /**
    * @notice Return the corresponding balance of the pool underlying token depending on the user's palToken balance
    * @param _account User address
    * @return uint256 : corresponding balance in the underlying token (in wei)
    */
    function underlyingBalanceOf(address _account) external view override returns(uint){
        uint _balance = palToken.balanceOf(_account);
        if(_balance == 0){
            return 0;
        }
        uint _exchRate = _exchangeRate();
        uint _num = _balance.mul(_exchRate);
        return _num.div(mantissaScale);
    }


    /**
    * @notice Return true is the given address is the owner of the palLoanToken for the given palLoan
    * @param _loanAddress Address of the Loan
    * @param _user User address
    * @return bool : true if owner
    */
    function isLoanOwner(address _loanAddress, address _user) public view override returns(bool){
        return palLoanToken.allOwnerOf(idOfLoan(_loanAddress)) == _user;
    }


    /**
    * @notice Return the token Id of the palLoanToken linked to this palLoan
    * @param _loanAddress Address of the Loan
    * @return uint256 : palLoanToken token Id
    */
    function idOfLoan(address _loanAddress) public view override returns(uint256){
        return loanToBorrow[_loanAddress].tokenId;
    }



    /**
    * @notice Return the list of all Loans for this Pool (closed and active)
    * @return address[] : list of Loans
    */
    function getLoansPools() external view override returns(address [] memory){
        //Return the addresses of all loans (old ones and active ones)
        return loans;
    }
    
    /**
    * @notice Return all the Loans for a given address
    * @param _borrower Address of the user
    * @return address : list of Loans
    */
    function getLoansByBorrower(address _borrower) external view override returns(address [] memory){
        return palLoanToken.allLoansOfForPool(_borrower, address(this));
    }

    
    /**
    * @notice Return the stored Borrow data for a given Loan
    * @dev Return the Borrow data for a given Loan
    * @param _loanAddress Address of the palLoan
    * Composants of a Borrow struct
    */
    function getBorrowData(address _loanAddress) external view override returns(
        address _borrower,
        address _delegatee,
        address _loan,
        uint256 _palLoanTokenId,
        uint _amount,
        address _underlying,
        uint _feesAmount,
        uint _feesUsed,
        uint _startBlock,
        uint _closeBlock,
        bool _closed,
        bool _killed
    ){
        //Return the data inside a Borrow struct
        Borrow memory _borrow = loanToBorrow[_loanAddress];
        return (
            //Get the Loan owner through the ERC721 contract
            palLoanToken.allOwnerOf(_borrow.tokenId),
            _borrow.delegatee,
            _borrow.loan,
            _borrow.tokenId,
            _borrow.amount,
            _borrow.underlying,
            _borrow.feesAmount,
            //Calculate amount of fees used
            _borrow.closed ? _borrow.feesUsed : (_borrow.amount.mul(borrowIndex).div(_borrow.borrowIndex)).sub(_borrow.amount),
            _borrow.startBlock,
            _borrow.closeBlock,
            _borrow.closed,
            _borrow.killed
        );

    }
    
    /**
    * @notice Get the Borrow Rate for this Pool
    * @dev Get the Borrow Rate from the Interest Module
    * @return uint : Borrow Rate (scale 1e18)
    */
    function borrowRatePerBlock() external view override returns (uint){
        return interestModule.getBorrowRate(address(this), underlyingBalance(), totalBorrowed, totalReserve);
    }
    
    /**
    * @notice Get the Supply Rate for this Pool
    * @dev Get the Supply Rate from the Interest Module
    * @return uint : Supply Rate (scale 1e18)
    */
    function supplyRatePerBlock() external view override returns (uint){
        return interestModule.getSupplyRate(address(this), underlyingBalance(), totalBorrowed, totalReserve, reserveFactor);
    }

    
    /**
    * @dev Calculates the current exchange rate
    * @return uint : current exchange rate (scale 1e18)
    */
    function _exchangeRate() internal view returns (uint){
        uint _totalSupply = palToken.totalSupply();
        //If no palTokens where minted, use the initial exchange rate
        if(_totalSupply == 0){
            return initialExchangeRate;
        }
        else{
            // Exchange Rate = (Cash + Borrows - Reserve) / Supply
            uint _cash = underlyingBalance();
            uint _availableCash = _cash.add(totalBorrowed).sub(totalReserve);
            return _availableCash.mul(1e18).div(_totalSupply);
        }
    }

    /**
    * @notice Get the current exchange rate for the palToken
    * @dev Updates interest & Calls internal function _exchangeRate
    * @return uint : current exchange rate (scale 1e18)
    */
    function exchangeRateCurrent() external override returns (uint){
        _updateInterest();
        return _exchangeRate();
    }
    
    /**
    * @notice Get the stored exchange rate for the palToken
    * @dev Calls internal function _exchangeRate
    * @return uint : current exchange rate (scale 1e18)
    */
    function exchangeRateStored() external view override returns (uint){
        return _exchangeRate();
    }

    /**
    * @notice Return the minimum of fees to pay to borrow
    * @dev Fees to pay for a Borrow (for the minimum borrow length)
    * @return uint : minimum amount (in wei)
    */
    function minBorrowFees(uint _amount) public view override returns (uint){
        require(_amount < underlyingBalance(), Errors.INSUFFICIENT_CASH);
        //Future Borrow Rate with the amount to borrow counted as already borrowed
        uint _borrowRate = interestModule.getBorrowRate(address(this), underlyingBalance().sub(_amount), totalBorrowed.add(_amount), totalReserve);
        uint _minFees = minBorrowLength.mul(_amount.mul(_borrowRate)).div(mantissaScale);
        return _minFees > 0 ? _minFees : 1;
    }

    function isKillable(address _loan) external view override returns(bool){
        Borrow memory __borrow = loanToBorrow[_loan];
        if(__borrow.closed){
            return false;
        }

        //Calculate the amount of fee used, and check if the Loan is killable
        uint _feesUsed = (__borrow.amount.mul(borrowIndex).div(__borrow.borrowIndex)).sub(__borrow.amount);
        uint _loanHealthFactor = _feesUsed.mul(uint(1e18)).div(__borrow.feesAmount);
        return _loanHealthFactor >= killFactor;
    }

    /**
    * @dev Updates Inetrest and variables for this Pool
    * @return bool : Update success
    */
    function _updateInterest() public returns (bool){
        //Get the current block
        //Check if the Pool has already been updated this block
        uint _currentBlock = block.number;
        if(_currentBlock == accrualBlockNumber){
            return true;
        }

        //Get Pool variables from Storage
        uint _cash = underlyingBalance();
        uint _borrows = totalBorrowed;
        uint _reserves = totalReserve;
        uint _accruedFees = accruedFees;
        uint _oldBorrowIndex = borrowIndex;

        //Get the Borrow Rate from the Interest Module
        uint _borrowRate = interestModule.getBorrowRate(address(this), _cash, _borrows, _reserves);

        //Delta of blocks since the last update
        uint _ellapsedBlocks = _currentBlock.sub(accrualBlockNumber);

        /*
        Interest Factor = Borrow Rate * Ellapsed Blocks
        Accumulated Interests = Interest Factor * Borrows
        Total Borrows = Borrows + Accumulated Interests
        Total Reserve = Reserve + Accumulated Interests * Reserve Factor
        Accrued Fees = Accrued Fees + Accumulated Interests * (Reserve Factor - Killer Ratio) -> (available fees should not count potential fees to send to killers)
        Borrow Index = old Borrow Index + old Borrow Index * Interest Factor 
        */
        uint _interestFactor = _borrowRate.mul(_ellapsedBlocks);
        uint _accumulatedInterest = _interestFactor.mul(_borrows).div(mantissaScale);
        uint _newBorrows = _borrows.add(_accumulatedInterest);
        uint _newReserve = _reserves.add(reserveFactor.mul(_accumulatedInterest).div(mantissaScale));
        uint _newAccruedFees = _accruedFees.add((reserveFactor.sub(killerRatio)).mul(_accumulatedInterest).div(mantissaScale));
        uint _newBorrowIndex = _oldBorrowIndex.add(_interestFactor.mul(_oldBorrowIndex).div(1e18));

        //Update storage
        totalBorrowed = _newBorrows;
        totalReserve = _newReserve;
        accruedFees = _newAccruedFees;
        borrowIndex = _newBorrowIndex;
        accrualBlockNumber = _currentBlock;

        return true;
    }

    


    // Admin Functions

    /**
    * @notice Set a new Controller
    * @dev Loads the new Controller for the Pool
    * @param  _newController address of the new Controller
    */
    function setNewController(address _newController) external override controllerOnly {
        controller = IPaladinController(_newController);
    }

    /**
    * @notice Set a new Interest Module
    * @dev Load a new Interest Module
    * @param _interestModule address of the new Interest Module
    */
    function setNewInterestModule(address _interestModule) external override adminOnly {
        interestModule = InterestInterface(_interestModule);
    }

    /**
    * @notice Set a new Delegator
    * @dev Change Delegator address
    * @param _delegator address of the new Delegator
    */
    function setNewDelegator(address _delegator) external override adminOnly {
        delegator = _delegator;
    }


    /**
    * @notice Set a new Minimum Borrow Length
    * @dev Change Minimum Borrow Length value
    * @param _length new Minimum Borrow Length
    */
    function updateMinBorrowLength(uint _length) external override adminOnly {
        require(_length > 0, Errors.INVALID_PARAMETERS);
        minBorrowLength = _length;
    }


    /**
    * @notice Update the Pool Reserve Factor & Killer Ratio
    * @dev Change Reserve Factor value & Killer Ratio value
    * @param _reserveFactor new % of fees to set as Reserve
    * @param _killerRatio new Ratio of Fees to pay the killer
    */
    function updatePoolFactors(uint _reserveFactor, uint _killerRatio) external override adminOnly {
        require(_reserveFactor > 0 && _killerRatio > 0 && _reserveFactor >= _killerRatio, 
            Errors.INVALID_PARAMETERS
        );
        reserveFactor = _reserveFactor;
        killerRatio = _killerRatio;
    }


    /**
    * @notice Add underlying in the Pool Reserve
    * @dev Transfer underlying token from the admin to the Pool
    * @param _amount Amount of underlying to transfer
    */
    function addReserve(uint _amount) external override adminOnly {
        require(_updateInterest());

        totalReserve = totalReserve.add(_amount);

        //Transfer from the admin to the Pool
        underlying.safeTransferFrom(admin, address(this), _amount);

        emit AddReserve(_amount);
    }

    /**
    * @notice Remove underlying from the Pool Reserve
    * @dev Transfer underlying token from the Pool to the admin
    * @param _amount Amount of underlying to transfer
    */
    function removeReserve(uint _amount) external override adminOnly {
        //Check if there is enough in the reserve
        require(_updateInterest());
        require(_amount <= underlyingBalance() && _amount <= totalReserve, Errors.RESERVE_FUNDS_INSUFFICIENT);

        totalReserve = totalReserve.sub(_amount);

        //Transfer underlying to the admin
        underlying.safeTransfer(admin, _amount);

        emit RemoveReserve(_amount);
    }

    /**
    * @notice Method to allow the Controller (or admin) to withdraw protocol fees
    * @dev Transfer underlying token from the Pool to the controller (or admin)
    * @param _amount Amount of underlying to transfer
    * @param _recipient Address to receive the token
    */
    function withdrawFees(uint _amount, address _recipient) external override controllerOnly {
        //Check if there is enough in the reserve
        require(_updateInterest());
        require(_amount<= accruedFees && _amount <= totalReserve, Errors.FEES_ACCRUED_INSUFFICIENT);

        //Substract from accruedFees (to track how much fees the Controller can withdraw since last time)
        //And also from the REserve, since the fees are part of the Reserve
        accruedFees = accruedFees.sub(_amount);
        totalReserve = totalReserve.sub(_amount);

        //Transfer fees to the recipient
        underlying.safeTransfer(_recipient, _amount);

        emit WithdrawFees(_amount);
    }

}

File 7 of 21 : IPalPool.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
pragma abicoder v2;
//SPDX-License-Identifier: MIT

/** @title palPool Interface  */
/// @author Paladin
interface IPalPool {

    //Events
    /** @notice Event when an user deposit tokens in the pool */
    event Deposit(address user, uint amount, address palPool);
    /** @notice Event when an user withdraw tokens from the pool */
    event Withdraw(address user, uint amount, address palPool);
    /** @notice Event when a loan is started */
    event NewLoan(
        address borrower,
        address delegatee,
        address underlying,
        uint amount,
        address palPool,
        address loanAddress,
        uint256 palLoanTokenId,
        uint startBlock);
    /** @notice Event when the fee amount in the loan is updated */
    event ExpandLoan(
        address borrower,
        address delegatee,
        address underlying,
        address palPool,
        uint newFeesAmount,
        address loanAddress,
        uint256 palLoanTokenId
    );
    /** @notice Event when the delegatee of the loan is updated */
    event ChangeLoanDelegatee(
        address borrower,
        address newDelegatee,
        address underlying,
        address palPool,
        address loanAddress,
        uint256 palLoanTokenId
    );
    /** @notice Event when a loan is ended */
    event CloseLoan(
        address borrower,
        address delegatee,
        address underlying,
        uint amount,
        address palPool,
        uint usedFees,
        address loanAddress,
        uint256 palLoanTokenId,
        bool wasKilled
    );

    /** @notice Reserve Events */
    event AddReserve(uint amount);
    event RemoveReserve(uint amount);
    event WithdrawFees(uint amount);


    //Functions
    function deposit(uint _amount) external returns(uint);
    function withdraw(uint _amount) external returns(uint);
    
    function borrow(address _delegatee, uint _amount, uint _feeAmount) external returns(uint);
    function expandBorrow(address _loanPool, uint _feeAmount) external returns(uint);
    function closeBorrow(address _loanPool) external;
    function killBorrow(address _loanPool) external;
    function changeBorrowDelegatee(address _loanPool, address _newDelegatee) external;

    function balanceOf(address _account) external view returns(uint);
    function underlyingBalanceOf(address _account) external view returns(uint);

    function isLoanOwner(address _loanAddress, address _user) external view returns(bool);
    function idOfLoan(address _loanAddress) external view returns(uint256);

    function getLoansPools() external view returns(address [] memory);
    function getLoansByBorrower(address _borrower) external view returns(address [] memory);
    function getBorrowData(address _loanAddress) external view returns(
        address _borrower,
        address _delegatee,
        address _loanPool,
        uint256 _palLoanTokenId,
        uint _amount,
        address _underlying,
        uint _feesAmount,
        uint _feesUsed,
        uint _startBlock,
        uint _closeBlock,
        bool _closed,
        bool _killed
    );

    function borrowRatePerBlock() external view returns (uint);
    function supplyRatePerBlock() external view returns (uint);

    function exchangeRateCurrent() external returns (uint);
    function exchangeRateStored() external view returns (uint);

    function minBorrowFees(uint _amount) external view returns (uint);

    function isKillable(address _loan) external view returns(bool);

    //Admin functions : 
    function setNewController(address _newController) external;
    function setNewInterestModule(address _interestModule) external;
    function setNewDelegator(address _delegator) external;

    function updateMinBorrowLength(uint _length) external;
    function updatePoolFactors(uint _reserveFactor, uint _killerRatio) external;

    function addReserve(uint _amount) external;
    function removeReserve(uint _amount) external;
    function withdrawFees(uint _amount, address _recipient) external;

}

File 8 of 21 : IPalToken.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT


/** @title simple PalToken Interface to be used inside the PalPool contract  */
/// @author Paladin
interface IPalToken {

    function mint(address _user, uint _toMint) external returns(bool);

    function burn(address _user, uint _toBurn) external returns(bool);

    function balanceOf(address owner) external view returns(uint);

    function totalSupply() external view returns (uint256);

}

File 9 of 21 : IERC20.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.7.6;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
  /**
   * @dev Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

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

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

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

  /**
   * @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);
}

File 10 of 21 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

import "./IERC20.sol";
import "./SafeMath.sol";
import "./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 SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @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, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 11 of 21 : Errors.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

library Errors {
    // Admin error
    string public constant CALLER_NOT_ADMIN = '1'; // 'The caller must be the admin'
    string public constant CALLER_NOT_CONTROLLER = '29'; // 'The caller must be the admin or the controller'
    string public constant CALLER_NOT_ALLOWED_POOL = '30';  // 'The caller must be a palPool listed in the controller'
    string public constant CALLER_NOT_MINTER = '31';
    string public constant CALLER_NOT_IMPLEMENTATION = '35'; // 'The caller must be the pending Implementation'

    // ERC20 type errors
    string public constant FAIL_TRANSFER = '2';
    string public constant FAIL_TRANSFER_FROM = '3';
    string public constant BALANCE_TOO_LOW = '4';
    string public constant ALLOWANCE_TOO_LOW = '5';
    string public constant SELF_TRANSFER = '6';

    // PalPool errors
    string public constant INSUFFICIENT_CASH = '9';
    string public constant INSUFFICIENT_BALANCE = '10';
    string public constant FAIL_DEPOSIT = '11';
    string public constant FAIL_LOAN_INITIATE = '12';
    string public constant FAIL_BORROW = '13';
    string public constant ZERO_BORROW = '27';
    string public constant BORROW_INSUFFICIENT_FEES = '23';
    string public constant LOAN_CLOSED = '14';
    string public constant NOT_LOAN_OWNER = '15';
    string public constant LOAN_OWNER = '16';
    string public constant FAIL_LOAN_EXPAND = '17';
    string public constant NOT_KILLABLE = '18';
    string public constant RESERVE_FUNDS_INSUFFICIENT = '19';
    string public constant FAIL_MINT = '20';
    string public constant FAIL_BURN = '21';
    string public constant FAIL_WITHDRAW = '24';
    string public constant FAIL_CLOSE_BORROW = '25';
    string public constant FAIL_KILL_BORROW = '26';
    string public constant ZERO_ADDRESS = '22';
    string public constant INVALID_PARAMETERS = '28'; 
    string public constant FAIL_LOAN_DELEGATEE_CHANGE = '32';
    string public constant FAIL_LOAN_TOKEN_BURN = '33';
    string public constant FEES_ACCRUED_INSUFFICIENT = '34';


    //Controller errors
    string public constant LIST_SIZES_NOT_EQUAL = '36';
    string public constant POOL_LIST_ALREADY_SET = '37';
    string public constant POOL_ALREADY_LISTED = '38';
    string public constant POOL_NOT_LISTED = '39';
    string public constant CALLER_NOT_POOL = '40';
    string public constant REWARDS_CASH_TOO_LOW = '41';
    string public constant FAIL_BECOME_IMPLEMENTATION = '42';
    string public constant INSUFFICIENT_DEPOSITED = '43';
    string public constant NOT_CLAIMABLE = '44';
    string public constant LOCKED = '45';
}

File 12 of 21 : Admin.sol
pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT


/** @title Admin contract  */
/// @author Paladin
contract Admin {

    /** @notice (Admin) Event when the contract admin is updated */
    event NewAdmin(address oldAdmin, address newAdmin);

    /** @dev Admin address for this contract */
    address payable internal admin;
    
    modifier adminOnly() {
        //allows only the admin of this contract to call the function
        require(msg.sender == admin, '1');
        _;
    }

        /**
    * @notice Set a new Admin
    * @dev Changes the address for the admin parameter
    * @param _newAdmin address of the new Controller Admin
    */
    function setNewAdmin(address payable _newAdmin) external adminOnly {
        address _oldAdmin = admin;
        admin = _newAdmin;

        emit NewAdmin(_oldAdmin, _newAdmin);
    }
}

File 13 of 21 : Clones.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address master) internal returns (address instance) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, master))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `master` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address master, bytes32 salt) internal returns (address instance) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, master))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address master, bytes32 salt, address deployer) internal pure returns (address predicted) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, master))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address master, bytes32 salt) internal view returns (address predicted) {
        return predictDeterministicAddress(master, salt, address(this));
    }
}

File 14 of 21 : PalPoolStorage.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

import "./IPaladinController.sol";
import "./IPalLoanToken.sol";
import "./interests/InterestInterface.sol";
import "./IPalPool.sol";
import "./IPalToken.sol";
import "./utils/IERC20.sol";

/** @title palPool Storage contract  */
/// @author Paladin
contract PalPoolStorage {

    /** @notice Struct of a Borrow */
    struct Borrow {
        //id of the palLoanToken
        uint256 tokenId;
        //address of the delegatee
        address delegatee;
        //address of the Loan Pool contract holding the loan
        address loan;
        //amount of the loan
        uint amount;
        //address of the underlying for this loan
        address underlying;
        //amount of fees (in the underlying token) paid by the borrower
        uint feesAmount;
        //amount of fees (in the underlying token) already used
        uint feesUsed;
        //borrow index at the loan creation
        uint borrowIndex;
        //start block for the Borrow
        uint startBlock;
        //block where the Borrow was closed
        uint closeBlock;
        //false if the loan is active, true if loan was closed or killed
        bool closed;
        //false when the loan is active, true if the loan was killed
        bool killed;
    }

    //palPool variables & Mappings

    /** @notice ERC721 palLoanToken */
    IPalLoanToken public palLoanToken;

    /** @notice Underlying ERC20 token of this Pool */
    IERC20 public underlying;

    /** @notice ERC20 palToken for this Pool */
    IPalToken public palToken;

    /** @dev Boolean to prevent reentry in some functions */
    bool internal entered = false;

    /** @notice Total of the current Reserve */
    uint public totalReserve;
    /** @notice Total of underlying tokens "borrowed" (in Loan Pool contracts) */
    uint public totalBorrowed;
    /** @notice Total fees accrued since last withdraw */
    /** (this amount id part of the Reserve : we should always have totalReserve >= accruedFees) */
    uint public accruedFees;

    /** @notice Minimum duration of a Borrow (in blocks) */
    uint public minBorrowLength = 45290;
    

    /** @dev Health Factor to kill a loan */
    uint public constant killFactor = 0.95e18;
    /** @dev Ratio of the borrow fees to pay the killer of a loan */
    uint public killerRatio = 0.1e18;

    /** @dev Base value to mint palTokens */
    uint internal constant initialExchangeRate = 1e18;
    /** @notice Part of the borrows interest to set as Reserves */
    uint public reserveFactor = 0.2e18;
    /** @notice Last block where the interest where updated for this pool */
    uint public accrualBlockNumber;
    /** @notice Borrow Index : increase at each interest update to represent borrows interests increasing */
    uint public borrowIndex;

    /** @dev Scale used to represent decimal values */
    uint constant internal mantissaScale = 1e18;

    /** @dev Mapping of Loan Pool contract address to Borrow struct */
    mapping (address => Borrow) internal loanToBorrow;
    /** @dev List of all Loans (active & closed) */
    address[] internal loans;
    /** @dev Current number of active Loans */
    uint public numberActiveLoans;

    //Modules

    /** @notice Paladin Controller contract */
    IPaladinController public controller;
    /** @dev Current Inetrest Module */
    InterestInterface internal interestModule;

    /** @dev Delegator for the underlying governance token */
    address internal delegator;
    
}

File 15 of 21 : IPalLoan.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

/** @title Interface for PalLoan contract  */
/// @author Paladin
interface IPalLoan {

    // Variables
    function underlying() external view returns(address);
    function amount() external view returns(uint);
    function borrower() external view returns(address);
    function delegatee() external view returns(address);
    function motherPool() external view returns(address);
    function feesAmount() external view returns(uint);

    // Functions
    function initiate(
        address _motherPool,
        address _borrower,
        address _underlying,
        address _delegatee,
        uint _amount,
        uint _feesAmount
    ) external returns(bool);
    function expand(uint _newFeesAmount) external returns(bool);
    function closeLoan(uint _usedAmount, address _currentBorrower) external;
    function killLoan(address _killer, uint _killerRatio) external;
    function changeDelegatee(address _delegatee) external returns(bool);
}

File 16 of 21 : IPalLoanToken.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
pragma abicoder v2;
//SPDX-License-Identifier: MIT

import "./utils/IERC721.sol";

/** @title palLoanToken Interface  */
/// @author Paladin
interface IPalLoanToken is IERC721 {

    //Events

    /** @notice Event when a new Loan Token is minted */
    event NewLoanToken(address palPool, address indexed owner, address indexed palLoan, uint256 indexed tokenId);
    /** @notice Event when a Loan Token is burned */
    event BurnLoanToken(address palPool, address indexed owner, address indexed palLoan, uint256 indexed tokenId);


    //Functions
    function mint(address to, address palPool, address palLoan) external returns(uint256);
    function burn(uint256 tokenId) external returns(bool);

    function tokenURI(uint256 tokenId) external view returns (string memory);

    function tokenOfByIndex(address owner, uint256 tokenIdex) external view returns (uint256);
    function loanOf(uint256 tokenId) external view returns(address);
    function poolOf(uint256 tokenId) external view returns(address);
    function loansOf(address owner) external view returns(address[] memory);
    function tokensOf(address owner) external view returns(uint256[] memory);
    function loansOfForPool(address owner, address palPool) external view returns(address[] memory);
    function allTokensOf(address owner) external view returns(uint256[] memory);
    function allLoansOf(address owner) external view returns(address[] memory);
    function allLoansOfForPool(address owner, address palPool) external view returns(address[] memory);
    function allOwnerOf(uint256 tokenId) external view returns(address);

    function isBurned(uint256 tokenId) external view returns(bool);

    //Admin functions
    function setNewController(address _newController) external;
    function setNewBaseURI(string memory _newBaseURI) external;

}

File 17 of 21 : InterestInterface.sol
//██████╗  █████╗ ██╗      █████╗ ██████╗ ██╗███╗   ██╗
//██╔══██╗██╔══██╗██║     ██╔══██╗██╔══██╗██║████╗  ██║
//██████╔╝███████║██║     ███████║██║  ██║██║██╔██╗ ██║
//██╔═══╝ ██╔══██║██║     ██╔══██║██║  ██║██║██║╚██╗██║
//██║     ██║  ██║███████╗██║  ██║██████╔╝██║██║ ╚████║
//╚═╝     ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝
                                                     

pragma solidity ^0.7.6;
//SPDX-License-Identifier: MIT

/** @title Interest Module Interface  */
/// @author Paladin
interface InterestInterface {

    function getSupplyRate(address palPool, uint cash, uint borrows, uint reserves, uint reserveFactor) external view returns(uint);
    function getBorrowRate(address palPool, uint cash, uint borrows, uint reserves) external view returns(uint);
}

File 18 of 21 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @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;

    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 make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 19 of 21 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;
/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @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`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // 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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 20 of 21 : IERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

import "./IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

File 21 of 21 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 999999
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"palToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"palPool","type":"address"},{"indexed":false,"internalType":"address","name":"palToken","type":"address"}],"name":"NewPalPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"palPool","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSupplySpeed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBorrowRatio","type":"uint256"},{"indexed":false,"internalType":"bool","name":"autoBorrowReward","type":"bool"}],"name":"PoolRewardsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"palPool","type":"address"},{"indexed":false,"internalType":"address","name":"palToken","type":"address"}],"name":"RemovePalPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"palToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accruedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_palToken","type":"address"},{"internalType":"address","name":"_palPool","type":"address"}],"name":"addNewPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"autoBorrowRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ControllerProxy","name":"proxy","type":"address"}],"name":"becomeImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"borrowPossible","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowRatios","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowRewardsStartBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"feesAmount","type":"uint256"},{"internalType":"address","name":"loanAddress","type":"address"}],"name":"borrowVerify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"address","name":"loanAddress","type":"address"}],"name":"claimLoanRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"address","name":"loanAddress","type":"address"}],"name":"claimableLoanRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"loanAddress","type":"address"}],"name":"closeBorrowVerify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"palToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"address","name":"dest","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositVerify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"estimateClaimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"address","name":"loanAddress","type":"address"},{"internalType":"uint256","name":"newFeesAmount","type":"uint256"}],"name":"expandBorrowVerify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getPalPools","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPalTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialRewardsIndex","outputs":[{"internalType":"uint224","name":"","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isLoanRewardClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"}],"name":"isPalPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"address","name":"killer","type":"address"},{"internalType":"address","name":"loanAddress","type":"address"}],"name":"killBorrowVerify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"loansBorrowRatios","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"palPools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"palTokenToPalPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"palTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"}],"name":"removePool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_palTokens","type":"address[]"},{"internalType":"address[]","name":"_palPools","type":"address[]"}],"name":"setInitialPools","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_newAdmin","type":"address"}],"name":"setNewAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newController","type":"address"}],"name":"setPoolsNewController","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"supplierDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"supplierRewardIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supplyRewardState","outputs":[{"internalType":"uint224","name":"index","type":"uint224"},{"internalType":"uint32","name":"blockNumber","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supplySpeeds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalSupplierDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupplyRewardSpeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"uint256","name":"newSupplySpeed","type":"uint256"},{"internalType":"uint256","name":"newBorrowRatio","type":"uint256"},{"internalType":"bool","name":"autoBorrowReward","type":"bool"}],"name":"updatePoolRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRewardTokenAddress","type":"address"}],"name":"updateRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"updateUserRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"palToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawFromPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawPossible","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"palPool","type":"address"},{"internalType":"address","name":"dest","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawVerify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50600080546001600160a01b031916331790556152ea806100326000396000f3fe608060405234801561001057600080fd5b506004361061032b5760003560e01c8063716e257f116101b2578063d448cca2116100f9578063f3fef3a3116100a2578063f7c618c11161007c578063f7c618c114610d15578063f8cf31cb14610d1d578063f949020f1461070a578063fac95a9e14610d505761032b565b8063f3fef3a314610b5e578063f5565ddf14610b97578063f69cd08b14610bee5761032b565b8063eaac8c32116100d3578063eaac8c3214610ac5578063eb3f9c9114610af8578063f31878ae14610b2b5761032b565b8063d448cca214610a82578063d8bd5c2914610a8a578063db5ea61314610a925761032b565b80639c5b00a31161015b578063bd8c16a511610135578063bd8c16a5146109dd578063c24525b514610a10578063cacd50f414610a495761032b565b80639c5b00a314610930578063a7d181ba14610963578063aa9e415a146109aa5761032b565b80638498fce41161018c5780638498fce41461089d5780638eec99c8146108f557806398ad9719146109285761032b565b8063716e257f14610330578063806f8ab2146107b157806382fc88101461086a5761032b565b8063402914f5116102765780634bb1a28d1161021f5780635c0d1b0d116101f95780635c0d1b0d146107b1578063603f20bd146107f45780636e8f42051461082f5761032b565b80634bb1a28d1461070a5780634c263c53146107435780635645a8551461077e5761032b565b806347e7ef241161025057806347e7ef241461065b57806348f105ef146106945780634b273370146106c75761032b565b8063402914f5146105d8578063418b8e011461060b57806345d465a2146106285761032b565b8063275bd214116102d857806338c20c3a116102b257806338c20c3a14610562578063396f7b231461059d5780633b7d0946146105a55761032b565b8063275bd214146104a25780632770cff5146105125780632d03d3ec146105455761032b565b8063128fced111610309578063128fced1146104075780631e83409a1461043a578063234ab4cb1461046f5761032b565b806308bc016e146103305780631024021914610389578063125f9e33146103d6575b600080fd5b6103756004803603606081101561034657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602081013582169160409091013516610d93565b604080519115158252519081900360200190f35b6103c46004803603604081101561039f57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610ebb565b60408051918252519081900360200190f35b6103de610ed0565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6103c46004803603602081101561041d57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16610ef1565b61046d6004803603602081101561045057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16610f03565b005b6103c46004803603602081101561048557600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166111f0565b6104d5600480360360208110156104b857600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611202565b604080517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909316835263ffffffff90911660208301528051918290030190f35b6103c46004803603602081101561052857600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661125a565b6103de6004803603602081101561055b57600080fd5b503561126c565b6103756004803603604081101561057857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160200135166112a3565b6103de6115ad565b610375600480360360208110156105bb57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166115c9565b6103c4600480360360208110156105ee57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611a79565b6103de6004803603602081101561062157600080fd5b5035611aa1565b6103756004803603602081101561063e57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611ab1565b6103756004803603604081101561067157600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135611ac6565b610375600480360360208110156106aa57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611efa565b610375600480360360608110156106dd57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135611fd7565b6103756004803603604081101561072057600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356120b7565b6103c46004803603604081101561075957600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661213a565b6103756004803603602081101561079457600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16612157565b610375600480360360608110156107c757600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602081013590911690604001356122fc565b61046d6004803603604081101561080a57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160200135166123ad565b6103c46004803603604081101561084557600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160200135166128bd565b6103de6004803603602081101561088057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166128da565b6108a5612902565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156108e15781810151838201526020016108c9565b505050509050019250505060405180910390f35b61046d6004803603602081101561090b57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16612971565b6108a5612a7f565b6103c46004803603602081101561094657600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16612aec565b61046d6004803603608081101561097957600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602081013590604081013590606001351515612afe565b6103c4600480360360208110156109c057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16612e0b565b6103c4600480360360208110156109f357600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16612e1d565b610a1861311d565b604080517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61046d60048036036040811015610a5f57600080fd5b508035906020013573ffffffffffffffffffffffffffffffffffffffff16613130565b6103c4613465565b6103de613555565b6103c460048036036020811015610aa857600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16613571565b61046d60048036036020811015610adb57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16613583565b61037560048036036020811015610b0e57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661371b565b61046d60048036036020811015610b4157600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16613730565b61037560048036036040811015610b7457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356137e9565b610375600480360360c0811015610bad57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020810135821691604082013581169160608101359160808201359160a0013516613bb5565b61037560048036036040811015610c0457600080fd5b810190602081018135640100000000811115610c1f57600080fd5b820183602082011115610c3157600080fd5b80359060200191846020830284011164010000000083111715610c5357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050640100000000811115610ca357600080fd5b820183602082011115610cb557600080fd5b80359060200191846020830284011164010000000083111715610cd757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550613ca2945050505050565b6103de614053565b61046d60048036036020811015610d3357600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16614074565b61037560048036036060811015610d6657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359160409091013516614146565b6000610d9e33611efa565b6040518060400160405280600281526020017f343000000000000000000000000000000000000000000000000000000000000081525090610e77576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610e3c578181015183820152602001610e24565b50505050905090810190601f168015610e695780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5073ffffffffffffffffffffffffffffffffffffffff84166000908152600f602052604090205460ff1615610eb057610eb08483614268565b5060015b9392505050565b6000610ec78383614427565b90505b92915050565b600654610100900473ffffffffffffffffffffffffffffffffffffffff1681565b600e6020526000908152604090205481565b60125460408051808201909152600281527f343500000000000000000000000000000000000000000000000000000000000060208201529060ff1615610fa4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b50601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610fd981613730565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600e602052604090205480156111c457600061100f614053565b90508073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561107857600080fd5b505afa15801561108c573d6000803e3d6000fd5b505050506040513d60208110156110a257600080fd5b505160408051808201909152600281527f3431000000000000000000000000000000000000000000000000000000000000602082015290831115611141576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5073ffffffffffffffffffffffffffffffffffffffff8084166000908152600e6020526040812055611176908216848461462f565b60408051838152905173ffffffffffffffffffffffffffffffffffffffff8516917f1f89f96333d3133000ee447473151fa9606543368f02271c9d95ae14f13bcc67919081900360200190a2505b5050601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b600d6020526000908152604090205481565b6007602052600090815260409020547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116907c0100000000000000000000000000000000000000000000000000000000900463ffffffff1682565b60086020526000908152604090205481565b6004818154811061127c57600080fd5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b6000805473ffffffffffffffffffffffffffffffffffffffff16331461132a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b61133382611efa565b156040518060400160405280600281526020017f3338000000000000000000000000000000000000000000000000000000000000815250906113d0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b506003805460018181019092557fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01805473ffffffffffffffffffffffffffffffffffffffff8087167fffffffffffffffffffffffff000000000000000000000000000000000000000092831681179093556004805494850190557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90930180549386169382168417905560009182526005602090815260408084208054909316851790925592825260079092522080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166114fb5780547fffffffff00000000000000000000000000000000000000000000000000000000166ec097ce7bc90715b34b9f10000000001781555b611504436146bc565b815463ffffffff919091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161781556040805173ffffffffffffffffffffffffffffffffffffffff80861682528616602082015281517f23437e5a11a835d7debb5f4c4261337d0cedb70993299eb75759a8a23d3a9b7b929181900390910190a15060019392505050565b60025473ffffffffffffffffffffffffffffffffffffffff1681565b6000805473ffffffffffffffffffffffffffffffffffffffff16331461165057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b61165982611efa565b6040518060400160405280600281526020017f3339000000000000000000000000000000000000000000000000000000000000815250906116f5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b506000600480548060200260200160405190810160405280929190818152602001828054801561175b57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611730575b5050505050905060006117796001835161473590919063ffffffff16565b905060005b8251811015611a6c578473ffffffffffffffffffffffffffffffffffffffff168382815181106117aa57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415611a64576000600382815481106117dd57fe5b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168083526005909152604090912080547fffffffffffffffffffffffff000000000000000000000000000000000000000016905560048054919250908490811061184657fe5b6000918252602090912001546004805473ffffffffffffffffffffffffffffffffffffffff909216918490811061187957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600383815481106118ce57fe5b6000918252602090912001546003805473ffffffffffffffffffffffffffffffffffffffff909216918490811061190157fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600480548061195457fe5b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905560038054806119b757fe5b6001900381819060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905590557fd602a50f89e680f70b82137ea55e3d0997dac209459429868fe10d249610cf6d8682604051808373ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a16001945050505050611a74565b60010161177e565b506000925050505b919050565b73ffffffffffffffffffffffffffffffffffffffff166000908152600e602052604090205490565b6003818154811061127c57600080fd5b60106020526000908152604090205460ff1681565b60125460408051808201909152600281527f3435000000000000000000000000000000000000000000000000000000000000602082015260009160ff1615611b69576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b50601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905573ffffffffffffffffffffffffffffffffffffffff80841660009081526005602052604090205416611bc681611efa565b6040518060400160405280600281526020017f333900000000000000000000000000000000000000000000000000000000000081525090611c62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b50604080517f70a0823100000000000000000000000000000000000000000000000000000000815233600482018190529151869173ffffffffffffffffffffffffffffffffffffffff8316916370a0823191602480820192602092909190829003018186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d6020811015611cfe57600080fd5b505160408051808201909152600281527f3130000000000000000000000000000000000000000000000000000000000000602082015290861115611d9d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b50611da783614777565b611db1838361499a565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600a6020908152604080832093861683529290522054611dee9086614aff565b73ffffffffffffffffffffffffffffffffffffffff8085166000818152600a60209081526040808320948816835293815283822094909455908152600b909252902054611e3b9086614aff565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600b6020526040902091909155611e73908216833088614b73565b6040805173ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528251908516927f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62928290030190a25050601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555060019392505050565b6000806004805480602002602001604051908101604052809291908181526020018280548015611f6057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611f35575b5050505050905060005b8151811015611fcd578373ffffffffffffffffffffffffffffffffffffffff16828281518110611f9657fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415611fc557600192505050611a74565b600101611f6a565b5060009392505050565b6000611fe233611efa565b6040518060400160405280600281526020017f34300000000000000000000000000000000000000000000000000000000000008152509061207e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5073ffffffffffffffffffffffffffffffffffffffff84166000908152600f602052604090205460ff1615610eb057610eb08484614c0e565b600080839050828173ffffffffffffffffffffffffffffffffffffffff166359356c5c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561210457600080fd5b505afa158015612118573d6000803e3d6000fd5b505050506040513d602081101561212e57600080fd5b50511015949350505050565b600960209081526000928352604080842090915290825290205481565b6000805473ffffffffffffffffffffffffffffffffffffffff1633146121de57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6000600480548060200260200160405190810160405280929190818152602001828054801561224357602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612218575b5050505050905060005b8151811015610eb057600082828151811061226457fe5b602002602001015190508073ffffffffffffffffffffffffffffffffffffffff166359baef40866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff168152602001915050600060405180830381600087803b1580156122d757600080fd5b505af11580156122eb573d6000803e3d6000fd5b50506001909301925061224d915050565b600061230733611efa565b6040518060400160405280600281526020017f3430000000000000000000000000000000000000000000000000000000000000815250906123a3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5050151592915050565b60125460408051808201909152600281527f343500000000000000000000000000000000000000000000000000000000000060208201529060ff161561244e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b50601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055604080517fc5d40b8a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301529151849260009283929185169163c5d40b8a9160248082019261018092909190829003018186803b1580156124f257600080fd5b505afa158015612506573d6000803e3d6000fd5b505050506040513d61018081101561251d57600080fd5b5080516101409091015160408051808201909152600281527f3135000000000000000000000000000000000000000000000000000000000000602082015291935091503373ffffffffffffffffffffffffffffffffffffffff8416146125de576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5060006125eb8686614427565b90508180156125f957508015155b6040518060400160405280600281526020017f343400000000000000000000000000000000000000000000000000000000000081525090612695576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5073ffffffffffffffffffffffffffffffffffffffff8516600090815260106020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556126ec614053565b90508073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561275557600080fd5b505afa158015612769573d6000803e3d6000fd5b505050506040513d602081101561277f57600080fd5b505160408051808201909152600281527f343100000000000000000000000000000000000000000000000000000000000060208201529083111561281e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5061284073ffffffffffffffffffffffffffffffffffffffff8216858461462f565b60408051838152905173ffffffffffffffffffffffffffffffffffffffff8616917f1f89f96333d3133000ee447473151fa9606543368f02271c9d95ae14f13bcc67919081900360200190a25050601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555050505050565b600a60209081526000928352604080842090915290825290205481565b60056020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6060600380548060200260200160405190810160405280929190818152602001828054801561296757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161293c575b5050505050905090565b60005473ffffffffffffffffffffffffffffffffffffffff1633146129f757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040805191909216808252602082019390935281517ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc929181900390910190a15050565b606060048054806020026020016040519081016040528092919081815260200182805480156129675760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161293c575050505050905090565b600c6020526000908152604090205481565b60005473ffffffffffffffffffffffffffffffffffffffff163314612b8457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b612b8d84611efa565b6040518060400160405280600281526020017f333900000000000000000000000000000000000000000000000000000000000081525090612c29576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5073ffffffffffffffffffffffffffffffffffffffff84166000908152600860205260409020548314612c8857612c5f84614777565b73ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090208390555b73ffffffffffffffffffffffffffffffffffffffff84166000908152600c60205260409020548214612cdd5773ffffffffffffffffffffffffffffffffffffffff84166000908152600c602052604090208290555b73ffffffffffffffffffffffffffffffffffffffff8416600090815260116020526040902054158015612d0f57508115155b15612d415773ffffffffffffffffffffffffffffffffffffffff84166000908152601160205260409020439055612d6d565b81612d6d5773ffffffffffffffffffffffffffffffffffffffff84166000908152601160205260408120555b73ffffffffffffffffffffffffffffffffffffffff84166000818152600f602090815260409182902080548515157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116811790915582519384529083018690528282018590526060830152517f510b9af7cf5e00211f646a615d08f77218872cd310a811f6913e91c1167bbf009181900360800190a150505050565b600b6020526000908152604090205481565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600e602090815260408083205460048054835181860281018601909452808452919385939290830182828015612ea557602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612e7a575b5050505050905060005b815181101561311457600060076000848481518110612eca57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90047bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169050600060096000858581518110612f6a57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600014801561300b57506ec097ce7bc90715b34b9f10000000008210155b1561302257506ec097ce7bc90715b34b9f10000000005b600061302e8383614735565b90508015613109576000600a600087878151811061304857fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060006130f86ec097ce7bc90715b34b9f10000000006130f28585614c4990919063ffffffff16565b90614cbc565b90506131048882614aff565b975050505b505050600101612eaf565b50909392505050565b6ec097ce7bc90715b34b9f100000000081565b60005473ffffffffffffffffffffffffffffffffffffffff1633146131b657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60408051808201909152600281527f3232000000000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff8216613266576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5060408051808201909152600281527f3238000000000000000000000000000000000000000000000000000000000000602082015282613301576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b50600061330c614053565b90508073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561337557600080fd5b505afa158015613389573d6000803e3d6000fd5b505050506040513d602081101561339f57600080fd5b505160408051808201909152600181527f340000000000000000000000000000000000000000000000000000000000000060208201529084111561343e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5061346073ffffffffffffffffffffffffffffffffffffffff8216838561462f565b505050565b60008060048054806020026020016040519081016040528092919081815260200182805480156134cb57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116134a0575b505050505090506000805b825181101561354e57613544600860008584815181106134f257fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483614aff90919063ffffffff16565b91506001016134d6565b5091505090565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b60116020526000908152604090205481565b60005473ffffffffffffffffffffffffffffffffffffffff16331461360957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b8073ffffffffffffffffffffffffffffffffffffffff166315ba56e56040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561365157600080fd5b505af1158015613665573d6000803e3d6000fd5b505050506040513d602081101561367b57600080fd5b505160408051808201909152600281527f3432000000000000000000000000000000000000000000000000000000000000602082015290613717576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5050565b600f6020526000908152604090205460ff1681565b6000600480548060200260200160405190810160405280929190818152602001828054801561379557602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161376a575b5050505050905060005b8151811015613460576137c48282815181106137b757fe5b6020026020010151614777565b6137e18282815181106137d357fe5b60200260200101518461499a565b60010161379f565b60125460408051808201909152600281527f3435000000000000000000000000000000000000000000000000000000000000602082015260009160ff161561388c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b50601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905573ffffffffffffffffffffffffffffffffffffffff808416600090815260056020526040902054166138e981611efa565b6040518060400160405280600281526020017f333900000000000000000000000000000000000000000000000000000000000081525090613985576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5073ffffffffffffffffffffffffffffffffffffffff81166000908152600a602090815260408083203380855290835292819020548151808301909252600282527f34330000000000000000000000000000000000000000000000000000000000009282019290925290851115613a57576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b50613a6182614777565b613a6b828261499a565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600a60209081526040808320938516835292905220548590613aaa9086614735565b73ffffffffffffffffffffffffffffffffffffffff8085166000818152600a60209081526040808320948816835293815283822094909455908152600b909252902054613af79086614735565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600b6020526040902091909155613b2e908216838761462f565b6040805173ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528251908516927f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb928290030190a25050601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555060019392505050565b6000613bc033611efa565b6040518060400160405280600281526020017f343000000000000000000000000000000000000000000000000000000000000081525090613c5c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5073ffffffffffffffffffffffffffffffffffffffff87166000908152600f602052604090205460ff1615613c9557613c958783614c0e565b5060019695505050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff163314613d2957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60065460408051808201909152600281527f333700000000000000000000000000000000000000000000000000000000000060208201529060ff1615613dca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b5081518351146040518060400160405280600281526020017f333600000000000000000000000000000000000000000000000000000000000081525090613e6c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b508151613e809060049060208501906151ad565b508251613e949060039060208601906151ad565b50600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560005b8251811015610eb057600060076000858481518110613ede57fe5b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002080549091507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613f6d5780547fffffffff00000000000000000000000000000000000000000000000000000000166ec097ce7bc90715b34b9f10000000001781555b613f76436146bc565b815463ffffffff919091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161781558351849083908110613fd157fe5b602002602001015160056000878581518110613fe957fe5b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683529082019290925260400160002080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169290911691909117905550600101613ec3565b600654610100900473ffffffffffffffffffffffffffffffffffffffff1690565b60005473ffffffffffffffffffffffffffffffffffffffff1633146140fa57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6006805473ffffffffffffffffffffffffffffffffffffffff909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b6000805473ffffffffffffffffffffffffffffffffffffffff1633146141cd57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f3100000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b604080517fdd2cc3f30000000000000000000000000000000000000000000000000000000081526004810185905273ffffffffffffffffffffffffffffffffffffffff84811660248301529151869283169163dd2cc3f391604480830192600092919082900301818387803b15801561424557600080fd5b505af1158015614259573d6000803e3d6000fd5b50600198975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d602052604090205480158015906142c3575073ffffffffffffffffffffffffffffffffffffffff821660009081526010602052604090205460ff16155b156134605760008390506000808273ffffffffffffffffffffffffffffffffffffffff1663c5d40b8a866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1681526020019150506101806040518083038186803b15801561433857600080fd5b505afa15801561434c573d6000803e3d6000fd5b505050506040513d61018081101561436357600080fd5b50805160e09091015190925090506000614389670de0b6b3a76400006130f28488614c49565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600e60205260409020549091506143bc9082614aff565b73ffffffffffffffffffffffffffffffffffffffff9384166000908152600e60209081526040808320939093559488168152601090945290922080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526010602052604081205460ff161561445d57506000610eca565b600083905060008060008373ffffffffffffffffffffffffffffffffffffffff1663c5d40b8a876040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1681526020019150506101806040518083038186803b1580156144cf57600080fd5b505afa1580156144e3573d6000803e3d6000fd5b505050506040513d6101808110156144fa57600080fd5b5060e081015161010082015161014090920151909450909250905080614527576000945050505050610eca565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260116020526040902054158061457d575073ffffffffffffffffffffffffffffffffffffffff871660009081526011602052604090205482105b1561458f576000945050505050610eca565b73ffffffffffffffffffffffffffffffffffffffff86166000908152600d60205260408120546145e45773ffffffffffffffffffffffffffffffffffffffff88166000908152600c602052604090205461460b565b73ffffffffffffffffffffffffffffffffffffffff87166000908152600d60205260409020545b9050614623670de0b6b3a76400006130f28684614c49565b98975050505050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052613460908490614cfe565b6000640100000000821061473157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4e756d626572206973206f766572203332206269747300000000000000000000604482015290519081900360640190fd5b5090565b6000610ec783836040518060400160405280601f81526020017f536166654d6174683a207375627472616374696f6e20756e646572666c6f7700815250614dd6565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260076020908152604080832060089092528220548154919243926147e290849063ffffffff7c010000000000000000000000000000000000000000000000000000000090910481169061473516565b90506000811180156147f45750600082115b156149365773ffffffffffffffffffffffffffffffffffffffff85166000908152600b60205260408120549061482a8385614c49565b9050600080831161483c576000614859565b614859836130f2846ec097ce7bc90715b34b9f1000000000614c49565b875490915061488f9061488a907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1683614aff565b614e4a565b87547fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff919091161787556148e0866146bc565b875463ffffffff919091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911617875550614993915050565b801561499357614945836146bc565b845463ffffffff919091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161784555b5050505050565b73ffffffffffffffffffffffffffffffffffffffff828116600090815260076020908152604080832080546009845282852095871685529490925290912080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90931690819055909180158015614a1d57506ec097ce7bc90715b34b9f10000000008210155b15614a3457506ec097ce7bc90715b34b9f10000000005b6000614a408383614735565b90508015614af75773ffffffffffffffffffffffffffffffffffffffff8087166000908152600a6020908152604080832093891683529290529081205490614a9b6ec097ce7bc90715b34b9f10000000006130f28486614c49565b73ffffffffffffffffffffffffffffffffffffffff88166000908152600e6020526040902054909150614ace9082614aff565b73ffffffffffffffffffffffffffffffffffffffff88166000908152600e602052604090205550505b505050505050565b600082820183811015610ec757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6040805173ffffffffffffffffffffffffffffffffffffffff80861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000179052614c08908590614cfe565b50505050565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600c6020908152604080832054939094168252600d90529190912055565b600082614c5857506000610eca565b82820282848281614c6557fe5b0414610ec7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061526a6021913960400191505060405180910390fd5b6000610ec783836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614ed7565b6000614d60826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16614f569092919063ffffffff16565b80519091501561346057808060200190516020811015614d7f57600080fd5b5051613460576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a81526020018061528b602a913960400191505060405180910390fd5b60008184841115614e42576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b505050900390565b60007c0100000000000000000000000000000000000000000000000000000000821061473157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4e756d626572206973206f766572203232342062697473000000000000000000604482015290519081900360640190fd5b60008183614f40576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152835160248401528351909283926044909101919085019080838360008315610e3c578181015183820152602001610e24565b506000838581614f4c57fe5b0495945050505050565b6060614f658484600085614f6d565b949350505050565b606082471015614fc8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806152446026913960400191505060405180910390fd5b614fd185615127565b61503c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040518082805190602001908083835b602083106150a557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101615068565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114615107576040519150601f19603f3d011682016040523d82523d6000602084013e61510c565b606091505b509150915061511c82828661512d565b979650505050505050565b3b151590565b6060831561513c575081610eb4565b82511561514c5782518084602001fd5b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201818152845160248401528451859391928392604401919085019080838360008315610e3c578181015183820152602001610e24565b828054828255906000526020600020908101928215615227579160200282015b8281111561522757825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9091161782556020909201916001909101906151cd565b506147319291505b80821115614731576000815560010161522f56fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220178f91d826d7bd7447e90f7b0343d4e5faad95eab8ed24cd09725753f17221cd64736f6c63430007060033

Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.

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