ETH Price: $2,478.24 (+1.58%)

Contract

0xb06B7770fcc166FfF8c978a5616f796184bc16f5
 
Transaction Hash
Method
Block
From
To
Sell Slashed175371742023-06-22 19:15:47439 days ago1687461347IN
0xb06B7770...184bc16f5
0 ETH0.0073563918.55841864
Sell Slashed175371332023-06-22 19:07:35439 days ago1687460855IN
0xb06B7770...184bc16f5
0 ETH0.0101113725.48705523
Slash Loan175371312023-06-22 19:07:11439 days ago1687460831IN
0xb06B7770...184bc16f5
0 ETH0.004171226.74464757
Slash Loan175371292023-06-22 19:06:47439 days ago1687460807IN
0xb06B7770...184bc16f5
0 ETH0.0047193628.07840296
Slash Loan175371262023-06-22 19:06:11439 days ago1687460771IN
0xb06B7770...184bc16f5
0 ETH0.0052523730.38196457
Slash Loan175371162023-06-22 19:04:11439 days ago1687460651IN
0xb06B7770...184bc16f5
0 ETH0.0088061643.04846078
Approve170573322023-04-16 5:14:59507 days ago1681622099IN
0xb06B7770...184bc16f5
0 ETH0.0007015823.97532387
Collect Fees170572622023-04-16 5:00:23507 days ago1681621223IN
0xb06B7770...184bc16f5
0 ETH0.0041477123.56174104
Make Loan Paymen...170256992023-04-11 15:20:35512 days ago1681226435IN
0xb06B7770...184bc16f5
0 ETH0.0097056534.5033452
Make Loan Paymen...167567122023-03-04 17:58:59549 days ago1677952739IN
0xb06B7770...184bc16f5
0 ETH0.0065144123.15859141
Make Loan Paymen...165815862023-02-08 4:10:35574 days ago1675829435IN
0xb06B7770...184bc16f5
0 ETH0.0085790728.75062302
Make Loan Paymen...165815802023-02-08 4:09:23574 days ago1675829363IN
0xb06B7770...184bc16f5
0 ETH0.0088246729.57370652
Make Loan Paymen...165808902023-02-08 1:50:59574 days ago1675821059IN
0xb06B7770...184bc16f5
0 ETH0.010621535.59534051
Approve Loan163696312023-01-09 13:45:47604 days ago1673271947IN
0xb06B7770...184bc16f5
0 ETH0.0075199115.36778551
Request Loan163696212023-01-09 13:43:47604 days ago1673271827IN
0xb06B7770...184bc16f5
0 ETH0.0037776316.59573639
Approve163696032023-01-09 13:40:11604 days ago1673271611IN
0xb06B7770...184bc16f5
0 ETH0.000785916.95117992
Deposit163696012023-01-09 13:39:47604 days ago1673271587IN
0xb06B7770...184bc16f5
0 ETH0.0057293617.77027993
Approve Loan163108252023-01-01 8:48:47612 days ago1672562927IN
0xb06B7770...184bc16f5
0 ETH0.0073250914.969648
Request Loan163108172023-01-01 8:47:11612 days ago1672562831IN
0xb06B7770...184bc16f5
0 ETH0.0031120713.67111809
Collect Fees162280792022-12-20 19:44:59623 days ago1671565499IN
0xb06B7770...184bc16f5
0 ETH0.0036717620.85803709
Make Loan Paymen...162260722022-12-20 13:01:23624 days ago1671541283IN
0xb06B7770...184bc16f5
0 ETH0.0056389520.04632747
Make Loan Paymen...162260672022-12-20 13:00:23624 days ago1671541223IN
0xb06B7770...184bc16f5
0 ETH0.004969817.66753645
Make Loan Paymen...161841402022-12-14 16:30:47630 days ago1671035447IN
0xb06B7770...184bc16f5
0 ETH0.0063155622.45165562
Make Loan Paymen...159231462022-11-08 5:12:59666 days ago1667884379IN
0xb06B7770...184bc16f5
0 ETH0.0060590221.53967772
Make Loan Paymen...158288712022-10-26 1:10:23679 days ago1666746623IN
0xb06B7770...184bc16f5
0 ETH0.0038806413.00501889
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
148284482022-05-23 8:15:38835 days ago1653293738  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x768d2e6C...536251689
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
BankingNode

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 17 : BankingNode.sol
// SPDX-License-Identifier: MIT

// NOTE: BankingNode.sol should only be created through the BNPLFactory contract to
// ensure compatibility of baseToken and minimum bond amounts. Before interacting,
// please ensure that the contract deployer was BNPLFactory.sol

pragma solidity ^0.8.0;

import "ERC20.sol";
import "Pausable.sol";
import "ReentrancyGuard.sol";
import "ILendingPool.sol";
import "ILendingPoolAddressesProvider.sol";
import "IAaveIncentivesController.sol";
import "UniswapV2Library.sol";
import "TransferHelper.sol";

//CUSTOM ERRORS

//occurs when trying to do privledged functions
error InvalidUser(address requiredUser);
//occurs when users try to add funds if node operator hasn't maintaioned enough pledged BNPL
error NodeInactive();
//occurs when trying to interact without being KYC's (if node requires it)
error KYCNotApproved();
//occurs when trying to pay loans that are completed or not started
error NoPrincipalRemaining();
//occurs when trying to swap/deposit/withdraw a zero
error ZeroInput();
//occurs if interest rate, loanAmount, or paymentInterval or is applied as 0
error InvalidLoanInput();
//occurs if trying to apply for a loan with >5 year loan length
error MaximumLoanDurationExceeded();
//occurs if user tries to withdraw collateral while loan is still ongoing
error LoanStillOngoing();
//edge case occurence if all BNPL is slashed, but there are still BNPL shares
error DonationRequired();
//occurs if operator tries to unstake while there are active loans
error ActiveLoansOngoing();
//occurs when trying to withdraw too much funds
error InsufficientBalance();
//occurs during swaps, if amount received is lower than minOut (slippage tolerance exceeded)
error InsufficentOutput();
//occurs if trying to approve a loan that has already started
error LoanAlreadyStarted();
//occurs if trying to approve a loan without enough collateral posted
error InsufficientCollateral();
//occurs when trying to slash a loan that is not yet considered defaulted
error LoanNotExpired();
//occurs is trying to slash an already slashed loan
error LoanAlreadySlashed();
//occurs if trying to withdraw staked BNPL where 7 day unbonding hasnt passed
error LoanStillUnbonding();
//occurs if trying to post baseToken as collateral
error InvalidCollateral();
//first deposit to prevent edge case must be at least 10M wei
error InvalidInitialDeposit();

contract BankingNode is ERC20("BNPL USD", "pUSD") {
    //Node specific variables
    address public operator;
    address public baseToken; //base liquidity token, e.g. USDT or USDC
    uint256 public gracePeriod;
    bool public requireKYC;

    //variables used for swaps, private to reduce contract size
    address private uniswapFactory;
    address private WETH;
    uint256 private incrementor;

    //constants set by factory
    address public BNPL;
    ILendingPoolAddressesProvider public lendingPoolProvider;
    address public immutable bnplFactory;
    //used by treasury can be private
    IAaveIncentivesController private aaveRewardController;
    address private treasury;

    //For loans
    mapping(uint256 => Loan) public idToLoan;
    uint256[] public pendingRequests;
    uint256[] public currentLoans;
    mapping(uint256 => uint256) defaultedLoans;
    uint256 public defaultedLoanCount;

    //For Staking, Slashing and Balances
    uint256 public accountsReceiveable;
    mapping(address => bool) public whitelistedAddresses;
    mapping(address => uint256) public unbondBlock;
    mapping(uint256 => address) public loanToAgent;
    uint256 public slashingBalance;
    mapping(address => uint256) public stakingShares;
    //can be private as there is a getter function for staking balance
    uint256 public totalStakingShares;

    uint256 public unbondingAmount;
    mapping(address => uint256) public unbondingShares;
    //can be private as there is getter function for unbonding balance
    uint256 private totalUnbondingShares;
    uint256 public timeCreated;

    //For Collateral in loans
    mapping(address => uint256) public collateralOwed;

    struct Loan {
        address borrower;
        bool interestOnly; //interest only or principal + interest
        uint256 loanStartTime; //unix timestamp of start
        uint256 loanAmount;
        uint256 paymentInterval; //unix interval of payment (e.g. monthly = 2,628,000)
        uint256 interestRate; //interest rate per peiod * 10000, e.g., 10% on a 12 month loan = : 0.1 * 10000 / 12 = 83
        uint256 numberOfPayments;
        uint256 principalRemaining;
        uint256 paymentsMade;
        address collateral;
        uint256 collateralAmount;
        bool isSlashed;
    }

    //EVENTS
    event LoanRequest(uint256 loanId, string message);
    event collateralWithdrawn(
        uint256 loanId,
        address collateral,
        uint256 collateralAmount
    );
    event approvedLoan(uint256 loanId);
    event loanPaymentMade(uint256 loanId);
    event loanRepaidEarly(uint256 loanId);
    event baseTokenDeposit(address user, uint256 amount);
    event baseTokenWithdrawn(address user, uint256 amount);
    event feesCollected(uint256 operatorFees, uint256 stakerFees);
    event baseTokensDonated(uint256 amount);
    event loanSlashed(uint256 loanId);
    event slashingSale(uint256 bnplSold, uint256 baseTokenRecovered);
    event bnplStaked(address user, uint256 bnplStaked);
    event unbondingInitiated(address user, uint256 unbondAmount);
    event bnplWithdrawn(address user, uint256 bnplWithdrawn);
    event KYCRequirementChanged(bool newStatus);

    constructor() {
        bnplFactory = msg.sender;
    }

    // MODIFIERS

    /**
     * Ensure a node is active for deposit, stake functions
     * Require KYC is also batched in
     */
    modifier ensureNodeActive() {
        address _operator = operator;
        if (msg.sender != bnplFactory && msg.sender != _operator) {
            if (getBNPLBalance(_operator) < 0x13DA329B6336471800000) {
                revert NodeInactive();
            }
            if (requireKYC && whitelistedAddresses[msg.sender] == false) {
                revert KYCNotApproved();
            }
        }
        _;
    }

    /**
     * Ensure that the loan has principal to be paid
     */
    modifier ensurePrincipalRemaining(uint256 loanId) {
        if (idToLoan[loanId].principalRemaining == 0) {
            revert NoPrincipalRemaining();
        }
        _;
    }

    /**
     * For operator only functions
     */
    modifier operatorOnly() {
        address _operator = operator;
        if (msg.sender != _operator) {
            revert InvalidUser(_operator);
        }
        _;
    }

    /**
     * Requires input value to be non-zero
     */
    modifier nonZeroInput(uint256 input) {
        if (input == 0) {
            revert ZeroInput();
        }
        _;
    }

    /**
     * Ensures collateral is not the baseToken
     */
    modifier nonBaseToken(address collateral) {
        if (collateral == baseToken) {
            revert InvalidCollateral();
        }
        _;
    }

    //STATE CHANGING FUNCTIONS

    /**
     * Called once by the factory at time of deployment
     */
    function initialize(
        address _baseToken,
        address _BNPL,
        bool _requireKYC,
        address _operator,
        uint256 _gracePeriod,
        address _lendingPoolProvider,
        address _WETH,
        address _aaveDistributionController,
        address _uniswapFactory
    ) external {
        //only to be done by factory, no need for error msgs in here as not used by users
        require(msg.sender == bnplFactory);
        baseToken = _baseToken;
        BNPL = _BNPL;
        requireKYC = _requireKYC;
        operator = _operator;
        gracePeriod = _gracePeriod;
        lendingPoolProvider = ILendingPoolAddressesProvider(
            _lendingPoolProvider
        );
        aaveRewardController = IAaveIncentivesController(
            _aaveDistributionController
        );
        WETH = _WETH;
        uniswapFactory = _uniswapFactory;
        treasury = address(0x27a99802FC48b57670846AbFFf5F2DcDE8a6fC29);
        timeCreated = block.timestamp;
        //decimal check on baseToken and aToken to make sure math logic on future steps
        require(
            ERC20(_baseToken).decimals() ==
                ERC20(
                    _getLendingPool().getReserveData(_baseToken).aTokenAddress
                ).decimals()
        );
    }

    /**
     * Request a loan from the banking node
     * Saves the loan with the operator able to approve or reject
     * Can post collateral if chosen, collateral accepted is anything that is accepted by aave
     * Collateral can not be the same token as baseToken
     */
    function requestLoan(
        uint256 loanAmount,
        uint256 paymentInterval,
        uint256 numberOfPayments,
        uint256 interestRate,
        bool interestOnly,
        address collateral,
        uint256 collateralAmount,
        address agent,
        string memory message
    )
        external
        ensureNodeActive
        nonBaseToken(collateral)
        returns (uint256 requestId)
    {
        if (
            loanAmount < 10000000 ||
            paymentInterval == 0 ||
            interestRate == 0 ||
            numberOfPayments == 0
        ) {
            revert InvalidLoanInput();
        }
        //157,680,000 seconds in 5 years
        if (paymentInterval * numberOfPayments > 157680000) {
            revert MaximumLoanDurationExceeded();
        }
        requestId = incrementor;
        incrementor++;
        pendingRequests.push(requestId);
        idToLoan[requestId] = Loan(
            msg.sender, //set borrower
            interestOnly,
            0, //start time initiated to 0
            loanAmount,
            paymentInterval, //interval of payments (e.g. Monthly)
            interestRate, //annualized interest rate per period * 10000 (e.g. 12 month loan 10% = 83)
            numberOfPayments,
            0, //initalize principalRemaining to 0
            0, //intialize paymentsMade to 0
            collateral,
            collateralAmount,
            false
        );
        //post the collateral if any
        if (collateralAmount > 0) {
            //update the collateral owed (interest accrued on collateral is given to lend)
            collateralOwed[collateral] += collateralAmount;
            TransferHelper.safeTransferFrom(
                collateral,
                msg.sender,
                address(this),
                collateralAmount
            );
            //deposit the collateral in AAVE to accrue interest
            _depositToLendingPool(collateral, collateralAmount);
        }
        //save the agent of the loan
        loanToAgent[requestId] = agent;

        emit LoanRequest(requestId, message);
    }

    /**
     * Withdraw the collateral from a loan
     * Loan must have no principal remaining (not approved, or payments finsihed)
     */
    function withdrawCollateral(uint256 loanId) external {
        Loan storage loan = idToLoan[loanId];
        address collateral = loan.collateral;
        uint256 amount = loan.collateralAmount;

        //must be the borrower or operator to withdraw, and loan must be either paid/not initiated
        if (msg.sender != loan.borrower) {
            revert InvalidUser(loan.borrower);
        }
        if (loan.principalRemaining > 0) {
            revert LoanStillOngoing();
        }

        //update the amounts
        collateralOwed[collateral] -= amount;
        loan.collateralAmount = 0;

        //no need to check if loan is slashed as collateral amont set to 0 on slashing
        _withdrawFromLendingPool(collateral, amount, loan.borrower);
        emit collateralWithdrawn(loanId, collateral, amount);
    }

    /**
     * Collect AAVE rewards to be sent to the treasury
     */
    function collectAaveRewards(address[] calldata assets) external {
        uint256 rewardAmount = aaveRewardController.getUserUnclaimedRewards(
            address(this)
        );
        address _treasuy = treasury;
        if (rewardAmount == 0) {
            revert ZeroInput();
        }
        //claim rewards to the treasury
        aaveRewardController.claimRewards(assets, rewardAmount, _treasuy);
        //no need for event as its a function that will only be used by treasury
    }

    /**
     * Collect the interest earnt on collateral posted to distribute to stakers
     * Collateral can not be the same as baseToken
     */
    function collectCollateralFees(address collateral)
        external
        nonBaseToken(collateral)
    {
        //get the aToken address
        ILendingPool lendingPool = _getLendingPool();
        address _bnpl = BNPL;
        uint256 feesAccrued = IERC20(
            lendingPool.getReserveData(collateral).aTokenAddress
        ).balanceOf(address(this)) - collateralOwed[collateral];
        //ensure there is collateral to collect inside of _swap
        lendingPool.withdraw(collateral, feesAccrued, address(this));
        //no slippage for small swaps
        _swapToken(collateral, _bnpl, 0, feesAccrued);
    }

    /*
     * Make a loan payment
     */
    function makeLoanPayment(uint256 loanId)
        external
        ensurePrincipalRemaining(loanId)
    {
        Loan storage loan = idToLoan[loanId];
        uint256 paymentAmount = getNextPayment(loanId);
        uint256 interestPortion = (loan.principalRemaining *
            loan.interestRate) / 10000;
        address _baseToken = baseToken;
        loan.paymentsMade++;
        //reduce accounts receiveable and loan principal if principal + interest payment
        bool finalPayment = loan.paymentsMade == loan.numberOfPayments;

        if (!loan.interestOnly) {
            uint256 principalPortion = paymentAmount - interestPortion;
            loan.principalRemaining -= principalPortion;
            accountsReceiveable -= principalPortion;
        } else {
            //interest only, principal change only on final payment
            if (finalPayment) {
                accountsReceiveable -= loan.principalRemaining;
                loan.principalRemaining = 0;
            }
        }
        //make payment
        TransferHelper.safeTransferFrom(
            _baseToken,
            msg.sender,
            address(this),
            paymentAmount
        );
        //deposit the tokens into AAVE on behalf of the pool contract, withholding 30% and the interest as baseToken
        _depositToLendingPool(
            _baseToken,
            paymentAmount - ((interestPortion * 3) / 10)
        );
        //remove if final payment
        if (finalPayment) {
            _removeCurrentLoan(loanId);
        }
        //increment the loan status

        emit loanPaymentMade(loanId);
    }

    /**
     * Repay remaining balance to save on interest cost
     * Payment amount is remaining principal + 1 period of interest
     */
    function repayEarly(uint256 loanId)
        external
        ensurePrincipalRemaining(loanId)
    {
        Loan storage loan = idToLoan[loanId];
        uint256 principalLeft = loan.principalRemaining;
        //make a payment of remaining principal + 1 period of interest
        uint256 interestAmount = (principalLeft * loan.interestRate) / 10000;
        uint256 paymentAmount = principalLeft + interestAmount;
        address _baseToken = baseToken;

        //update accounts
        accountsReceiveable -= principalLeft;
        loan.principalRemaining = 0;
        //increment the loan status to final and remove from current loans array
        loan.paymentsMade = loan.numberOfPayments;
        _removeCurrentLoan(loanId);

        //make payment
        TransferHelper.safeTransferFrom(
            _baseToken,
            msg.sender,
            address(this),
            paymentAmount
        );
        //deposit withholding 30% of the interest as fees
        _depositToLendingPool(
            _baseToken,
            paymentAmount - ((interestAmount * 3) / 10)
        );

        emit loanRepaidEarly(loanId);
    }

    /**
     * Converts the baseToken (e.g. USDT) 20% BNPL for stakers, and sends 10% to the Banking Node Operator
     * Slippage set to 0 here as they would be small purchases of BNPL
     */
    function collectFees() external {
        //requirement check for nonzero inside of _swap
        //33% to go to operator as baseToken
        address _baseToken = baseToken;
        address _bnpl = BNPL;
        address _operator = operator;
        uint256 _operatorFees = IERC20(_baseToken).balanceOf(address(this)) / 3;
        TransferHelper.safeTransfer(_baseToken, _operator, _operatorFees);
        //remainder (67%) is traded for staking rewards
        //no need for slippage on small trade
        uint256 _stakingRewards = _swapToken(
            _baseToken,
            _bnpl,
            0,
            IERC20(_baseToken).balanceOf(address(this))
        );
        emit feesCollected(_operatorFees, _stakingRewards);
    }

    /**
     * Deposit liquidity to the banking node in the baseToken (e.g. usdt) specified
     * Mints tokens, with check on decimals of base tokens
     */
    function deposit(uint256 _amount)
        external
        ensureNodeActive
        nonZeroInput(_amount)
    {
        //First deposit must be at least 10M wei to prevent initial attack
        if (getTotalAssetValue() == 0 && _amount < 10000000) {
            revert InvalidInitialDeposit();
        }
        //check the decimals of the baseTokens
        address _baseToken = baseToken;
        uint256 decimalAdjust = 1;
        uint256 tokenDecimals = ERC20(_baseToken).decimals();
        if (tokenDecimals != 18) {
            decimalAdjust = 10**(18 - tokenDecimals);
        }
        //get the amount of tokens to mint
        uint256 what = _amount * decimalAdjust;
        if (totalSupply() != 0) {
            //no need to decimal adjust here as total asset value adjusts
            //unable to deposit if getTotalAssetValue() == 0 and totalSupply() != 0, but this
            //should never occur as defaults will get slashed for some base token recovery
            what = (_amount * totalSupply()) / getTotalAssetValue();
        }
        //transfer tokens from the user and mint
        TransferHelper.safeTransferFrom(
            _baseToken,
            msg.sender,
            address(this),
            _amount
        );
        _mint(msg.sender, what);

        _depositToLendingPool(_baseToken, _amount);

        emit baseTokenDeposit(msg.sender, _amount);
    }

    /**
     * Withdraw liquidity from the banking node
     * To avoid need to decimal adjust, input _amount is in USDT(or equiv) to withdraw
     * , not BNPL USD to burn
     */
    function withdraw(uint256 _amount) external nonZeroInput(_amount) {
        uint256 userBaseBalance = getBaseTokenBalance(msg.sender);
        if (userBaseBalance < _amount) {
            revert InsufficientBalance();
        }
        //safe div, if _amount > 0, asset value always >0;
        uint256 what = (_amount * totalSupply()) / getTotalAssetValue();
        address _baseToken = baseToken;
        _burn(msg.sender, what);
        //non-zero revert with checked in "_withdrawFromLendingPool"
        _withdrawFromLendingPool(_baseToken, _amount, msg.sender);

        emit baseTokenWithdrawn(msg.sender, _amount);
    }

    /**
     * Stake BNPL into a node
     */
    function stake(uint256 _amount)
        external
        ensureNodeActive
        nonZeroInput(_amount)
    {
        address staker = msg.sender;
        //factory initial bond counted as operator
        if (msg.sender == bnplFactory) {
            staker = operator;
        }
        //calcualte the number of shares to give
        uint256 what = _amount;
        uint256 _totalStakingShares = totalStakingShares;
        if (_totalStakingShares > 0) {
            //edge case - if totalStakingShares != 0, but all bnpl has been slashed:
            //node will require a donation to work again
            uint256 totalStakedBNPL = getStakedBNPL();
            if (totalStakedBNPL == 0) {
                revert DonationRequired();
            }
            what = (_amount * _totalStakingShares) / totalStakedBNPL;
        }
        //collect the BNPL
        address _bnpl = BNPL;
        TransferHelper.safeTransferFrom(
            _bnpl,
            msg.sender,
            address(this),
            _amount
        );
        //issue the shares
        stakingShares[staker] += what;
        totalStakingShares += what;

        emit bnplStaked(msg.sender, _amount);
    }

    /**
     * Unbond BNPL from a node, input is the number shares (sBNPL)
     * Requires a 7 day unbond to prevent frontrun of slashing events or interest repayments
     * Operator can not unstake unless there are no loans active
     */
    function initiateUnstake(uint256 _amount) external nonZeroInput(_amount) {
        //operator cannot withdraw unless there are no active loans
        address _operator = operator;
        if (msg.sender == _operator && currentLoans.length > 0) {
            revert ActiveLoansOngoing();
        }
        uint256 stakingSharesUser = stakingShares[msg.sender];
        //require the user has enough
        if (stakingShares[msg.sender] < _amount) {
            revert InsufficientBalance();
        }
        //set the time of the unbond
        unbondBlock[msg.sender] = block.number;
        //get the amount of BNPL to issue back
        //safe div: if user staking shares >0, totalStakingShares always > 0
        uint256 what = (_amount * getStakedBNPL()) / totalStakingShares;
        //subtract the number of shares of BNPL from the user
        stakingShares[msg.sender] -= _amount;
        totalStakingShares -= _amount;
        //initiate as 1:1 for unbonding shares with BNPL sent
        uint256 _newUnbondingShares = what;
        uint256 _unbondingAmount = unbondingAmount;
        //update amount if there is a pool of unbonding
        if (_unbondingAmount != 0) {
            _newUnbondingShares =
                (what * totalUnbondingShares) /
                _unbondingAmount;
        }
        //add the balance to their unbonding
        unbondingShares[msg.sender] += _newUnbondingShares;
        totalUnbondingShares += _newUnbondingShares;
        unbondingAmount += what;

        emit unbondingInitiated(msg.sender, _amount);
    }

    /**
     * Withdraw BNPL from a bond once unbond period ends
     * Unbonding period is 46523 blocks (~7 days assuming a 13s avg. block time)
     */
    function unstake() external {
        uint256 _userAmount = unbondingShares[msg.sender];
        if (_userAmount == 0) {
            revert ZeroInput();
        }
        //assuming 13s block, 46523 blocks for 1 week
        if (block.number < unbondBlock[msg.sender] + 46523) {
            revert LoanStillUnbonding();
        }
        uint256 _unbondingAmount = unbondingAmount;
        uint256 _totalUnbondingShares = totalUnbondingShares;
        address _bnpl = BNPL;
        //safe div: if user amount > 0, then totalUnbondingShares always > 0
        uint256 _what = (_userAmount * _unbondingAmount) /
            _totalUnbondingShares;
        //update the balances
        unbondingShares[msg.sender] = 0;
        unbondingAmount -= _what;
        totalUnbondingShares -= _userAmount;

        //transfer the tokens to user
        TransferHelper.safeTransfer(_bnpl, msg.sender, _what);
        emit bnplWithdrawn(msg.sender, _what);
    }

    /**
     * Declare a loan defaulted and slash the loan
     * Can be called by anyone
     * Move BNPL to a slashing balance, to be sold in seperate function
     * minOut used for sale of collateral, if no collateral, put 0
     */
    function slashLoan(uint256 loanId, uint256 minOut)
        external
        ensurePrincipalRemaining(loanId)
    {
        //Step 1. load loan as local variable
        Loan storage loan = idToLoan[loanId];

        //Step 2. requirement checks: loan is ongoing and expired past grace period
        if (loan.isSlashed) {
            revert LoanAlreadySlashed();
        }
        if (block.timestamp <= getNextDueDate(loanId) + gracePeriod) {
            revert LoanNotExpired();
        }

        //Step 3, Check if theres any collateral to slash
        uint256 _collateralPosted = loan.collateralAmount;
        uint256 baseTokenOut = 0;
        address _baseToken = baseToken;
        if (_collateralPosted > 0) {
            //Step 3a. load local variables
            address _collateral = loan.collateral;

            //Step 3b. update the colleral owed and loan amounts
            collateralOwed[_collateral] -= _collateralPosted;
            loan.collateralAmount = 0;

            //Step 3c. withdraw collateral from aave
            _withdrawFromLendingPool(
                _collateral,
                _collateralPosted,
                address(this)
            );
            //Step 3d. sell collateral for baseToken
            baseTokenOut = _swapToken(
                _collateral,
                _baseToken,
                minOut,
                _collateralPosted
            );
            //Step 3e. deposit the recovered baseTokens to aave
            _depositToLendingPool(_baseToken, baseTokenOut);
        }
        //Step 4. calculate the amount to be slashed
        uint256 principalLost = loan.principalRemaining;
        //Check if there was a full recovery for the loan, if so
        if (baseTokenOut >= principalLost) {
            //return excess to the lender (if any)
            _withdrawFromLendingPool(
                _baseToken,
                baseTokenOut - principalLost,
                loan.borrower
            );
        }
        //slash loan only if losses are greater than recovered
        else {
            principalLost -= baseTokenOut;
            //safe div: principal > 0 => totalassetvalue > 0
            uint256 slashPercent = (1e12 * principalLost) /
                getTotalAssetValue();
            uint256 unbondingSlash = (unbondingAmount * slashPercent) / 1e12;
            uint256 stakingSlash = (getStakedBNPL() * slashPercent) / 1e12;
            //Step 5. deduct slashed from respective balances
            accountsReceiveable -= principalLost;
            slashingBalance += unbondingSlash + stakingSlash;
            unbondingAmount -= unbondingSlash;
        }

        //Step 6. remove loan from currentLoans and add to defaulted loans
        defaultedLoans[defaultedLoanCount] = loanId;
        defaultedLoanCount++;

        loan.isSlashed = true;
        _removeCurrentLoan(loanId);
        emit loanSlashed(loanId);
    }

    /**
     * Sell the slashing balance of BNPL to give to lenders as <aBaseToken>
     * Slashing sale moved to seperate function to simplify logic with minOut
     */
    function sellSlashed(uint256 minOut) external {
        //Step 1. load local variables
        address _baseToken = baseToken;
        address _bnpl = BNPL;
        uint256 _slashingBalance = slashingBalance;
        //Step 2. check there is a balance to sell
        if (_slashingBalance == 0) {
            revert ZeroInput();
        }
        //Step 3. sell the slashed BNPL for baseToken
        uint256 baseTokenOut = _swapToken(
            _bnpl,
            _baseToken,
            minOut,
            _slashingBalance
        );
        //Step 4. deposit baseToken received to aave and update slashing balance
        slashingBalance = 0;
        _depositToLendingPool(_baseToken, baseTokenOut);

        emit slashingSale(_slashingBalance, baseTokenOut);
    }

    /**
     * Donate baseToken for when debt is collected post default
     * BNPL can be donated by simply sending it to the contract
     */
    function donateBaseToken(uint256 _amount) external nonZeroInput(_amount) {
        //Step 1. load local variables
        address _baseToken = baseToken;
        //Step 2. collect the baseTokens
        TransferHelper.safeTransferFrom(
            _baseToken,
            msg.sender,
            address(this),
            _amount
        );
        //Step 3. deposit baseToken to aave
        _depositToLendingPool(_baseToken, _amount);

        emit baseTokensDonated(_amount);
    }

    //OPERATOR ONLY FUNCTIONS

    /**
     * Approve a pending loan request
     * Ensures collateral amount has been posted to prevent front run withdrawal
     */
    function approveLoan(uint256 loanId, uint256 requiredCollateralAmount)
        external
        operatorOnly
    {
        Loan storage loan = idToLoan[loanId];
        uint256 length = pendingRequests.length;
        uint256 loanSize = loan.loanAmount;
        address _baseToken = baseToken;

        if (getBNPLBalance(operator) < 0x13DA329B6336471800000) {
            revert NodeInactive();
        }
        //ensure the loan was never started and collateral enough
        if (loan.loanStartTime > 0) {
            revert LoanAlreadyStarted();
        }
        if (loan.collateralAmount < requiredCollateralAmount) {
            revert InsufficientCollateral();
        }

        //remove from loanRequests and add loan to current loans

        for (uint256 i = 0; i < length; i++) {
            if (loanId == pendingRequests[i]) {
                pendingRequests[i] = pendingRequests[length - 1];
                pendingRequests.pop();
                break;
            }
        }

        currentLoans.push(loanId);

        //add the principal remaining and start the loan

        loan.principalRemaining = loanSize;
        loan.loanStartTime = block.timestamp;
        accountsReceiveable += loanSize;
        //send the funds and update accounts (minus 0.5% origination fee)

        _withdrawFromLendingPool(
            _baseToken,
            (loanSize * 199) / 200,
            loan.borrower
        );
        //send the 0.25% origination fee to treasury and agent
        _withdrawFromLendingPool(_baseToken, loanSize / 400, treasury);
        _withdrawFromLendingPool(
            _baseToken,
            loanSize / 400,
            loanToAgent[loanId]
        );

        emit approvedLoan(loanId);
    }

    /**
     * Used to reject all current pending loan requests
     */
    function clearPendingLoans() external operatorOnly {
        pendingRequests = new uint256[](0);
    }

    /**
     * Whitelist or delist a given list of addresses
     * Only relevant on KYC nodes
     */
    function whitelistAddresses(
        address[] memory whitelistAddition,
        bool _status
    ) external operatorOnly {
        uint256 length = whitelistAddition.length;
        for (uint256 i; i < length; i++) {
            address newWhistelist = whitelistAddition[i];
            whitelistedAddresses[newWhistelist] = _status;
        }
    }

    /**
     * Updates the KYC Status of a node
     */
    function setKYC(bool _newStatus) external operatorOnly {
        requireKYC = _newStatus;
        emit KYCRequirementChanged(_newStatus);
    }

    //PRIVATE FUNCTIONS

    /**
     * Deposit token onto AAVE lending pool, receiving aTokens in return
     */
    function _depositToLendingPool(address tokenIn, uint256 amountIn) private {
        address _lendingPool = address(_getLendingPool());
        TransferHelper.safeApprove(tokenIn, _lendingPool, 0);
        TransferHelper.safeApprove(tokenIn, _lendingPool, amountIn);
        _getLendingPool().deposit(tokenIn, amountIn, address(this), 0);
    }

    /**
     * Withdraw token from AAVE lending pool, converting from aTokens to ERC20 equiv
     */
    function _withdrawFromLendingPool(
        address tokenOut,
        uint256 amountOut,
        address to
    ) private nonZeroInput(amountOut) {
        _getLendingPool().withdraw(tokenOut, amountOut, to);
    }

    /**
     * Get the latest AAVE Lending Pool contract
     */
    function _getLendingPool() private view returns (ILendingPool) {
        return ILendingPool(lendingPoolProvider.getLendingPool());
    }

    /**
     * Remove given loan from current loan list
     */
    function _removeCurrentLoan(uint256 loanId) private {
        for (uint256 i = 0; i < currentLoans.length; i++) {
            if (loanId == currentLoans[i]) {
                currentLoans[i] = currentLoans[currentLoans.length - 1];
                currentLoans.pop();
                return;
            }
        }
    }

    /**
     * Swaps given token, with path of length 3, tokenIn => WETH => tokenOut
     * Uses Sushiswap pairs only
     * Ensures slippage with minOut
     */
    function _swapToken(
        address tokenIn,
        address tokenOut,
        uint256 minOut,
        uint256 amountIn
    ) private returns (uint256 tokenOutput) {
        if (amountIn == 0) {
            revert ZeroInput();
        }
        //Step 1. load data to local variables
        address _uniswapFactory = uniswapFactory;
        address _weth = WETH;
        address pair1 = UniswapV2Library.pairFor(
            _uniswapFactory,
            tokenIn,
            _weth
        );
        address pair2 = UniswapV2Library.pairFor(
            _uniswapFactory,
            _weth,
            tokenOut
        );
        //if tokenIn = weth, only need to swap with pair2 with amountIn as input
        if (tokenIn == _weth) {
            pair1 = pair2;
            tokenOutput = amountIn;
        }
        //Step 2. transfer the tokens to first pair (pair 2 if tokenIn == weth)
        TransferHelper.safeTransfer(tokenIn, pair1, amountIn);
        //Step 3. Swap tokenIn to WETH (only if tokenIn != weth)
        if (tokenIn != _weth) {
            tokenOutput = _swap(tokenIn, _weth, amountIn, pair1, pair2);
        }
        //Step 4. Swap ETH for tokenOut
        tokenOutput = _swap(_weth, tokenOut, tokenOutput, pair2, address(this));
        //Step 5. Check slippage parameters
        if (minOut > tokenOutput) {
            revert InsufficentOutput();
        }
    }

    /**
     * Helper function for _swapToken
     * Modified from uniswap router to save gas, makes a single trade
     * with uniswap pair without needing address[] path or uit256[] amounts
     */
    function _swap(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        address pair,
        address to
    ) private returns (uint256 tokenOutput) {
        address _uniswapFactory = uniswapFactory;
        //Step 1. get the reserves of each token
        (uint256 reserveIn, uint256 reserveOut) = UniswapV2Library.getReserves(
            _uniswapFactory,
            tokenIn,
            tokenOut
        );
        //Step 2. get the tokens that will be received
        tokenOutput = UniswapV2Library.getAmountOut(
            amountIn,
            reserveIn,
            reserveOut
        );
        //Step 3. sort the tokens to pass IUniswapV2Pair
        (address token0, ) = UniswapV2Library.sortTokens(tokenIn, tokenOut);
        (uint256 amount0Out, uint256 amount1Out) = tokenIn == token0
            ? (uint256(0), tokenOutput)
            : (tokenOutput, uint256(0));
        //Step 4. make the trade
        IUniswapV2Pair(pair).swap(amount0Out, amount1Out, to, new bytes(0));
    }

    //VIEW ONLY FUNCTIONS

    /**
     * Get the total BNPL in the staking account
     * Given by (total BNPL of node) - (unbonding balance) - (slashing balance)
     */
    function getStakedBNPL() public view returns (uint256) {
        return
            IERC20(BNPL).balanceOf(address(this)) -
            unbondingAmount -
            slashingBalance;
    }

    /**
     * Gets the given users balance in baseToken
     */
    function getBaseTokenBalance(address user) public view returns (uint256) {
        uint256 _balance = balanceOf(user);
        if (totalSupply() == 0) {
            return 0;
        }
        return (_balance * getTotalAssetValue()) / totalSupply();
    }

    /**
     * Get the value of the BNPL staked by user
     * Given by (user's shares) * (total BNPL staked) / (total number of shares)
     */
    function getBNPLBalance(address user) public view returns (uint256 what) {
        uint256 _balance = stakingShares[user];
        uint256 _totalStakingShares = totalStakingShares;
        if (_totalStakingShares == 0) {
            what = 0;
        } else {
            what = (_balance * getStakedBNPL()) / _totalStakingShares;
        }
    }

    /**
     * Get the amount a user has that is being unbonded
     * Given by (user's unbonding shares) * (total unbonding BNPL) / (total unbonding shares)
     */
    function getUnbondingBalance(address user) external view returns (uint256) {
        uint256 _totalUnbondingShares = totalUnbondingShares;
        uint256 _userUnbondingShare = unbondingShares[user];
        if (_totalUnbondingShares == 0) {
            return 0;
        }
        return (_userUnbondingShare * unbondingAmount) / _totalUnbondingShares;
    }

    /**
     * Gets the next payment amount due
     * If loan is completed or not approved, returns 0
     */
    function getNextPayment(uint256 loanId) public view returns (uint256) {
        //if loan is completed or not approved, return 0
        Loan storage loan = idToLoan[loanId];
        if (loan.principalRemaining == 0) {
            return 0;
        }
        uint256 _interestRate = loan.interestRate;
        uint256 _loanAmount = loan.loanAmount;
        uint256 _numberOfPayments = loan.numberOfPayments;
        //check if it is an interest only loan
        if (loan.interestOnly) {
            //check if its the final payment
            if (loan.paymentsMade + 1 == _numberOfPayments) {
                //if final payment, then principal + final interest amount
                return _loanAmount + ((_loanAmount * _interestRate) / 10000);
            } else {
                //if not final payment, simple interest amount
                return (_loanAmount * _interestRate) / 10000;
            }
        } else {
            //principal + interest payments, payment given by the formula:
            //p : principal
            //i : interest rate per period
            //d : duration
            // p * (i * (1+i) ** d) / ((1+i) ** d - 1)
            uint256 numerator = _loanAmount *
                _interestRate *
                (10000 + _interestRate)**_numberOfPayments;
            uint256 denominator = (10000 + _interestRate)**_numberOfPayments -
                (10**(4 * _numberOfPayments));
            return numerator / (denominator * 10000);
        }
    }

    /**
     * Gets the next due date (unix timestamp) of a given loan
     * Returns 0 if loan is not a current loan or loan has already been paid
     */
    function getNextDueDate(uint256 loanId) public view returns (uint256) {
        //check that the loan has been approved and loan is not completed;
        Loan storage loan = idToLoan[loanId];
        if (loan.principalRemaining == 0) {
            return 0;
        }
        return
            loan.loanStartTime +
            ((loan.paymentsMade + 1) * loan.paymentInterval);
    }

    /**
     * Get the total assets (accounts receivable + aToken balance)
     * Only principal owed is counted as accounts receivable
     */
    function getTotalAssetValue() public view returns (uint256) {
        return
            IERC20(_getLendingPool().getReserveData(baseToken).aTokenAddress)
                .balanceOf(address(this)) + accountsReceiveable;
    }

    /**
     * Get number of pending requests
     */
    function getPendingRequestCount() external view returns (uint256) {
        return pendingRequests.length;
    }

    /**
     * Get the current number of active loans
     */
    function getCurrentLoansCount() external view returns (uint256) {
        return currentLoans.length;
    }

    /**
     * Get the total Losses occurred
     */
    function getTotalDefaultLoss() external view returns (uint256) {
        uint256 totalLosses = 0;
        for (uint256 i; i < defaultedLoanCount; i++) {
            Loan storage loan = idToLoan[defaultedLoans[i]];
            totalLosses += loan.principalRemaining;
        }
        return totalLosses;
    }
}

File 2 of 17 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC20.sol";
import "IERC20Metadata.sol";
import "Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

File 3 of 17 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @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 4 of 17 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 5 of 17 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 6 of 17 : Pausable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 7 of 17 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.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 8 of 17 : ILendingPool.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

import {ILendingPoolAddressesProvider} from "ILendingPoolAddressesProvider.sol";
import {DataTypes} from "DataTypes.sol";

interface ILendingPool {
    /**
     * @dev Emitted on deposit()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address initiating the deposit
     * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens
     * @param amount The amount deposited
     * @param referral The referral code used
     **/
    event Deposit(
        address indexed reserve,
        address user,
        address indexed onBehalfOf,
        uint256 amount,
        uint16 indexed referral
    );

    /**
     * @dev Emitted on withdraw()
     * @param reserve The address of the underlyng asset being withdrawn
     * @param user The address initiating the withdrawal, owner of aTokens
     * @param to Address that will receive the underlying
     * @param amount The amount to be withdrawn
     **/
    event Withdraw(
        address indexed reserve,
        address indexed user,
        address indexed to,
        uint256 amount
    );

    /**
     * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
     * @param reserve The address of the underlying asset being borrowed
     * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
     * initiator of the transaction on flashLoan()
     * @param onBehalfOf The address that will be getting the debt
     * @param amount The amount borrowed out
     * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable
     * @param borrowRate The numeric rate at which the user has borrowed
     * @param referral The referral code used
     **/
    event Borrow(
        address indexed reserve,
        address user,
        address indexed onBehalfOf,
        uint256 amount,
        uint256 borrowRateMode,
        uint256 borrowRate,
        uint16 indexed referral
    );

    /**
     * @dev Emitted on repay()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The beneficiary of the repayment, getting his debt reduced
     * @param repayer The address of the user initiating the repay(), providing the funds
     * @param amount The amount repaid
     **/
    event Repay(
        address indexed reserve,
        address indexed user,
        address indexed repayer,
        uint256 amount
    );

    /**
     * @dev Emitted on swapBorrowRateMode()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address of the user swapping his rate mode
     * @param rateMode The rate mode that the user wants to swap to
     **/
    event Swap(address indexed reserve, address indexed user, uint256 rateMode);

    /**
     * @dev Emitted on setUserUseReserveAsCollateral()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address of the user enabling the usage as collateral
     **/
    event ReserveUsedAsCollateralEnabled(
        address indexed reserve,
        address indexed user
    );

    /**
     * @dev Emitted on setUserUseReserveAsCollateral()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address of the user enabling the usage as collateral
     **/
    event ReserveUsedAsCollateralDisabled(
        address indexed reserve,
        address indexed user
    );

    /**
     * @dev Emitted on rebalanceStableBorrowRate()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address of the user for which the rebalance has been executed
     **/
    event RebalanceStableBorrowRate(
        address indexed reserve,
        address indexed user
    );

    /**
     * @dev Emitted on flashLoan()
     * @param target The address of the flash loan receiver contract
     * @param initiator The address initiating the flash loan
     * @param asset The address of the asset being flash borrowed
     * @param amount The amount flash borrowed
     * @param premium The fee flash borrowed
     * @param referralCode The referral code used
     **/
    event FlashLoan(
        address indexed target,
        address indexed initiator,
        address indexed asset,
        uint256 amount,
        uint256 premium,
        uint16 referralCode
    );

    /**
     * @dev Emitted when the pause is triggered.
     */
    event Paused();

    /**
     * @dev Emitted when the pause is lifted.
     */
    event Unpaused();

    /**
     * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via
     * LendingPoolCollateral manager using a DELEGATECALL
     * This allows to have the events in the generated ABI for LendingPool.
     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
     * @param user The address of the borrower getting liquidated
     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
     * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator
     * @param liquidator The address of the liquidator
     * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
     * to receive the underlying collateral asset directly
     **/
    event LiquidationCall(
        address indexed collateralAsset,
        address indexed debtAsset,
        address indexed user,
        uint256 debtToCover,
        uint256 liquidatedCollateralAmount,
        address liquidator,
        bool receiveAToken
    );

    /**
     * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
     * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
     * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
     * gets added to the LendingPool ABI
     * @param reserve The address of the underlying asset of the reserve
     * @param liquidityRate The new liquidity rate
     * @param stableBorrowRate The new stable borrow rate
     * @param variableBorrowRate The new variable borrow rate
     * @param liquidityIndex The new liquidity index
     * @param variableBorrowIndex The new variable borrow index
     **/
    event ReserveDataUpdated(
        address indexed reserve,
        uint256 liquidityRate,
        uint256 stableBorrowRate,
        uint256 variableBorrowRate,
        uint256 liquidityIndex,
        uint256 variableBorrowIndex
    );

    /**
     * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
     * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
     * @param asset The address of the underlying asset to deposit
     * @param amount The amount to be deposited
     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
     *   is a different wallet
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function deposit(
        address asset,
        uint256 amount,
        address onBehalfOf,
        uint16 referralCode
    ) external;

    /**
     * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
     * @param asset The address of the underlying asset to withdraw
     * @param amount The underlying amount to be withdrawn
     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
     * @param to Address that will receive the underlying, same as msg.sender if the user
     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
     *   different wallet
     * @return The final amount withdrawn
     **/
    function withdraw(
        address asset,
        uint256 amount,
        address to
    ) external returns (uint256);

    /**
     * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
     * already deposited enough collateral, or he was given enough allowance by a credit delegator on the
     * corresponding debt token (StableDebtToken or VariableDebtToken)
     * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
     *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
     * @param asset The address of the underlying asset to borrow
     * @param amount The amount to be borrowed
     * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
     * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
     * if he has been given credit delegation allowance
     **/
    function borrow(
        address asset,
        uint256 amount,
        uint256 interestRateMode,
        uint16 referralCode,
        address onBehalfOf
    ) external;

    /**
     * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
     * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
     * @param asset The address of the borrowed underlying asset previously borrowed
     * @param amount The amount to repay
     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
     * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
     * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
     * other borrower whose debt should be removed
     * @return The final amount repaid
     **/
    function repay(
        address asset,
        uint256 amount,
        uint256 rateMode,
        address onBehalfOf
    ) external returns (uint256);

    /**
     * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
     * @param asset The address of the underlying asset borrowed
     * @param rateMode The rate mode that the user wants to swap to
     **/
    function swapBorrowRateMode(address asset, uint256 rateMode) external;

    /**
     * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
     * - Users can be rebalanced if the following conditions are satisfied:
     *     1. Usage ratio is above 95%
     *     2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
     *        borrowed at a stable rate and depositors are not earning enough
     * @param asset The address of the underlying asset borrowed
     * @param user The address of the user to be rebalanced
     **/
    function rebalanceStableBorrowRate(address asset, address user) external;

    /**
     * @dev Allows depositors to enable/disable a specific deposited asset as collateral
     * @param asset The address of the underlying asset deposited
     * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
     **/
    function setUserUseReserveAsCollateral(address asset, bool useAsCollateral)
        external;

    /**
     * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
     * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
     *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
     * @param user The address of the borrower getting liquidated
     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
     * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
     * to receive the underlying collateral asset directly
     **/
    function liquidationCall(
        address collateralAsset,
        address debtAsset,
        address user,
        uint256 debtToCover,
        bool receiveAToken
    ) external;

    /**
     * @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
     * as long as the amount taken plus a fee is returned.
     * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
     * For further details please visit https://developers.aave.com
     * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
     * @param assets The addresses of the assets being flash-borrowed
     * @param amounts The amounts amounts being flash-borrowed
     * @param modes Types of the debt to open if the flash loan is not returned:
     *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
     *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
     *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
     * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
     * @param params Variadic packed params to pass to the receiver as extra information
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function flashLoan(
        address receiverAddress,
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata modes,
        address onBehalfOf,
        bytes calldata params,
        uint16 referralCode
    ) external;

    /**
     * @dev Returns the user account data across all the reserves
     * @param user The address of the user
     * @return totalCollateralETH the total collateral in ETH of the user
     * @return totalDebtETH the total debt in ETH of the user
     * @return availableBorrowsETH the borrowing power left of the user
     * @return currentLiquidationThreshold the liquidation threshold of the user
     * @return ltv the loan to value of the user
     * @return healthFactor the current health factor of the user
     **/
    function getUserAccountData(address user)
        external
        view
        returns (
            uint256 totalCollateralETH,
            uint256 totalDebtETH,
            uint256 availableBorrowsETH,
            uint256 currentLiquidationThreshold,
            uint256 ltv,
            uint256 healthFactor
        );

    function initReserve(
        address reserve,
        address aTokenAddress,
        address stableDebtAddress,
        address variableDebtAddress,
        address interestRateStrategyAddress
    ) external;

    function setReserveInterestRateStrategyAddress(
        address reserve,
        address rateStrategyAddress
    ) external;

    function setConfiguration(address reserve, uint256 configuration) external;

    /**
     * @dev Returns the configuration of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The configuration of the reserve
     **/
    function getConfiguration(address asset)
        external
        view
        returns (DataTypes.ReserveConfigurationMap memory);

    /**
     * @dev Returns the configuration of the user across all the reserves
     * @param user The user address
     * @return The configuration of the user
     **/
    function getUserConfiguration(address user)
        external
        view
        returns (DataTypes.UserConfigurationMap memory);

    /**
     * @dev Returns the normalized income normalized income of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The reserve's normalized income
     */
    function getReserveNormalizedIncome(address asset)
        external
        view
        returns (uint256);

    /**
     * @dev Returns the normalized variable debt per unit of asset
     * @param asset The address of the underlying asset of the reserve
     * @return The reserve normalized variable debt
     */
    function getReserveNormalizedVariableDebt(address asset)
        external
        view
        returns (uint256);

    /**
     * @dev Returns the state and configuration of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The state of the reserve
     **/
    function getReserveData(address asset)
        external
        view
        returns (DataTypes.ReserveData memory);

    function finalizeTransfer(
        address asset,
        address from,
        address to,
        uint256 amount,
        uint256 balanceFromAfter,
        uint256 balanceToBefore
    ) external;

    function getReservesList() external view returns (address[] memory);

    function getAddressesProvider()
        external
        view
        returns (ILendingPoolAddressesProvider);

    function setPause(bool val) external;

    function paused() external view returns (bool);
}

File 9 of 17 : ILendingPoolAddressesProvider.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;

/**
 * @title LendingPoolAddressesProvider contract
 * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
 * - Acting also as factory of proxies and admin of those, so with right to change its implementations
 * - Owned by the Aave Governance
 * @author Aave
 **/
interface ILendingPoolAddressesProvider {
    event MarketIdSet(string newMarketId);
    event LendingPoolUpdated(address indexed newAddress);
    event ConfigurationAdminUpdated(address indexed newAddress);
    event EmergencyAdminUpdated(address indexed newAddress);
    event LendingPoolConfiguratorUpdated(address indexed newAddress);
    event LendingPoolCollateralManagerUpdated(address indexed newAddress);
    event PriceOracleUpdated(address indexed newAddress);
    event LendingRateOracleUpdated(address indexed newAddress);
    event ProxyCreated(bytes32 id, address indexed newAddress);
    event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);

    function getMarketId() external view returns (string memory);

    function setMarketId(string calldata marketId) external;

    function setAddress(bytes32 id, address newAddress) external;

    function setAddressAsProxy(bytes32 id, address impl) external;

    function getAddress(bytes32 id) external view returns (address);

    function getLendingPool() external view returns (address);

    function setLendingPoolImpl(address pool) external;

    function getLendingPoolConfigurator() external view returns (address);

    function setLendingPoolConfiguratorImpl(address configurator) external;

    function getLendingPoolCollateralManager() external view returns (address);

    function setLendingPoolCollateralManager(address manager) external;

    function getPoolAdmin() external view returns (address);

    function setPoolAdmin(address admin) external;

    function getEmergencyAdmin() external view returns (address);

    function setEmergencyAdmin(address admin) external;

    function getPriceOracle() external view returns (address);

    function setPriceOracle(address priceOracle) external;

    function getLendingRateOracle() external view returns (address);

    function setLendingRateOracle(address lendingRateOracle) external;
}

File 10 of 17 : DataTypes.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;

library DataTypes {
    // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
    struct ReserveData {
        //stores the reserve configuration
        ReserveConfigurationMap configuration;
        //the liquidity index. Expressed in ray
        uint128 liquidityIndex;
        //variable borrow index. Expressed in ray
        uint128 variableBorrowIndex;
        //the current supply rate. Expressed in ray
        uint128 currentLiquidityRate;
        //the current variable borrow rate. Expressed in ray
        uint128 currentVariableBorrowRate;
        //the current stable borrow rate. Expressed in ray
        uint128 currentStableBorrowRate;
        uint40 lastUpdateTimestamp;
        //tokens addresses
        address aTokenAddress;
        address stableDebtTokenAddress;
        address variableDebtTokenAddress;
        //address of the interest rate strategy
        address interestRateStrategyAddress;
        //the id of the reserve. Represents the position in the list of the active reserves
        uint8 id;
    }

    struct ReserveConfigurationMap {
        //bit 0-15: LTV
        //bit 16-31: Liq. threshold
        //bit 32-47: Liq. bonus
        //bit 48-55: Decimals
        //bit 56: Reserve is active
        //bit 57: reserve is frozen
        //bit 58: borrowing is enabled
        //bit 59: stable rate borrowing enabled
        //bit 60-63: reserved
        //bit 64-79: reserve factor
        uint256 data;
    }

    struct UserConfigurationMap {
        uint256 data;
    }

    enum InterestRateMode {
        NONE,
        STABLE,
        VARIABLE
    }
}

File 11 of 17 : IAaveIncentivesController.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;

pragma experimental ABIEncoderV2;

import {IAaveDistributionManager} from "IAaveDistributionManager.sol";

interface IAaveIncentivesController is IAaveDistributionManager {
    event RewardsAccrued(address indexed user, uint256 amount);

    event RewardsClaimed(
        address indexed user,
        address indexed to,
        address indexed claimer,
        uint256 amount
    );

    event ClaimerSet(address indexed user, address indexed claimer);

    /**
     * @dev Whitelists an address to claim the rewards on behalf of another address
     * @param user The address of the user
     * @param claimer The address of the claimer
     */
    function setClaimer(address user, address claimer) external;

    /**
     * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)
     * @param user The address of the user
     * @return The claimer address
     */
    function getClaimer(address user) external view returns (address);

    /**
     * @dev Configure assets for a certain rewards emission
     * @param assets The assets to incentivize
     * @param emissionsPerSecond The emission for each asset
     */
    function configureAssets(
        address[] calldata assets,
        uint256[] calldata emissionsPerSecond
    ) external;

    /**
     * @dev Called by the corresponding asset on any update that affects the rewards distribution
     * @param asset The address of the user
     * @param userBalance The balance of the user of the asset in the lending pool
     * @param totalSupply The total supply of the asset in the lending pool
     **/
    function handleAction(
        address asset,
        uint256 userBalance,
        uint256 totalSupply
    ) external;

    /**
     * @dev Returns the total of rewards of an user, already accrued + not yet accrued
     * @param user The address of the user
     * @return The rewards
     **/
    function getRewardsBalance(address[] calldata assets, address user)
        external
        view
        returns (uint256);

    /**
     * @dev Claims reward for an user to the desired address, on all the assets of the lending pool, accumulating the pending rewards
     * @param amount Amount of rewards to claim
     * @param to Address that will be receiving the rewards
     * @return Rewards claimed
     **/
    function claimRewards(
        address[] calldata assets,
        uint256 amount,
        address to
    ) external returns (uint256);

    /**
     * @dev Claims reward for an user on behalf, on all the assets of the lending pool, accumulating the pending rewards. The caller must
     * be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
     * @param amount Amount of rewards to claim
     * @param user Address to check and claim rewards
     * @param to Address that will be receiving the rewards
     * @return Rewards claimed
     **/
    function claimRewardsOnBehalf(
        address[] calldata assets,
        uint256 amount,
        address user,
        address to
    ) external returns (uint256);

    /**
     * @dev Claims reward for msg.sender, on all the assets of the lending pool, accumulating the pending rewards
     * @param amount Amount of rewards to claim
     * @return Rewards claimed
     **/
    function claimRewardsToSelf(address[] calldata assets, uint256 amount)
        external
        returns (uint256);

    /**
     * @dev returns the unclaimed rewards of the user
     * @param user the address of the user
     * @return the unclaimed user rewards
     */
    function getUserUnclaimedRewards(address user)
        external
        view
        returns (uint256);

    /**
     * @dev for backward compatibility with previous implementation of the Incentives controller
     */
    function REWARD_TOKEN() external view returns (address);
}

File 12 of 17 : IAaveDistributionManager.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

import {DistributionTypes} from "DistributionTypes.sol";

interface IAaveDistributionManager {
    event AssetConfigUpdated(address indexed asset, uint256 emission);
    event AssetIndexUpdated(address indexed asset, uint256 index);
    event UserIndexUpdated(
        address indexed user,
        address indexed asset,
        uint256 index
    );
    event DistributionEndUpdated(uint256 newDistributionEnd);

    /**
     * @dev Sets the end date for the distribution
     * @param distributionEnd The end date timestamp
     **/
    function setDistributionEnd(uint256 distributionEnd) external;

    /**
     * @dev Gets the end date for the distribution
     * @return The end of the distribution
     **/
    function getDistributionEnd() external view returns (uint256);

    /**
     * @dev for backwards compatibility with the previous DistributionManager used
     * @return The end of the distribution
     **/
    function DISTRIBUTION_END() external view returns (uint256);

    /**
     * @dev Returns the data of an user on a distribution
     * @param user Address of the user
     * @param asset The address of the reference asset of the distribution
     * @return The new index
     **/
    function getUserAssetData(address user, address asset)
        external
        view
        returns (uint256);

    /**
     * @dev Returns the configuration of the distribution for a certain asset
     * @param asset The address of the reference asset of the distribution
     * @return The asset index, the emission per second and the last updated timestamp
     **/
    function getAssetData(address asset)
        external
        view
        returns (
            uint256,
            uint256,
            uint256
        );
}

File 13 of 17 : DistributionTypes.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

library DistributionTypes {
    struct AssetConfigInput {
        uint104 emissionPerSecond;
        uint256 totalStaked;
        address underlyingAsset;
    }

    struct UserStakeInput {
        address underlyingAsset;
        uint256 stakedByUser;
        uint256 totalStaked;
    }
}

File 14 of 17 : UniswapV2Library.sol
pragma solidity >=0.5.0;

import "IUniswapV2Pair.sol";

import "SafeMath.sol";

library UniswapV2Library {
    using SafeMath for uint256;

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

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

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

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

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

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

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

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

File 15 of 17 : IUniswapV2Pair.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
    event Transfer(address indexed from, address indexed to, uint256 value);

    function name() external pure returns (string memory);

    function symbol() external pure returns (string memory);

    function decimals() external pure returns (uint8);

    function totalSupply() external view returns (uint256);

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

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 value) external returns (bool);

    function transfer(address to, uint256 value) external returns (bool);

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    function PERMIT_TYPEHASH() external pure returns (bytes32);

    function nonces(address owner) external view returns (uint256);

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    event Mint(address indexed sender, uint256 amount0, uint256 amount1);
    event Burn(
        address indexed sender,
        uint256 amount0,
        uint256 amount1,
        address indexed to
    );
    event Swap(
        address indexed sender,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint256);

    function factory() external view returns (address);

    function token0() external view returns (address);

    function token1() external view returns (address);

    function getReserves()
        external
        view
        returns (
            uint112 reserve0,
            uint112 reserve1,
            uint32 blockTimestampLast
        );

    function price0CumulativeLast() external view returns (uint256);

    function price1CumulativeLast() external view returns (uint256);

    function kLast() external view returns (uint256);

    function mint(address to) external returns (uint256 liquidity);

    function burn(address to)
        external
        returns (uint256 amount0, uint256 amount1);

    function swap(
        uint256 amount0Out,
        uint256 amount1Out,
        address to,
        bytes calldata data
    ) external;

    function skim(address to) external;

    function sync() external;

    function initialize(address, address) external;
}

File 16 of 17 : SafeMath.sol
pragma solidity >=0.6.6;

// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)

library SafeMath {
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x, "ds-math-add-overflow");
    }

    function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x - y) <= x, "ds-math-sub-underflow");
    }

    function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
    }
}

File 17 of 17 : TransferHelper.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.6.0;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(0x095ea7b3, to, value)
        );
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper: APPROVE_FAILED"
        );
    }

    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(0xa9059cbb, to, value)
        );
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper: TRANSFER_FAILED"
        );
    }

    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(0x23b872dd, from, to, value)
        );
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper: TRANSFER_FROM_FAILED"
        );
    }

    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, "TransferHelper: ETH_TRANSFER_FAILED");
    }
}

Settings
{
  "evmVersion": "istanbul",
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "libraries": {
    "BankingNode.sol": {}
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ActiveLoansOngoing","type":"error"},{"inputs":[],"name":"DonationRequired","type":"error"},{"inputs":[],"name":"InsufficentOutput","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientCollateral","type":"error"},{"inputs":[],"name":"InvalidCollateral","type":"error"},{"inputs":[],"name":"InvalidInitialDeposit","type":"error"},{"inputs":[],"name":"InvalidLoanInput","type":"error"},{"inputs":[{"internalType":"address","name":"requiredUser","type":"address"}],"name":"InvalidUser","type":"error"},{"inputs":[],"name":"KYCNotApproved","type":"error"},{"inputs":[],"name":"LoanAlreadySlashed","type":"error"},{"inputs":[],"name":"LoanAlreadyStarted","type":"error"},{"inputs":[],"name":"LoanNotExpired","type":"error"},{"inputs":[],"name":"LoanStillOngoing","type":"error"},{"inputs":[],"name":"LoanStillUnbonding","type":"error"},{"inputs":[],"name":"MaximumLoanDurationExceeded","type":"error"},{"inputs":[],"name":"NoPrincipalRemaining","type":"error"},{"inputs":[],"name":"NodeInactive","type":"error"},{"inputs":[],"name":"ZeroInput","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"newStatus","type":"bool"}],"name":"KYCRequirementChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"LoanRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"approvedLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"baseTokenDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"baseTokenWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"baseTokensDonated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"bnplStaked","type":"uint256"}],"name":"bnplStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"bnplWithdrawn","type":"uint256"}],"name":"bnplWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"collateralWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"operatorFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stakerFees","type":"uint256"}],"name":"feesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"loanPaymentMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"loanRepaidEarly","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"loanSlashed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"bnplSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseTokenRecovered","type":"uint256"}],"name":"slashingSale","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"unbondAmount","type":"uint256"}],"name":"unbondingInitiated","type":"event"},{"inputs":[],"name":"BNPL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accountsReceiveable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"uint256","name":"requiredCollateralAmount","type":"uint256"}],"name":"approveLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bnplFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clearPendingLoans","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"collateralOwed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"}],"name":"collectAaveRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collateral","type":"address"}],"name":"collectCollateralFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"currentLoans","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultedLoanCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"donateBaseToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getBNPLBalance","outputs":[{"internalType":"uint256","name":"what","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getBaseTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentLoansCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"getNextDueDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"getNextPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingRequestCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakedBNPL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalAssetValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalDefaultLoss","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUnbondingBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gracePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"idToLoan","outputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"bool","name":"interestOnly","type":"bool"},{"internalType":"uint256","name":"loanStartTime","type":"uint256"},{"internalType":"uint256","name":"loanAmount","type":"uint256"},{"internalType":"uint256","name":"paymentInterval","type":"uint256"},{"internalType":"uint256","name":"interestRate","type":"uint256"},{"internalType":"uint256","name":"numberOfPayments","type":"uint256"},{"internalType":"uint256","name":"principalRemaining","type":"uint256"},{"internalType":"uint256","name":"paymentsMade","type":"uint256"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"bool","name":"isSlashed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_baseToken","type":"address"},{"internalType":"address","name":"_BNPL","type":"address"},{"internalType":"bool","name":"_requireKYC","type":"bool"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"_gracePeriod","type":"uint256"},{"internalType":"address","name":"_lendingPoolProvider","type":"address"},{"internalType":"address","name":"_WETH","type":"address"},{"internalType":"address","name":"_aaveDistributionController","type":"address"},{"internalType":"address","name":"_uniswapFactory","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"initiateUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lendingPoolProvider","outputs":[{"internalType":"contract ILendingPoolAddressesProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"loanToAgent","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"makeLoanPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pendingRequests","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"repayEarly","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanAmount","type":"uint256"},{"internalType":"uint256","name":"paymentInterval","type":"uint256"},{"internalType":"uint256","name":"numberOfPayments","type":"uint256"},{"internalType":"uint256","name":"interestRate","type":"uint256"},{"internalType":"bool","name":"interestOnly","type":"bool"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"address","name":"agent","type":"address"},{"internalType":"string","name":"message","type":"string"}],"name":"requestLoan","outputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requireKYC","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"minOut","type":"uint256"}],"name":"sellSlashed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_newStatus","type":"bool"}],"name":"setKYC","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"}],"name":"slashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slashingBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakingShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timeCreated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakingShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unbondBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unbondingAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unbondingShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"whitelistAddition","type":"address[]"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"whitelistAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedAddresses","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"withdrawCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103c55760003560e01c806382f84336116101ff578063b6b55f251161011a578063c8796572116100ad578063eb876ded1161007c578063eb876ded14610923578063ed0aa42814610936578063f1653f6e14610949578063f2a85a781461095257600080fd5b8063c8796572146108d0578063d7bf54b2146108d8578063d9ab1b64146108e1578063dd62ed3e146108ea57600080fd5b8063c4be2490116100e9578063c4be24901461089a578063c522b7fd146108ad578063c55dae63146108b5578063c754d713146108c857600080fd5b8063b6b55f2514610859578063b9d3e77a1461086c578063bb05c30e1461087f578063bc0ed30d1461089257600080fd5b8063a457c2d711610192578063a9e5a0d911610161578063a9e5a0d91461080a578063ab74f2e11461082a578063abd594df14610833578063ae5ac9211461084657600080fd5b8063a457c2d7146107be578063a4b06048146107d1578063a694fc3a146107e4578063a9059cbb146107f757600080fd5b806395d89b41116101ce57806395d89b411461076657806397713c651461076e5780639bb8edab1461078e578063a06db7dc146107b557600080fd5b806382f843361461072057806386809a28146107335780638805aaa8146107465780639228c8f31461075357600080fd5b806339fcbfe8116102ef57806362e7707e116102825780637a646492116102515780637a646492146106df5780637c413e9c146106f25780637cc3fa0d146107055780638129820c1461070d57600080fd5b806362e7707e146106875780636e69611d1461069a57806370a08231146106ad57806370c6a17e146106d657600080fd5b8063570ca735116102be578063570ca735146105555780635ddf32a0146105685780636112fe2e14610591578063614da4ac146105a457600080fd5b806339fcbfe8146104f15780633c652a9a146105045780634252873814610517578063441b4c551461052a57600080fd5b80632def662011610367578063313ce56711610336578063313ce567146104b3578063320dbacf146104c25780633948ed4d146104d557806339509351146104de57600080fd5b80632def6620146104635780632e1a7d4d1461046d5780632fe1ac9e14610480578063306f1fc8146104a057600080fd5b806310a4a269116103a357806310a4a2691461042a57806318160ddd1461044057806323b872dd146104485780632a82f11e1461045b57600080fd5b806306c933d8146103ca57806306fdde0314610402578063095ea7b314610417575b600080fd5b6103ed6103d8366004613f34565b60156020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b61040a610972565b6040516103f99190613fa9565b6103ed610425366004613fbc565b610a04565b610432610a1b565b6040519081526020016103f9565b600254610432565b6103ed610456366004613fe8565b610b12565b610432610bc1565b61046b610c16565b005b61046b61047b366004614029565b610d40565b61043261048e366004613f34565b60166020526000908152604090205481565b6104326104ae3660046140d1565b610e0b565b604051601281526020016103f9565b61046b6104d0366004614029565b6111dc565b61043260135481565b6103ed6104ec366004613fbc565b61126c565b6104326104ff366004614029565b6112a8565b610432610512366004614029565b6113cf565b610432610525366004614029565b6113f0565b600b5461053d906001600160a01b031681565b6040516001600160a01b0390911681526020016103f9565b60055461053d906001600160a01b031681565b61053d610576366004614029565b6017602052600090815260409020546001600160a01b031681565b61046b61059f366004614029565b611400565b6106196105b2366004614029565b600f60205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007880154600889015460098a0154600a909a01546001600160a01b03808b169b60ff600160a01b909c048c169b93909116929091168c565b604080516001600160a01b039d8e1681529b151560208d01528b019990995260608a0197909752608089019590955260a088019390935260c087019190915260e0860152610100850152909316610120830152610140820192909252901515610160820152610180016103f9565b61046b6106953660046141cf565b611512565b61046b6106a8366004614029565b6115b3565b6104326106bb366004613f34565b6001600160a01b031660009081526020819052604090205490565b610432601a5481565b61046b6106ed366004614293565b61175a565b610432610700366004613f34565b6119f0565b601154610432565b61046b61071b366004613f34565b611a41565b600c5461053d906001600160a01b031681565b610432610741366004614029565b611c0e565b6008546103ed9060ff1681565b610432610761366004613f34565b611c65565b61040a611cab565b61043261077c366004613f34565b601c6020526000908152604090205481565b61053d7f0000000000000000000000007edb0c8b428b97ea1ca44ea9fcda0835fbd8802981565b61043260075481565b6103ed6107cc366004613fbc565b611cba565b61046b6107df366004614293565b611d53565b61046b6107f2366004614029565b611fdf565b6103ed610805366004613fbc565b6121e7565b610432610818366004613f34565b60196020526000908152604090205481565b610432601b5481565b61046b610841366004614029565b6121f4565b61046b610854366004614029565b61230f565b61046b610867366004614029565b6124c2565b61046b61087a3660046142b5565b61270a565b61046b61088d366004614368565b61294c565b601054610432565b6104326108a8366004613f34565b6129cb565b61046b612a16565b60065461053d906001600160a01b031681565b610432612a6e565b61046b612af7565b61043260185481565b61043260145481565b6104326108f8366004614385565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b61046b6109313660046143be565b612c43565b61046b610944366004614029565b612d5a565b610432601e5481565b610432610960366004613f34565b601f6020526000908152604090205481565b60606003805461098190614433565b80601f01602080910402602001604051908101604052809291908181526020018280546109ad90614433565b80156109fa5780601f106109cf576101008083540402835291602001916109fa565b820191906000526020600020905b8154815290600101906020018083116109dd57829003601f168201915b5050505050905090565b6000610a11338484612dd6565b5060015b92915050565b6000601454610a28612efb565b6006546040516335ea6a7560e01b81526001600160a01b0391821660048201529116906335ea6a759060240161018060405180830381865afa158015610a72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9691906144fa565b60e001516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015610adf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0391906145e7565b610b0d9190614616565b905090565b6000610b1f848484612f69565b6001600160a01b038416600090815260016020908152604080832033845290915290205482811015610ba95760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b610bb68533858403612dd6565b506001949350505050565b600080805b601354811015610c10576000818152601260209081526040808320548352600f90915290206006810154610bfa9084614616565b9250508080610c089061462e565b915050610bc6565b50919050565b336000908152601c602052604081205490819003610c475760405163af458c0760e01b815260040160405180910390fd5b33600090815260166020526040902054610c639061b5bb614616565b431015610c83576040516396cc364160e01b815260040160405180910390fd5b601b54601d54600b546001600160a01b0316600082610ca28587614647565b610cac9190614666565b336000908152601c60205260408120819055601b80549293508392909190610cd5908490614688565b9250508190555084601d6000828254610cee9190614688565b90915550610cff9050823383613139565b60408051338152602081018390527f0b909ff012e2792bea5063882ec57876738ce69f509c169114cdce50bf1de80191015b60405180910390a15050505050565b8080600003610d625760405163af458c0760e01b815260040160405180910390fd5b6000610d6d336129cb565b905082811015610d9057604051631e9acf1760e31b815260040160405180910390fd5b6000610d9a610a1b565b600254610da79086614647565b610db19190614666565b6006549091506001600160a01b0316610dca338361324d565b610dd5818633613393565b60408051338152602081018790527f8e0f31f7a30a1c4fd45f7eaf1d33485ec105ea952db80be5b351c77b8eb936b19101610d31565b6005546000906001600160a01b03908116907f0000000000000000000000007edb0c8b428b97ea1ca44ea9fcda0835fbd88029163314801590610e575750336001600160a01b03821614155b15610ed0576a013da329b6336471800000610e7182611c65565b1015610e9057604051635123fc5760e11b815260040160405180910390fd5b60085460ff168015610eb257503360009081526015602052604090205460ff16155b15610ed05760405163532fc80960e01b815260040160405180910390fd5b60065486906001600160a01b0390811690821603610f01576040516368f7a67560e11b815260040160405180910390fd5b629896808c1080610f1057508a155b80610f19575088155b80610f22575089155b15610f4057604051631a9e484d60e01b815260040160405180910390fd5b6309660180610f4f8b8d614647565b1115610f6e57604051637e3c795160e01b815260040160405180910390fd5b600a8054935083906000610f818361462e565b91905055506010839080600181540180825580915050600190039060005260206000200160009091909190915055604051806101800160405280336001600160a01b031681526020018915158152602001600081526020018d81526020018c81526020018a81526020018b81526020016000815260200160008152602001886001600160a01b0316815260200187815260200160001515815250600f600085815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548160ff02191690831515021790555060408201518160010155606082015181600201556080820151816003015560a0820151816004015560c0820151816005015560e0820151816006015561010082015181600701556101208201518160080160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550610140820151816009015561016082015181600a0160006101000a81548160ff021916908315150217905550905050600086111561116c576001600160a01b0387166000908152601f602052604081208054889290611150908490614616565b909155506111629050873330896133fc565b61116c8787613524565b6000838152601760205260409081902080546001600160a01b0319166001600160a01b038816179055517fbefa86d08b8d52d88970167c70a90fb1c9b3c027d6246599d8ced309ce984e70906111c5908590879061469f565b60405180910390a150509998505050505050505050565b600654600b546018546001600160a01b03928316929091169060008190036112175760405163af458c0760e01b815260040160405180910390fd5b6000611225838587856135c5565b600060185590506112368482613524565b60408051838152602081018390527f3a8c7cb7b1cad4f721c777bdeaf586de9a9901c13350a07e36acaa0eec6911f99101610d31565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610a119185906112a3908690614616565b612dd6565b6000818152600f60205260408120600681015482036112ca5750600092915050565b6004810154600282015460058301548354600160a01b900460ff16156113435780846007015460016112fc9190614616565b0361132c5761271061130e8484614647565b6113189190614666565b6113229083614616565b9695505050505050565b6127106113398484614647565b6113229190614666565b60008161135285612710614616565b61135c919061479c565b6113668585614647565b6113709190614647565b9050600061137f836004614647565b61138a90600a61479c565b8361139787612710614616565b6113a1919061479c565b6113ab9190614688565b90506113b981612710614647565b6113c39083614666565b98975050505050505050565b601081815481106113df57600080fd5b600091825260209091200154905081565b601181815481106113df57600080fd5b6000818152600f602052604090206008810154600982015482546001600160a01b0392831692163314611454578254604051632e86c5bb60e21b81526001600160a01b039091166004820152602401610ba0565b600683015415611477576040516372f028b760e01b815260040160405180910390fd5b6001600160a01b0382166000908152601f60205260408120805483929061149f908490614688565b90915550506000600984015582546114c390839083906001600160a01b0316613393565b604080518581526001600160a01b03841660208201529081018290527f752a8ac4b43ccbb781ff7543d74d2ec46766ae9a4ae69a3458b231171eadba829060600160405180910390a150505050565b6005546001600160a01b031633811461154957604051632e86c5bb60e21b81526001600160a01b0382166004820152602401610ba0565b825160005b818110156115ac57600085828151811061156a5761156a6147a8565b6020908102919091018101516001600160a01b03166000908152601590915260409020805460ff191686151517905550806115a48161462e565b91505061154e565b5050505050565b6000818152600f60205260408120600601548291036115e55760405163407f400d60e01b815260040160405180910390fd5b6000828152600f60205260408120906115fd846112a8565b90506000612710836004015484600601546116189190614647565b6116229190614666565b6006546007850180549293506001600160a01b03909116919060006116468361462e565b9091555050600584015460078501548554911490600160a01b900460ff166116af5760006116748486614688565b90508086600601600082825461168a9190614688565b9250508190555080601460008282546116a39190614688565b909155506116d8915050565b80156116d8578460060154601460008282546116cb9190614688565b9091555050600060068601555b6116e4823330876133fc565b61170e82600a6116f5866003614647565b6116ff9190614666565b6117099087614688565b613524565b801561171d5761171d876136ae565b6040518781527fb4bc0416ec8ea617ee898691ae316a60e78d10d815c8c72698467a09cb74331d906020015b60405180910390a150505050505050565b6005546001600160a01b031633811461179157604051632e86c5bb60e21b81526001600160a01b0382166004820152602401610ba0565b6000838152600f6020526040902060105460028201546006546005546001600160a01b03918216916a013da329b6336471800000916117d09116611c65565b10156117ef57604051635123fc5760e11b815260040160405180910390fd5b60018401541561181257604051632d58e4fd60e01b815260040160405180910390fd5b858460090154101561183757604051633a23d82560e01b815260040160405180910390fd5b60005b838110156118eb5760108181548110611855576118556147a8565b906000526020600020015488036118d9576010611873600186614688565b81548110611883576118836147a8565b9060005260206000200154601082815481106118a1576118a16147a8565b60009182526020909120015560108054806118be576118be6147be565b600190038181906000526020600020016000905590556118eb565b806118e38161462e565b91505061183a565b50601180546001818101835560009283527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6890910189905560068601849055429086015560148054849290611941908490614616565b9091555061197290508160c86119588560c7614647565b6119629190614666565b86546001600160a01b0316613393565b6119938161198261019085614666565b600e546001600160a01b0316613393565b6119c0816119a361019085614666565b60008a8152601760205260409020546001600160a01b0316613393565b6040518781527fac21b8aff4163c7e0a13ba5d42e7ef9c9566ebefd48783e8d02818a630e61f6390602001611749565b601d546001600160a01b0382166000908152601c6020526040812054909190818303611a20575060009392505050565b81601b5482611a2f9190614647565b611a399190614666565b949350505050565b60065481906001600160a01b0390811690821603611a72576040516368f7a67560e11b815260040160405180910390fd5b6000611a7c612efb565b600b546001600160a01b038581166000818152601f60205260408082205490516335ea6a7560e01b8152600481019390935294955092821693918516906335ea6a759060240161018060405180830381865afa158015611ae0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0491906144fa565b60e001516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611b4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b7191906145e7565b611b7b9190614688565b604051631a4ca37b60e21b81526001600160a01b03878116600483015260248201839052306044830152919250908416906369328dec906064016020604051808303816000875af1158015611bd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bf891906145e7565b50611c0685836000846135c5565b505050505050565b6000818152600f6020526040812060068101548203611c305750600092915050565b60038101546007820154611c45906001614616565b611c4f9190614647565b8160010154611c5e9190614616565b9392505050565b6001600160a01b038116600090815260196020526040812054601a54808303611c915760009250611ca4565b80611c9a612a6e565b611a2f9084614647565b5050919050565b60606004805461098190614433565b3360009081526001602090815260408083206001600160a01b038616845290915281205482811015611d3c5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610ba0565b611d493385858403612dd6565b5060019392505050565b6000828152600f6020526040812060060154839103611d855760405163407f400d60e01b815260040160405180910390fd5b6000838152600f60205260409020600a81015460ff1615611db957604051632491521f60e01b815260040160405180910390fd5b600754611dc585611c0e565b611dcf9190614616565b4211611dee5760405163261094a960e21b815260040160405180910390fd5b60098101546006546000906001600160a01b03168215611e655760088401546001600160a01b03166000818152601f602052604081208054869290611e34908490614688565b909155505060006009860155611e4b818530613393565b611e57818389876135c5565b9250611e638284613524565b505b6006840154808310611e9457611e8f82611e7f8386614688565b87546001600160a01b0316613393565b611f63565b611e9e8382614688565b90506000611eaa610a1b565b611eb98364e8d4a51000614647565b611ec39190614666565b9050600064e8d4a5100082601b54611edb9190614647565b611ee59190614666565b9050600064e8d4a5100083611ef8612a6e565b611f029190614647565b611f0c9190614666565b90508360146000828254611f209190614688565b90915550611f3090508183614616565b60186000828254611f419190614616565b9250508190555081601b6000828254611f5a9190614688565b90915550505050505b6013805460009081526012602052604081208a905581549190611f858361462e565b9091555050600a8501805460ff19166001179055611fa2886136ae565b6040518881527f2c93ebe7e3acf24c31cb32e31caac61a6b39275a7cf4f2d144f9fa5f6f2838109060200160405180910390a15050505050505050565b6005546001600160a01b03908116907f0000000000000000000000007edb0c8b428b97ea1ca44ea9fcda0835fbd880291633148015906120285750336001600160a01b03821614155b156120a1576a013da329b633647180000061204282611c65565b101561206157604051635123fc5760e11b815260040160405180910390fd5b60085460ff16801561208357503360009081526015602052604090205460ff16155b156120a15760405163532fc80960e01b815260040160405180910390fd5b81806000036120c35760405163af458c0760e01b815260040160405180910390fd5b336001600160a01b037f0000000000000000000000007edb0c8b428b97ea1ca44ea9fcda0835fbd8802916810361210257506005546001600160a01b03165b601a5484908015612153576000612117612a6e565b90508060000361213a57604051630166412360e01b815260040160405180910390fd5b806121458389614647565b61214f9190614666565b9250505b600b546001600160a01b031661216b8133308a6133fc565b6001600160a01b03841660009081526019602052604081208054859290612193908490614616565b9250508190555082601a60008282546121ac9190614616565b909155505060408051338152602081018990527f705dbbe97fba7049622083cf39e9486517651c1e99df5f423780fb0f93b1f8e09101611749565b6000610a11338484612f69565b6000818152600f60205260408120600601548291036122265760405163407f400d60e01b815260040160405180910390fd5b6000828152600f602052604081206006810154600482015491929091612710906122509084614647565b61225a9190614666565b905060006122688284614616565b600654601480549293506001600160a01b039091169185919060009061228f908490614688565b909155505060006006860155600585015460078601556122ae876136ae565b6122ba813330856133fc565b6122df81600a6122cb866003614647565b6122d59190614666565b6117099085614688565b6040518781527f1fb4fd74b5293affc7e1783f9ddce5c78840d2faa4553ad905f3c844d97cdec390602001611749565b80806000036123315760405163af458c0760e01b815260040160405180910390fd5b6005546001600160a01b0316338114801561234d575060115415155b1561236b57604051632e0ccae760e01b815260040160405180910390fd5b336000908152601960205260409020548381101561239c57604051631e9acf1760e31b815260040160405180910390fd5b336000908152601660205260408120439055601a546123b9612a6e565b6123c39087614647565b6123cd9190614666565b336000908152601960205260408120805492935087929091906123f1908490614688565b9250508190555084601a600082825461240a9190614688565b9091555050601b54819080156124365780601d54846124299190614647565b6124339190614666565b91505b336000908152601c602052604081208054849290612455908490614616565b9250508190555081601d600082825461246e9190614616565b9250508190555082601b60008282546124879190614616565b909155505060408051338152602081018990527fc201291f33bce3d28f4999ee7cea8cf46b839d53a58ed6ff7be7a4410aa8e53e9101611749565b6005546001600160a01b03908116907f0000000000000000000000007edb0c8b428b97ea1ca44ea9fcda0835fbd8802916331480159061250b5750336001600160a01b03821614155b15612584576a013da329b633647180000061252582611c65565b101561254457604051635123fc5760e11b815260040160405180910390fd5b60085460ff16801561256657503360009081526015602052604090205460ff16155b156125845760405163532fc80960e01b815260040160405180910390fd5b81806000036125a65760405163af458c0760e01b815260040160405180910390fd5b6125ae610a1b565b1580156125bd57506298968083105b156125db576040516392c968ed60e01b815260040160405180910390fd5b6006546040805163313ce56760e01b815290516001600160a01b0390921691600191600091849163313ce5679160048083019260209291908290030181865afa15801561262c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061265091906147d4565b60ff1690508060121461267657612668816012614688565b61267390600a61479c565b91505b60006126828388614647565b905061268d60025490565b156126b45761269a610a1b565b6002546126a79089614647565b6126b19190614666565b90505b6126c08433308a6133fc565b6126ca3382613766565b6126d48488613524565b60408051338152602081018990527f10d8a0a86ea75b902f0343c144f8aa26be01c323cad73779d0bb8c73cb4a15369101611749565b336001600160a01b037f0000000000000000000000007edb0c8b428b97ea1ca44ea9fcda0835fbd88029161461273f57600080fd5b600680546001600160a01b03199081166001600160a01b038c811691909117909255600b805482168b8416179055600880546005805484168b86161790556007899055600c80548416898616179055600d805484168786161790556009805484168886161790556001600160a81b0319168a1515610100600160a81b031916176101009385169390930292909217909155600e80547327a99802fc48b57670846abfff5f2dcde8a6fc29921691909117905542601e556127fd612efb565b6040516335ea6a7560e01b81526001600160a01b038b8116600483015291909116906335ea6a759060240161018060405180830381865afa158015612846573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061286a91906144fa565b60e001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128cf91906147d4565b60ff16896001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612910573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293491906147d4565b60ff161461294157600080fd5b505050505050505050565b6005546001600160a01b031633811461298357604051632e86c5bb60e21b81526001600160a01b0382166004820152602401610ba0565b6008805460ff19168315159081179091556040519081527f409f2525ed22a884cfcdab48ec9a11940ed320013c22f86652d3f91a160d12039060200160405180910390a15050565b6001600160a01b0381166000908152602081905260408120546002546000036129f75750600092915050565b600254612a02610a1b565b612a0c9083614647565b611c5e9190614666565b6005546001600160a01b0316338114612a4d57604051632e86c5bb60e21b81526001600160a01b0382166004820152602401610ba0565b6040805160008152602081019182905251612a6a91601091613ebc565b5050565b601854601b54600b546040516370a0823160e01b815230600482015260009392916001600160a01b0316906370a0823190602401602060405180830381865afa158015612abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae391906145e7565b612aed9190614688565b610b0d9190614688565b600654600b546005546040516370a0823160e01b81523060048201526001600160a01b0393841693928316929091169060009060039085906370a0823190602401602060405180830381865afa158015612b55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b7991906145e7565b612b839190614666565b9050612b90848383613139565b6040516370a0823160e01b8152306004820152600090612c0a908690869084906001600160a01b038416906370a0823190602401602060405180830381865afa158015612be1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0591906145e7565b6135c5565b60408051848152602081018390529192507f4ae0b37d5a39fb263fa4f0f32474835a7c0756c2a48f5e7b27b048b80f76a4739101610d31565b600d54604051630cc7d40f60e11b81523060048201526000916001600160a01b03169063198fa81e90602401602060405180830381865afa158015612c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cb091906145e7565b600e549091506001600160a01b03166000829003612ce15760405163af458c0760e01b815260040160405180910390fd5b600d54604051633111e7b360e01b81526001600160a01b0390911690633111e7b390612d179087908790879087906004016147ef565b6020604051808303816000875af1158015612d36573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ac91906145e7565b8080600003612d7c5760405163af458c0760e01b815260040160405180910390fd5b6006546001600160a01b0316612d94813330866133fc565b612d9e8184613524565b6040518381527f6ff2115c70ad2c537458a34e1906c5b884dbd79cabd620ada0fbe07892077bc99060200160405180910390a1505050565b6001600160a01b038316612e385760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610ba0565b6001600160a01b038216612e995760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610ba0565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b600c5460408051630261bf8b60e01b815290516000926001600160a01b031691630261bf8b9160048083019260209291908290030181865afa158015612f45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0d9190614855565b6001600160a01b038316612fcd5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610ba0565b6001600160a01b03821661302f5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610ba0565b6001600160a01b038316600090815260208190526040902054818110156130a75760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610ba0565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906130de908490614616565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161312a91815260200190565b60405180910390a35b50505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916131959190614872565b6000604051808303816000865af19150503d80600081146131d2576040519150601f19603f3d011682016040523d82523d6000602084013e6131d7565b606091505b5091509150818015613201575080511580613201575080806020019051810190613201919061488e565b6115ac5760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610ba0565b6001600160a01b0382166132ad5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610ba0565b6001600160a01b038216600090815260208190526040902054818110156133215760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610ba0565b6001600160a01b0383166000908152602081905260408120838303905560028054849290613350908490614688565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612eee565b81806000036133b55760405163af458c0760e01b815260040160405180910390fd5b6133bd612efb565b604051631a4ca37b60e21b81526001600160a01b03868116600483015260248201869052848116604483015291909116906369328dec90606401612d17565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916134609190614872565b6000604051808303816000865af19150503d806000811461349d576040519150601f19603f3d011682016040523d82523d6000602084013e6134a2565b606091505b50915091508180156134cc5750805115806134cc5750808060200190518101906134cc919061488e565b611c065760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401610ba0565b600061352e612efb565b905061353c83826000613845565b613547838284613845565b61354f612efb565b60405163e8eda9df60e01b81526001600160a01b0385811660048301526024820185905230604483015260006064830152919091169063e8eda9df90608401600060405180830381600087803b1580156135a857600080fd5b505af11580156135bc573d6000803e3d6000fd5b50505050505050565b6000816000036135e85760405163af458c0760e01b815260040160405180910390fd5b6008546009546001600160a01b0361010090920482169116600061360d838984613959565b9050600061361c84848a613959565b9050826001600160a01b0316896001600160a01b03160361363e578091508594505b613649898388613139565b826001600160a01b0316896001600160a01b0316146136725761366f8984888585613a32565b94505b61367f8389878430613a32565b9450848711156136a257604051636dd1824760e11b815260040160405180910390fd5b50505050949350505050565b60005b601154811015612a6a57601181815481106136ce576136ce6147a8565b9060005260206000200154820361375457601180546136ef90600190614688565b815481106136ff576136ff6147a8565b90600052602060002001546011828154811061371d5761371d6147a8565b600091825260209091200155601180548061373a5761373a6147be565b600190038181906000526020600020016000905590555050565b8061375e8161462e565b9150506136b1565b6001600160a01b0382166137bc5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610ba0565b80600260008282546137ce9190614616565b90915550506001600160a01b038216600090815260208190526040812080548392906137fb908490614616565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b17905291516000928392908716916138a19190614872565b6000604051808303816000865af19150503d80600081146138de576040519150601f19603f3d011682016040523d82523d6000602084013e6138e3565b606091505b509150915081801561390d57508051158061390d57508080602001905181019061390d919061488e565b6115ac5760405162461bcd60e51b815260206004820152601e60248201527f5472616e7366657248656c7065723a20415050524f56455f4641494c454400006044820152606401610ba0565b60008060006139688585613b20565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b1660348201529193509150869060480160405160208183030381529060405280519060200120604051602001613a109291906001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b60085460009061010090046001600160a01b03168180613a53838a8a613c17565b91509150613a62878383613ce1565b93506000613a708a8a613b20565b509050600080826001600160a01b03168c6001600160a01b031614613a9757866000613a9b565b6000875b6040805160008152602081019182905263022c0d9f60e01b90915291935091506001600160a01b038a169063022c0d9f90613adf90859085908d90602481016148ab565b600060405180830381600087803b158015613af957600080fd5b505af1158015613b0d573d6000803e3d6000fd5b5050505050505050505095945050505050565b600080826001600160a01b0316846001600160a01b031603613b925760405162461bcd60e51b815260206004820152602560248201527f556e697377617056324c6962726172793a204944454e544943414c5f41444452604482015264455353455360d81b6064820152608401610ba0565b826001600160a01b0316846001600160a01b031610613bb2578284613bb5565b83835b90925090506001600160a01b038216613c105760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f4144445245535300006044820152606401610ba0565b9250929050565b6000806000613c268585613b20565b509050600080613c37888888613959565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015613c74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c9891906148ef565b506001600160701b031691506001600160701b03169150826001600160a01b0316876001600160a01b031614613ccf578082613cd2565b81815b90999098509650505050505050565b6000808411613d465760405162461bcd60e51b815260206004820152602b60248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4960448201526a1394155517d05353d5539560aa1b6064820152608401610ba0565b600083118015613d565750600082115b613db35760405162461bcd60e51b815260206004820152602860248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4c604482015267495155494449545960c01b6064820152608401610ba0565b6000613dc1856103e5613e00565b90506000613dcf8285613e00565b90506000613de983613de3886103e8613e00565b90613e67565b9050613df58183614666565b979650505050505050565b6000811580613e2457508282613e168183614647565b9250613e229083614666565b145b610a155760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b6044820152606401610ba0565b600082613e748382614616565b9150811015610a155760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b6044820152606401610ba0565b828054828255906000526020600020908101928215613ef7579160200282015b82811115613ef7578251825591602001919060010190613edc565b50613f03929150613f07565b5090565b5b80821115613f035760008155600101613f08565b6001600160a01b0381168114613f3157600080fd5b50565b600060208284031215613f4657600080fd5b8135611c5e81613f1c565b60005b83811015613f6c578181015183820152602001613f54565b838111156131335750506000910152565b60008151808452613f95816020860160208601613f51565b601f01601f19169290920160200192915050565b602081526000611c5e6020830184613f7d565b60008060408385031215613fcf57600080fd5b8235613fda81613f1c565b946020939093013593505050565b600080600060608486031215613ffd57600080fd5b833561400881613f1c565b9250602084013561401881613f1c565b929592945050506040919091013590565b60006020828403121561403b57600080fd5b5035919050565b8015158114613f3157600080fd5b803561405b81614042565b919050565b634e487b7160e01b600052604160045260246000fd5b604051610180810167ffffffffffffffff8111828210171561409a5761409a614060565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156140c9576140c9614060565b604052919050565b60008060008060008060008060006101208a8c0312156140f057600080fd5b893598506020808b0135985060408b0135975060608b0135965060808b013561411881614042565b955060a08b013561412881613f1c565b945060c08b0135935060e08b013561413f81613f1c565b92506101008b013567ffffffffffffffff8082111561415d57600080fd5b818d0191508d601f83011261417157600080fd5b81358181111561418357614183614060565b614195601f8201601f191685016140a0565b91508082528e848285010111156141ab57600080fd5b80848401858401376000848284010152508093505050509295985092959850929598565b600080604083850312156141e257600080fd5b823567ffffffffffffffff808211156141fa57600080fd5b818501915085601f83011261420e57600080fd5b813560208282111561422257614222614060565b8160051b92506142338184016140a0565b828152928401810192818101908985111561424d57600080fd5b948201945b84861015614277578535935061426784613f1c565b8382529482019490820190614252565b96506142869050878201614050565b9450505050509250929050565b600080604083850312156142a657600080fd5b50508035926020909101359150565b60008060008060008060008060006101208a8c0312156142d457600080fd5b89356142df81613f1c565b985060208a01356142ef81613f1c565b975060408a01356142ff81614042565b965060608a013561430f81613f1c565b955060808a0135945060a08a013561432681613f1c565b935060c08a013561433681613f1c565b925060e08a013561434681613f1c565b91506101008a013561435781613f1c565b809150509295985092959850929598565b60006020828403121561437a57600080fd5b8135611c5e81614042565b6000806040838503121561439857600080fd5b82356143a381613f1c565b915060208301356143b381613f1c565b809150509250929050565b600080602083850312156143d157600080fd5b823567ffffffffffffffff808211156143e957600080fd5b818501915085601f8301126143fd57600080fd5b81358181111561440c57600080fd5b8660208260051b850101111561442157600080fd5b60209290920196919550909350505050565b600181811c9082168061444757607f821691505b602082108103610c1057634e487b7160e01b600052602260045260246000fd5b60006020828403121561447957600080fd5b6040516020810181811067ffffffffffffffff8211171561449c5761449c614060565b6040529151825250919050565b80516fffffffffffffffffffffffffffffffff8116811461405b57600080fd5b805164ffffffffff8116811461405b57600080fd5b805161405b81613f1c565b805160ff8116811461405b57600080fd5b6000610180828403121561450d57600080fd5b614515614076565b61451f8484614467565b815261452d602084016144a9565b602082015261453e604084016144a9565b604082015261454f606084016144a9565b6060820152614560608084016144a9565b608082015261457160a084016144a9565b60a082015261458260c084016144c9565b60c082015261459360e084016144de565b60e08201526101006145a68185016144de565b908201526101206145b88482016144de565b908201526101406145ca8482016144de565b908201526101606145dc8482016144e9565b908201529392505050565b6000602082840312156145f957600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561462957614629614600565b500190565b60006001820161464057614640614600565b5060010190565b600081600019048311821515161561466157614661614600565b500290565b60008261468357634e487b7160e01b600052601260045260246000fd5b500490565b60008282101561469a5761469a614600565b500390565b828152604060208201526000611a396040830184613f7d565b600181815b808511156146f35781600019048211156146d9576146d9614600565b808516156146e657918102915b93841c93908002906146bd565b509250929050565b60008261470a57506001610a15565b8161471757506000610a15565b816001811461472d576002811461473757614753565b6001915050610a15565b60ff84111561474857614748614600565b50506001821b610a15565b5060208310610133831016604e8410600b8410161715614776575081810a610a15565b61478083836146b8565b806000190482111561479457614794614600565b029392505050565b6000611c5e83836146fb565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b6000602082840312156147e657600080fd5b611c5e826144e9565b6060808252810184905260008560808301825b8781101561483257823561481581613f1c565b6001600160a01b0316825260209283019290910190600101614802565b50602084019590955250506001600160a01b039190911660409091015292915050565b60006020828403121561486757600080fd5b8151611c5e81613f1c565b60008251614884818460208701613f51565b9190910192915050565b6000602082840312156148a057600080fd5b8151611c5e81614042565b84815283602082015260018060a01b03831660408201526080606082015260006113226080830184613f7d565b80516001600160701b038116811461405b57600080fd5b60008060006060848603121561490457600080fd5b61490d846148d8565b925061491b602085016148d8565b9150604084015163ffffffff8116811461493457600080fd5b80915050925092509256fea26469706673582212204a44eda7c82a4f75bf57931551ba7ea5887003ffc29f09cc1a123675f8afc79564736f6c634300080d0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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