ETH Price: $2,398.59 (-0.29%)

Contract

0x9b20bAd4B7759ECa269a4B2607A2999f39820698
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Initialize175174582023-06-20 0:50:47444 days ago1687222247IN
0x9b20bAd4...f39820698
0 ETH0.0032590412.83251906
0x61012060175174572023-06-20 0:50:35444 days ago1687222235IN
 Create: LSDai
0 ETH0.026735213.20155908

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LSDai

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 13 : LSDai.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
// Custom Ownable logic from OZ
import {Ownable} from "./Ownable.sol";

// Interfaces
import {ILSDai} from "./interfaces/ILSDai.sol";

// DSR helpers
import {RMath} from "./libraries/RMath.sol";
import {IDai} from "./interfaces/IDai.sol";
import {IPot} from "./interfaces/IPot.sol";
import {IJoin} from "./interfaces/IJoin.sol";
import {IVat} from "./interfaces/IVat.sol";

/**
 * @title LSDAI
 * @dev LSDai is a rebasing token that earns interest on DAI deposited in the MakerDAO DSR.
 */
contract LSDai is Ownable, ILSDai {
  error LSDai__AlreadyInitialized();
  error LSDai__DepositCapLowerThanTotalPooledDai();
  error LSDai__DepositCap();
  error LSDai__WithdrawalFeeLow();
  error LSDai__InterestFeeLow();
  error LSDai__TransferToZeroAddress();
  error LSDai__TransferFromZeroAddress();
  error LSDai__TransferToLSDaiContract();
  error LSDai__MintToZeroAddress();
  error LSDai__BurnFromZeroAddress();
  error LSDai__SharesAmountExceedsBalance();
  error LSDai__FeeRecipientZeroAddress();
  error LSDai__RebaseOverflow(uint256 preRebaseTotalPooledDai, uint256 postRebaseTotalPooledDai);

  using SafeMath for uint256;
  ///////////////////////////
  //     ERC20 storage     //
  ///////////////////////////

  /**
   * @dev Returns the name of the token.
   */
  string public name;

  /**
   * @dev Returns the symbol of the token, usually a shorter version of the
   * name.
   */
  string public symbol;

  /**
   * @dev Returns the number of decimals used to get its user representation.
   */
  uint256 public immutable decimals = 18;

  /**
   * @dev Returns the amount of tokens in existence.
   */
  mapping(address => mapping(address => uint256)) private _allowances;

  /*//////////////////////////////////////////////////////////////
                                 LSDAI STORAGE
    //////////////////////////////////////////////////////////////*/

  /**
   * @dev LSDAI is initialized.
   */
  bool private _initialized;

  /**
   * @dev LSDAI deposit cap. This is the maximum amount of DAI that can be deposited.
   */
  uint256 public depositCap;

  /**
   * @dev Address shares
   */
  mapping(address => uint256) private _shares;

  /**
   * @dev Total shares of LSDAI
   */
  uint256 private _totalLsdaiShares;

  /**
   * @notice Total amount of DAI controlled by LSDAI at MakerDAO DSR.
   * @dev This value must be updated before depositing or withdrawing.
   */
  uint256 private _totalPooledDai;

  /**
   * @dev the total amount of pot shares
   */
  uint256 private _totalPotShares;

  ///////////////////////////
  // LSDAI Fee Information //
  ///////////////////////////
  /**
   * @notice Interest fee taken on interest earned, in basis points.
   */
  uint256 public interestFee = 250; // 2.5%

  /**
   * @notice Withdrawal fee taken on exit, in basis points.
   */
  uint256 public withdrawalFee = 1; // 0.01%

  /**
   * @notice Fee recipient address.
   */
  address public feeRecipient;

  ///////////////////////////
  // MakerDAO DSR Contracts //
  ///////////////////////////
  IVat public immutable vat = IVat(0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B);
  IPot public immutable pot = IPot(0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7);
  IJoin public immutable daiJoin = IJoin(0x9759A6Ac90977b93B58547b4A71c78317f391A28);
  IDai public immutable dai = IDai(0x6B175474E89094C44Da98b954EedeAC495271d0F);

  /**
   * @dev initializes the contract.
   * @param _depositCap the DAI deposit cap.
   * @param _interestFee the interest fee percentage in basis points (1/100 of a percent)
   * @param _withdrawalFee the withdrawal fee percentage in basis points (1/100 of a percent)
   * @param _feeRecipient the address of the fee recipient
   */
  function initialize(uint256 _depositCap, uint256 _interestFee, uint256 _withdrawalFee, address _feeRecipient)
    external
    returns (bool)
  {
    if (_initialized) {
      revert LSDai__AlreadyInitialized();
    }

    // Transfer ownership to message sender
    _transferOwnership(msg.sender);

    // Set ERC20 name and symbol
    name = "Liquid Savings DAI";
    symbol = "LSDAI";

    // Set initial deposit cap to 10m DAI
    setDepositCap(_depositCap);
    // Set fee information
    setFeeRecipient(_feeRecipient);
    setWithdrawalFee(_withdrawalFee);
    setInterestFee(_interestFee);

    _initialized = true;

    // Setup the LSDAI contract to be able to interact with the MakerDAO contracts and DAI token
    vat.hope(address(daiJoin));
    vat.hope(address(pot));
    dai.approve(address(daiJoin), type(uint256).max);

    return true;
  }

  /**
   * @return the amount of shares owned by `_account`.
   */
  function sharesOf(address account) public view returns (uint256) {
    return _shares[account];
  }

  /**
   * @dev returns the amount of pot shares the LSDAI contract has in the DSR pot contract
   */
  function potShares() public view returns (uint256) {
    return pot.pie(address(this));
  }

  /**
   * @dev Deposit DAI and mint LSDAI.
   * @param to The address to mint LSDAI to.
   * @param daiAmount The amount of DAI to deposit.
   * @return amount of LSDAI minted.
   */
  function deposit(address to, uint256 daiAmount) external returns (uint256) {
    dai.transferFrom(msg.sender, address(this), daiAmount);
    return _deposit(to, daiAmount);
  }

  /**
   * @dev Deposit DAI and mint LSDAI.
   * @param to The address to mint LSDAI to.
   * @param daiAmount The amount of DAI to deposit.
   * @param permitNonce The nonce of the permit signature.
   * @param permitExpiry The deadline timestamp, type(uint256).max for no deadline.
   * @param permitV The recovery byte of the signature.
   * @param permitR Half of the ECDSA signature pair.
   * @param permitS Half of the ECDSA signature pair.
   * @return amount of LSDAI minted.
   */
  function depositWithPermit(
    address to,
    uint256 daiAmount,
    uint256 permitNonce,
    uint256 permitExpiry,
    uint8 permitV,
    bytes32 permitR,
    bytes32 permitS
  ) external returns (uint256) {
    dai.permit(msg.sender, address(this), permitNonce, permitExpiry, true, permitV, permitR, permitS);
    dai.transferFrom(msg.sender, address(this), daiAmount);
    return _deposit(to, daiAmount);
  }

  /**
   * Withdraw DAI from the contract
   * @param daiAmount The amount of LSDAI to withdraw. wad is denominated in dai
   */
  function withdraw(uint256 daiAmount) external returns (bool) {
    return _withdraw(daiAmount, withdrawalFee);
  }

  /**
   * @dev withdraws the pending protocol fees from the DSR pot to the owner. Only callable by the owner.
   */
  function collectFees() external onlyOwner returns (bool) {
    return _withdraw(balanceOf(feeRecipient), 0);
  }

  /**
   * @dev Updates the withdrawal fee. Only callable by the owner.
   * @param fee The new withdrawal fee, in basis points.
   */
  function setWithdrawalFee(uint256 fee) public onlyOwner {
    if (fee < 0) {
      revert LSDai__WithdrawalFeeLow();
    }

    withdrawalFee = fee;

    emit WithdrawalFeeSet(fee);
  }

  /**
   * @dev Updates the interest fee. Only callable by the owner.
   * @param fee The new interest fee, in basis points.
   */
  function setInterestFee(uint256 fee) public onlyOwner {
    if (fee < 0) {
      revert LSDai__InterestFeeLow();
    }

    interestFee = fee;

    emit InterestFeeSet(fee);
  }

  /**
   * @dev Updates the fee recipient. Only callable by the owner.
   * @param recipient The new fee recipient.
   */
  function setFeeRecipient(address recipient) public onlyOwner {
    if (recipient == address(0)) {
      revert LSDai__FeeRecipientZeroAddress();
    }

    feeRecipient = recipient;

    emit FeeRecipientSet(recipient);
  }

  /**
   * @return the amount of tokens owned by the `account`.
   *
   * @dev Balances are dynamic and equal the `account`'s share in the amount of the
   * total DAI controlled by the protocol. See `sharesOf`.
   */
  function balanceOf(address account) public view virtual override returns (uint256) {
    return getPooledDaiByShares(sharesOf(account));
  }

  /**
   * @return the amount of shares that corresponds to `daiAmount` protocol-controlled DAI.
   * @param daiAmount The amount of protocol-controlled DAI.
   */
  function getSharesByPooledDai(uint256 daiAmount) public view returns (uint256) {
    // Prevent division by zero
    if (_totalPooledDai == 0) {
      return daiAmount;
    }

    return daiAmount.mul(_totalLsdaiShares).div(_totalPooledDai);
  }

  /**
   * @return the amount of DAI that corresponds to `sharesAmount` token shares.
   * @param sharesAmount The amount of LSDAI shares.
   */
  function getPooledDaiByShares(uint256 sharesAmount) public view returns (uint256) {
    return sharesAmount.mul(_totalPooledDai).div(_totalLsdaiShares);
  }

  /**
   * @return the amount of tokens in existence.
   *
   * @dev Always equals to `_getTotalPooledDai()` since token amount
   * is pegged to the total amount of DAI controlled by the protocol.
   */
  function totalSupply() public view override returns (uint256) {
    return _getTotalPooledDai();
  }

  /**
   * @return the amount of total LSDAI shares
   */
  function totalShares() public view returns (uint256) {
    return _totalLsdaiShares;
  }

  /**
   * @dev rebase the total pooled DAI, user balance and total supply of LSDAI.
   * Can only be called by anyone
   */
  function rebase() external {
    uint256 chi = _getMostRecentChi();
    _rebase(chi, true);
  }

  /**
   * @notice Sets deposit cap. Exclusive for the owner.
   */
  function setDepositCap(uint256 cap) public onlyOwner {
    // Must be higher than total pooled DAI
    if (cap < _getTotalPooledDai()) {
      revert LSDai__DepositCapLowerThanTotalPooledDai();
    }

    depositCap = cap;

    emit DepositCapSet(cap);
  }

  /**
   * Returns DAI balance at the MakerDAO DSR contract.
   */
  function getTotalPotSharesValue() external view returns (uint256) {
    uint256 chi = (block.timestamp > pot.rho())
      ? (RMath.rpow(pot.dsr(), block.timestamp - pot.rho()) * pot.chi()) / RMath.RAY
      : pot.chi();

    // total pooled DAI is the total shares times the chi
    return (_totalPotShares * chi) / RMath.RAY;
  }

  ///////////////////////////////////////
  ///////// Internal functions /////////
  /////////////////////////////////////

  /**
   * @dev Deposit DAI and mint LSDAI.
   * @param _to The address to mint LSDAI to.
   * @param _daiAmount The amount of DAI to deposit.
   * @return shares amount of LSDAI minted.
   */
  function _deposit(address _to, uint256 _daiAmount) internal returns (uint256 shares) {
    // Check if the deposit cap is reached
    if (depositCap > 0 && _getTotalPooledDai().add(_daiAmount) > depositCap) {
      revert LSDai__DepositCap();
    }

    uint256 chi = _getMostRecentChi();

    // Calculate the amount of pot shares to mint
    uint256 potSharesAmount = RMath.rdiv(_daiAmount, chi);

    // Mint the shares to the user
    shares = getSharesByPooledDai(_daiAmount);
    _mintShares(_to, shares);

    // Increase the total amount of DAI pooled
    _totalPooledDai = _totalPooledDai.add(_daiAmount);
    // Keep track of total pot shares controlled by LSDAI
    _totalPotShares = _totalPotShares.add(potSharesAmount);

    // Mint LSDAI at 1:1 ratio to DAI
    emit Transfer(address(0), _to, _daiAmount);

    // Join the DSR on behalf of the user
    daiJoin.join(address(this), _daiAmount);
    pot.join(potSharesAmount);
  }

  /**
   * Withdraw shares back to DAI
   * @param _daiAmount The amount of LSDAI to withdraw. wad is denominated in (1/chi) * dai
   * @param _withdrawFee The fee to be charged on the withdrawal, in basis points.
   */
  function _withdraw(uint256 _daiAmount, uint256 _withdrawFee) internal returns (bool) {
    uint256 chi = _getMostRecentChi();

    // Split the amount into the fee and the actual withdrawal
    uint256 feeAmount = _daiAmount.mul(_withdrawFee).div(10_000);
    // Amount going to the user
    uint256 withdrawAmount = _daiAmount.sub(feeAmount);

    // Transfer the fee shares to fee recipient
    // and burn the withdraw shares from the user
    uint256 feeShares = getSharesByPooledDai(feeAmount);
    uint256 withdrawShares = getSharesByPooledDai(withdrawAmount);

    // Decrease the total amount of DAI pooled
    _totalPooledDai = _totalPooledDai.sub(withdrawAmount);

    _transferShares(msg.sender, feeRecipient, feeShares);
    _burnShares(msg.sender, withdrawShares);

    // Withdraw from the DSR, roudning up ensures we get at least the amount of DAI requested
    uint256 withdrawPotShares = RMath.rdivup(withdrawAmount, chi);
    // Reduce the total pot shares controlled by LSDAI
    _totalPotShares = _totalPotShares.sub(withdrawPotShares);

    // Get back the DAI from the DSR to the contract
    pot.exit(withdrawPotShares);

    //  daiJoin.exit(msg.sender, rmul(chi, wad)); // wad is in dai units
    daiJoin.exit(address(this), withdrawAmount); // wad is in dai units

    // Send it over
    return dai.transfer(msg.sender, withdrawAmount);
  }

  /**
   * @notice Destroys `_sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
   * @dev This doesn't decrease the token total supply.
   *
   * Requirements:
   *
   * - `_account` cannot be the zero address.
   * - `_account` must hold at least `_sharesAmount` shares.
   * - the contract must not be paused.
   */
  function _burnShares(address _account, uint256 _sharesAmount) internal returns (uint256 newTotalShares) {
    if (_account == address(0)) {
      revert LSDai__BurnFromZeroAddress();
    }

    uint256 accountShares = _shares[_account];

    if (_sharesAmount > accountShares) {
      revert LSDai__SharesAmountExceedsBalance();
    }

    uint256 preRebaseTokenAmount = getPooledDaiByShares(_sharesAmount);

    newTotalShares = _totalLsdaiShares.sub(_sharesAmount);

    _totalLsdaiShares = newTotalShares;

    _shares[_account] = accountShares.sub(_sharesAmount);

    uint256 postRebaseTokenAmount = getPooledDaiByShares(_sharesAmount);

    emit SharesBurnt(_account, preRebaseTokenAmount, postRebaseTokenAmount, _sharesAmount);

    // Notice: we're not emitting a Transfer event to the zero address here since shares burn
    // works by redistributing the amount of tokens corresponding to the burned shares between
    // all other token holders. The total supply of the token doesn't change as the result.
    // This is equivalent to performing a send from `address` to each other token holder address,
    // but we cannot reflect this as it would require sending an unbounded number of events.

    // We're emitting `SharesBurnt` event to provide an explicit rebase log record nonetheless.
  }

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

  /**
   * @dev See {IERC20-approve}.
   *
   * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
   * `transferFrom`. This is semantically equivalent to an infinite approval.
   *
   * Requirements:
   *
   * - `spender` cannot be the zero address.
   */
  function approve(address spender, uint256 amount) public virtual override returns (bool) {
    address owner = msg.sender;
    _approve(owner, spender, amount);
    return true;
  }

  /**
   * @notice Moves `_amount` tokens from the caller's account to the `_recipient` account.
   *
   * @return a boolean value indicating whether the operation succeeded.
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the caller must have a balance of at least `_amount`.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function transfer(address _recipient, uint256 _amount) public override returns (bool) {
    _transfer(msg.sender, _recipient, _amount);
    return true;
  }

  /**
   * @notice Moves `_amount` tokens from `_sender` to `_recipient` using the
   * allowance mechanism. `_amount` is then deducted from the caller's
   * allowance.
   *
   * @return a boolean value indicating whether the operation succeeded.
   *
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_sender` and `_recipient` cannot be the zero addresses.
   * - `_sender` must have a balance of at least `_amount`.
   * - the caller must have allowance for `_sender`'s tokens of at least `_amount`.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function transferFrom(address _sender, address _recipient, uint256 _amount) external override returns (bool) {
    _spendAllowance(_sender, msg.sender, _amount);
    _transfer(_sender, _recipient, _amount);
    return true;
  }

  /**
   * @notice Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account.
   *
   * @return amount of transferred tokens.
   * Emits a `TransferShares` event.
   * Emits a `Transfer` event.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the caller must have at least `_sharesAmount` shares.
   * - the contract must not be paused.
   *
   * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
   */
  function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256) {
    _transferShares(msg.sender, _recipient, _sharesAmount);
    uint256 tokensAmount = getPooledDaiByShares(_sharesAmount);
    _emitTransferEvents(msg.sender, _recipient, tokensAmount, _sharesAmount);
    return tokensAmount;
  }

  /**
   * @notice Moves `_sharesAmount` token shares from the `_sender` account to the `_recipient` account.
   *
   * @return amount of transferred tokens.
   * Emits a `TransferShares` event.
   * Emits a `Transfer` event.
   *
   * Requirements:
   *
   * - `_sender` and `_recipient` cannot be the zero addresses.
   * - `_sender` must have at least `_sharesAmount` shares.
   * - the caller must have allowance for `_sender`'s tokens of at least `getPooledDaiByShares(_sharesAmount)`.
   * - the contract must not be paused.
   *
   * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
   */
  function transferSharesFrom(address _sender, address _recipient, uint256 _sharesAmount) external returns (uint256) {
    uint256 tokensAmount = getPooledDaiByShares(_sharesAmount);
    _spendAllowance(_sender, msg.sender, tokensAmount);
    _transferShares(_sender, _recipient, _sharesAmount);
    _emitTransferEvents(_sender, _recipient, tokensAmount, _sharesAmount);
    return tokensAmount;
  }

  /**
   * @notice Moves `_amount` tokens from `_sender` to `_recipient`.
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   */
  function _transfer(address _sender, address _recipient, uint256 _amount) internal {
    uint256 _sharesToTransfer = getSharesByPooledDai(_amount);
    _transferShares(_sender, _recipient, _sharesToTransfer);
    _emitTransferEvents(_sender, _recipient, _amount, _sharesToTransfer);
  }

  /**
   * @notice Moves `_sharesAmount` shares from `_sender` to `_recipient`.
   *
   * Requirements:
   *
   * - `_sender` cannot be the zero address.
   * - `_recipient` cannot be the zero address or the `LSDai` token contract itself
   * - `_sender` must hold at least `_sharesAmount` shares.
   * - the contract must not be paused.
   */
  function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal {
    if (_sender == address(0)) {
      revert LSDai__TransferFromZeroAddress();
    }
    if (_recipient == address(0)) {
      revert LSDai__TransferToZeroAddress();
    }

    if (_recipient == address(this)) {
      revert LSDai__TransferToLSDaiContract();
    }

    // _whenNotStopped();

    uint256 currentSenderShares = _shares[_sender];

    if (_sharesAmount > currentSenderShares) {
      revert LSDai__SharesAmountExceedsBalance();
    }

    _shares[_sender] = currentSenderShares.sub(_sharesAmount);
    _shares[_recipient] = _shares[_recipient].add(_sharesAmount);
  }

  /**
   * @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 {
    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 Updates `owner` s allowance for `spender` based on spent `amount`.
   *
   * Does not update the allowance amount in case of infinite allowance.
   * Revert if not enough allowance is available.
   *
   * Might emit an {Approval} event.
   */
  function _spendAllowance(address owner, address spender, uint256 amount) internal {
    uint256 currentAllowance = allowance(owner, spender);
    if (currentAllowance != type(uint256).max) {
      require(currentAllowance >= amount, "ERC20: insufficient allowance");
      unchecked {
        _approve(owner, spender, currentAllowance - amount);
      }
    }
  }

  /**
   * @dev Emits {Transfer} and {TransferShares} events
   */
  function _emitTransferEvents(address _from, address _to, uint256 _tokenAmount, uint256 _sharesAmount) internal {
    emit Transfer(_from, _to, _tokenAmount);
    emit TransferShares(_from, _to, _sharesAmount);
  }

  /**
   * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
   * @dev This doesn't increase the token total supply.
   *
   * NB: The method doesn't check protocol pause relying on the external enforcement.
   *
   * Requirements:
   *
   * - `_to` cannot be the zero address.
   * - the contract must not be paused.
   */
  function _mintShares(address _to, uint256 _sharesAmount) internal returns (uint256 newTotalShares) {
    if (_to == address(0)) {
      revert LSDai__TransferToZeroAddress();
    }

    newTotalShares = _totalLsdaiShares.add(_sharesAmount);

    /// @todo research a better place for the storage location for the total shares
    _totalLsdaiShares = newTotalShares;

    _shares[_to] = _shares[_to].add(_sharesAmount);
  }

  /**
   * @dev updates the total amount of DAI controlled by LSDai.
   * @param chi If overrideChi is greater than 0, it will use that chi instead of the most recent chi.
   * @param requireSuccess If true, it will revert if the delta pooled DAI underflows or overflows.
   * It also calcuates the fees on the accrued interest and appends them to the protocol fee pot.chi();
   */
  function _rebase(uint256 chi, bool requireSuccess) internal {
    uint256 preRebaseTotalPooledDai = _totalPooledDai;
    // total pooled DAI is the total shares times the chi
    uint256 postRebaseTotalPooledDai = (_totalPotShares * chi) / RMath.RAY;

    // Change in total pooled DAI is the total pooled DAI before fees minus the total pooled DAI after fees
    (bool isOk, uint256 deltaTotalPooledDai) = postRebaseTotalPooledDai.trySub(_totalPooledDai); // Interest earned since last rebase

    // Revert with custom error in event of underflow/overflow
    if (isOk == false && requireSuccess == true) {
      revert LSDai__RebaseOverflow(preRebaseTotalPooledDai, postRebaseTotalPooledDai);
    } else if (isOk == false) {
      return;
    }

    // Update total pooled DAI
    _totalPooledDai = postRebaseTotalPooledDai;

    // Get the fees on accrued interest
    uint256 protocolFeeDaiAmount = _calcInterestFees(deltaTotalPooledDai);

    // Mint LSdai shares to the protocol
    uint256 protocolFeeLsdaiShares = getSharesByPooledDai(protocolFeeDaiAmount);
    _mintShares(feeRecipient, protocolFeeLsdaiShares);
  }

  /**
   * Returns the total supply of LSDAI by converting the DSR shares to DAI
   */
  function _getTotalPooledDai() internal view returns (uint256) {
    return _totalPooledDai;
  }

  /**
   * @dev Calculates the fees on the accrued interest
   * @param _daiAmount The change in total pooled DAI since the last rebase
   */
  function _calcInterestFees(uint256 _daiAmount) internal view returns (uint256 protocolFee) {
    if (interestFee == 0) {
      return 0;
    }

    protocolFee = _daiAmount.mul(interestFee).div(10_000);
  }

  /**
   * @dev returns most recent chi (the rate accumulator) by calling drip if necessary
   */
  function _getMostRecentChi() internal returns (uint256) {
    if (block.timestamp > pot.rho()) {
      return pot.drip();
    }
    return pot.chi();
  }
}

File 2 of 13 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

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

File 3 of 13 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 4 of 13 : SafeMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

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

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

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

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

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

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 5 of 13 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 6 of 13 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 7 of 13 : IDai.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

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

interface IDai is IERC20 {
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  function permit(
    address holder,
    address spender,
    uint256 nonce,
    uint256 expiry,
    bool allowed,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

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

File 8 of 13 : IJoin.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IJoin {
  function join(address, uint256) external;

  function exit(address, uint256) external;
}

File 9 of 13 : ILSDai.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

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

/**
 * @title LSDai interface
 * @dev extention of ERC20 interface, with LSDai-specific events
 */
interface ILSDai is IERC20 {
  /**
   * @notice An executed shares transfer from `sender` to `recipient`.
   *
   * @dev emitted in pair with an ERC20-defined `Transfer` event.
   */
  event TransferShares(address indexed from, address indexed to, uint256 sharesValue);

  /**
   * @notice An executed `burnShares` request
   *
   * @dev Reports simultaneously burnt shares amount
   * and corresponding stETH amount.
   * The stETH amount is calculated twice: before and after the burning incurred rebase.
   *
   * @param account holder of the burnt shares
   * @param preRebaseTokenAmount amount of stETH the burnt shares corresponded to before the burn
   * @param postRebaseTokenAmount amount of stETH the burnt shares corresponded to after the burn
   * @param sharesAmount amount of burnt shares
   */
  event SharesBurnt(
    address indexed account, uint256 preRebaseTokenAmount, uint256 postRebaseTokenAmount, uint256 sharesAmount
  );

  /**
   * @dev emitted when the DAI deposit cap is set. set `setDepositCap` for more details.
   */
  event DepositCapSet(uint256 depositCap);

  /**
   * @dev emitted when the withdrawal fee is set. set `setWithdrawalFee` for more details.
   */
  event WithdrawalFeeSet(uint256 withdrawalFee);

  /**
   * @dev emitted when the interest fee is set. set `setInterestFee` for more details.
   */
  event InterestFeeSet(uint256 interestFee);

  /**
   * @dev emitted when the fee recipient is set. set `setFeeRecipient` for more details.
   */
  event FeeRecipientSet(address indexed recipient);

  /**
   * @notice The DAI deposit cap.
   * @dev can be changed by the owner of the contract.
   */
  function depositCap() external view returns (uint256);

  /**
   * @notice the fee recipient.
   * @dev can be changed by the owner of the contract.
   */
  function feeRecipient() external view returns (address);

  /**
   * @dev Updates the fee recipient. Only callable by the owner.
   * @param recipient The new fee recipient.
   */
  function setFeeRecipient(address recipient) external;

  /**
   * @notice sets the DAI deposit cap.
   * @dev can be changed by the owner of the contract.
   * @param cap the new DAI deposit cap.
   */
  function setDepositCap(uint256 cap) external;

  /**
   * @notice the interest fee percentage in basis points (1/100 of a percent)
   */
  function interestFee() external view returns (uint256);

  /**
   * @notice sets the interest fee percentage in basis points (1/100 of a percent)
   * @param fee the new interest fee percentage in basis points (1/100 of a percent)
   */
  function setInterestFee(uint256 fee) external;

  /**
   * @notice the withdrawal fee percentage in basis points (1/100 of a percent)
   */
  function withdrawalFee() external view returns (uint256);

  /**
   * @notice sets the withdrawal fee percentage in basis points (1/100 of a percent)
   * @param fee the new withdrawal fee percentage in basis points (1/100 of a percent)
   */
  function setWithdrawalFee(uint256 fee) external;

  /**
   * @dev initializes the contract.
   * @param _depositCap the DAI deposit cap.
   * @param _interestFee the interest fee percentage in basis points (1/100 of a percent)
   * @param _withdrawalFee the withdrawal fee percentage in basis points (1/100 of a percent)
   * @param _feeRecipient the address of the fee recipient
   */
  function initialize(uint256 _depositCap, uint256 _interestFee, uint256 _withdrawalFee, address _feeRecipient)
    external
    returns (bool);

  /**
   * @dev rebase the total pooled DAI, user balance and total supply of LSDAI.
   * Can only be called by anyone
   */
  function rebase() external;

  /**
   * @return the amount of tokens in existence.
   *
   * @dev Always equals to `_getTotalPooledDai()` since token amount
   * is pegged to the total amount of DAI controlled by the protocol.
   */
  function totalSupply() external view returns (uint256);

  /**
   * @return the amount of total LSDAI shares
   */
  function totalShares() external view returns (uint256);

  ////////////////////////////////////////
  // User functions //////////////////////
  ////////////////////////////////////////

  /// getters ///
  /**
   * @return the amount of shares owned by `_account`.
   */
  function sharesOf(address account) external view returns (uint256);

  /**
   * @notice Returns the amount of LSDai tokens owned by the `account`.
   * @dev Balances are dynamic and equal the `account`'s share in the amount of the
   * total DAI controlled by the protocol. See `sharesOf`.
   * @param account The address of the account to check the balance of.
   * @return The amount of LSDai tokens owned by the `account`.
   */
  function balanceOf(address account) external view returns (uint256);

  /**
   * @dev Deposit DAI and mint LSDAI.
   * @param to The address to mint LSDAI to.
   * @param daiAmount The amount of DAI to deposit.
   * @return amount of LSDAI minted.
   */
  function deposit(address to, uint256 daiAmount) external returns (uint256);

  /**
   * @dev Deposit DAI and mint LSDAI using ERC20 permit.
   * @param to The address to mint LSDAI to.
   * @param daiAmount The amount of DAI to deposit.
   * @param permitNonce The nonce of the permit signature.
   * @param permitExpiry The deadline timestamp, type(uint256).max for no deadline.
   * @param permitV The recovery byte of the signature.
   * @param permitR Half of the ECDSA signature pair.
   * @param permitS Half of the ECDSA signature pair.
   * @return amount amount of LSDAI minted.
   */
  function depositWithPermit(
    address to,
    uint256 daiAmount,
    uint256 permitNonce,
    uint256 permitExpiry,
    uint8 permitV,
    bytes32 permitR,
    bytes32 permitS
  ) external returns (uint256);

  /**
   * Withdraw DAI from the contract
   * @param daiAmount The amount of LSDAI to withdraw. wad is denominated in dai
   */
  function withdraw(uint256 daiAmount) external returns (bool);

  /**
   * @notice Returns the amount of LSDai shares that corresponds to `daiAmount` protocol-controlled DAI.
   * @param daiAmount The amount of protocol-controlled DAI.
   * @return The amount of LSDai shares that corresponds to `daiAmount` protocol-controlled DAI.
   */
  function getSharesByPooledDai(uint256 daiAmount) external view returns (uint256);

  /**
   * @notice Returns the amount of protocol-controlled DAI that corresponds to `sharesAmount` LSDai shares.
   * @param sharesAmount The amount of LSDai shares.
   * @return The amount of protocol-controlled DAI that corresponds to `sharesAmount` LSDai shares.
   */
  function getPooledDaiByShares(uint256 sharesAmount) external view returns (uint256);
}

File 10 of 13 : IPot.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IPot {
  function chi() external view returns (uint256);

  function rho() external view returns (uint256);

  function dsr() external view returns (uint256);

  function drip() external returns (uint256);

  function join(uint256) external;

  function exit(uint256) external;

  /**
   * @notice Return the balance of a given address in this contract. Normalised Savings Dai [wad]
   */
  function pie(address) external view returns (uint256);
}

File 11 of 13 : IVat.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IVat {
  function hope(address) external;
}

File 12 of 13 : RMath.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";

/**
 * @title RMath - math library
 * @notice based on MakerDAO's math function in DSRManager
 */
library RMath {
  // --- Math ---
  uint256 constant RAY = 10 ** 27;

  function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
    // always rounds down
    z = SafeMath.mul(x, y) / RAY;
  }

  function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
    // always rounds down
    z = SafeMath.mul(x, RAY) / y;
  }

  function rdivup(uint256 x, uint256 y) internal pure returns (uint256 z) {
    // always rounds up
    z = SafeMath.add(SafeMath.mul(x, RAY), SafeMath.sub(y, 1)) / y;
  }

  function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
    assembly {
      switch x
      case 0 {
        switch n
        case 0 { z := RAY }
        default { z := 0 }
      }
      default {
        switch mod(n, 2)
        case 0 { z := RAY }
        default { z := x }
        let half := div(RAY, 2) // for rounding.
        for { n := div(n, 2) } n { n := div(n, 2) } {
          let xx := mul(x, x)
          if iszero(eq(div(xx, x), x)) { revert(0, 0) }
          let xxRound := add(xx, half)
          if lt(xxRound, xx) { revert(0, 0) }
          x := div(xxRound, RAY)
          if mod(n, 2) {
            let zx := mul(z, x)
            if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0, 0) }
            let zxRound := add(zx, half)
            if lt(zxRound, zx) { revert(0, 0) }
            z := div(zxRound, RAY)
          }
        }
      }
    }
  }
}

File 13 of 13 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

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

  /**
   * @dev Throws if the sender is not the owner.
   */
  function _checkOwner() internal view virtual {
    require(owner() == msg.sender, "Ownable: caller is not the owner");
  }

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

  /**
   * @dev Transfers ownership of the contract to a new account (`newOwner`).
   * Can only be called by the current owner.
   */
  function transferOwnership(address newOwner) public virtual onlyOwner {
    require(newOwner != address(0), "Ownable: new owner is the zero address");
    _transferOwnership(newOwner);
  }

  /**
   * @dev Transfers ownership of the contract to a new account (`newOwner`).
   * Internal function without access restriction.
   */
  function _transferOwnership(address newOwner) internal virtual {
    address oldOwner = _owner;
    _owner = newOwner;
    emit OwnershipTransferred(oldOwner, newOwner);
  }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"LSDai__AlreadyInitialized","type":"error"},{"inputs":[],"name":"LSDai__BurnFromZeroAddress","type":"error"},{"inputs":[],"name":"LSDai__DepositCap","type":"error"},{"inputs":[],"name":"LSDai__DepositCapLowerThanTotalPooledDai","type":"error"},{"inputs":[],"name":"LSDai__FeeRecipientZeroAddress","type":"error"},{"inputs":[],"name":"LSDai__InterestFeeLow","type":"error"},{"inputs":[],"name":"LSDai__MintToZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"preRebaseTotalPooledDai","type":"uint256"},{"internalType":"uint256","name":"postRebaseTotalPooledDai","type":"uint256"}],"name":"LSDai__RebaseOverflow","type":"error"},{"inputs":[],"name":"LSDai__SharesAmountExceedsBalance","type":"error"},{"inputs":[],"name":"LSDai__TransferFromZeroAddress","type":"error"},{"inputs":[],"name":"LSDai__TransferToLSDaiContract","type":"error"},{"inputs":[],"name":"LSDai__TransferToZeroAddress","type":"error"},{"inputs":[],"name":"LSDai__WithdrawalFeeLow","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":"uint256","name":"depositCap","type":"uint256"}],"name":"DepositCapSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"FeeRecipientSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"interestFee","type":"uint256"}],"name":"InterestFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"preRebaseTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"postRebaseTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesAmount","type":"uint256"}],"name":"SharesBurnt","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":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"sharesValue","type":"uint256"}],"name":"TransferShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawalFee","type":"uint256"}],"name":"WithdrawalFeeSet","type":"event"},{"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":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dai","outputs":[{"internalType":"contract IDai","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"daiJoin","outputs":[{"internalType":"contract IJoin","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"daiAmount","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"daiAmount","type":"uint256"},{"internalType":"uint256","name":"permitNonce","type":"uint256"},{"internalType":"uint256","name":"permitExpiry","type":"uint256"},{"internalType":"uint8","name":"permitV","type":"uint8"},{"internalType":"bytes32","name":"permitR","type":"bytes32"},{"internalType":"bytes32","name":"permitS","type":"bytes32"}],"name":"depositWithPermit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharesAmount","type":"uint256"}],"name":"getPooledDaiByShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"daiAmount","type":"uint256"}],"name":"getSharesByPooledDai","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalPotSharesValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositCap","type":"uint256"},{"internalType":"uint256","name":"_interestFee","type":"uint256"},{"internalType":"uint256","name":"_withdrawalFee","type":"uint256"},{"internalType":"address","name":"_feeRecipient","type":"address"}],"name":"initialize","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pot","outputs":[{"internalType":"contract IPot","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"potShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cap","type":"uint256"}],"name":"setDepositCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setInterestFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setWithdrawalFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"sharesOf","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":"totalShares","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":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_sharesAmount","type":"uint256"}],"name":"transferShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_sharesAmount","type":"uint256"}],"name":"transferSharesFrom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vat","outputs":[{"internalType":"contract IVat","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"daiAmount","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

610120604052601260805260fa600a556001600b557335d1b3f3d7966a1dfe207aa4514c12a259a0492b60a05273197e90f9fad81970ba7976f33cbd77088e5d7cf760c052739759a6ac90977b93b58547b4a71c78317f391a2860e052736b175474e89094c44da98b954eedeac495271d0f61010052348015610080575f80fd5b5060805160a05160c05160e051610100516122e961015f5f395f818161051d0152818161085c0152818161094b015281816109cb01528181610a7a015261154701525f8181610487015281816107200152818161082d015281816114cc01526116b501525f818161037c0152818161079701528181610b6801528181610bee01528181610c7f01528181610d0201528181610d8201528181610f62015281816114530152818161172801528181611aa701528181611b2e0152611b8b01525f81816102e9015281816106f301526107bf01525f6102c201526122e95ff3fe608060405234801561000f575f80fd5b5060043610610229575f3560e01c8063866512031161012a578063af14052c116100b4578063dd62ed3e11610079578063dd62ed3e146104ba578063e74b981b146104f2578063f2fde38b14610505578063f4b9fa7514610518578063f5eb42dc1461053f575f80fd5b8063af14052c14610467578063b8e107461461046f578063c11645bc14610482578063c8796572146104a9578063dbd5edc7146104b1575f80fd5b806395d89b41116100fa57806395d89b411461042857806398d704a214610430578063a75df49814610438578063a9059cbb14610441578063ac1e502514610454575f80fd5b806386651203146103e95780638bc7e8c4146103fc5780638da5cb5b146104055780638fcb4e5b14610415575f80fd5b80633d935d9e116101b65780635b56d6f51161017b5780635b56d6f51461039e57806367e70811146103b35780636d780459146103bb57806370a08231146103ce578063715018a6146103e1575f80fd5b80633d935d9e1461032b578063469048401461033e57806347e7ef241461035157806349986feb146103645780634ba2363a14610377575f80fd5b806323b872dd116101fc57806323b872dd146102975780632e1a7d4d146102aa578063313ce567146102bd57806336569e77146102e45780633a98ef3914610323575f80fd5b806306fdde031461022d578063095ea7b31461024b57806318160ddd1461026e5780631f8d519d14610284575b5f80fd5b610235610567565b6040516102429190611eed565b60405180910390f35b61025e610259366004611f53565b6105f3565b6040519015158152602001610242565b61027661060c565b604051908152602001610242565b61025e610292366004611f7b565b61061b565b61025e6102a5366004611fb7565b6108d4565b61025e6102b8366004611ff0565b6108f5565b6102767f000000000000000000000000000000000000000000000000000000000000000081565b61030b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610242565b600754610276565b610276610339366004612007565b610902565b600c5461030b906001600160a01b031681565b61027661035f366004611f53565b610a56565b610276610372366004611ff0565b610afe565b61030b7f000000000000000000000000000000000000000000000000000000000000000081565b6103b16103ac366004611ff0565b610b20565b005b610276610b64565b6102766103c9366004611fb7565b610e4f565b6102766103dc36600461206d565b610e86565b6103b1610ea7565b6103b16103f7366004611ff0565b610eba565b610276600b5481565b5f546001600160a01b031661030b565b610276610423366004611f53565b610f1a565b610235610f3e565b610276610f4b565b610276600a5481565b61025e61044f366004611f53565b610fd3565b6103b1610462366004611ff0565b610fe8565b6103b1611025565b61027661047d366004611ff0565b61103e565b61030b7f000000000000000000000000000000000000000000000000000000000000000081565b61025e611067565b61027660055481565b6102766104c8366004612086565b6001600160a01b039182165f90815260036020908152604080832093909416825291909152205490565b6103b161050036600461206d565b61108f565b6103b161051336600461206d565b611107565b61030b7f000000000000000000000000000000000000000000000000000000000000000081565b61027661054d36600461206d565b6001600160a01b03165f9081526006602052604090205490565b60018054610574906120b7565b80601f01602080910402602001604051908101604052809291908181526020018280546105a0906120b7565b80156105eb5780601f106105c2576101008083540402835291602001916105eb565b820191905f5260205f20905b8154815290600101906020018083116105ce57829003601f168201915b505050505081565b5f33610600818585611182565b60019150505b92915050565b5f61061660085490565b905090565b6004545f9060ff16156106415760405163649b1ae560e11b815260040160405180910390fd5b61064a336112a5565b6040805180820190915260128152714c697175696420536176696e67732044414960701b60208201526001906106809082612151565b506040805180820190915260058152644c5344414960d81b60208201526002906106aa9082612151565b506106b485610eba565b6106bd8261108f565b6106c683610fe8565b6106cf84610b20565b6004805460ff191660011781556040516328ec8bf160e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163a3b22fc491610755917f000000000000000000000000000000000000000000000000000000000000000091016001600160a01b0391909116815260200190565b5f604051808303815f87803b15801561076c575f80fd5b505af115801561077e573d5f803e3d5ffd5b50506040516328ec8bf160e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f000000000000000000000000000000000000000000000000000000000000000016925063a3b22fc491506024015f604051808303815f87803b158015610802575f80fd5b505af1158015610814573d5f803e3d5ffd5b505060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f1960248301527f000000000000000000000000000000000000000000000000000000000000000016925063095ea7b391506044016020604051808303815f875af11580156108a4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108c8919061220d565b50600195945050505050565b5f6108e08433846112f4565b6108eb848484611384565b5060019392505050565b5f61060682600b546113a7565b6040516323f2ebc360e21b815233600482015230602482015260448101869052606481018590526001608482015260ff841660a482015260c4810183905260e481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690638fcbaf0c90610104015f604051808303815f87803b158015610995575f80fd5b505af11580156109a7573d5f803e3d5ffd5b50506040516323b872dd60e01b8152336004820152306024820152604481018a90527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692506323b872dd91506064016020604051808303815f875af1158015610a1b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a3f919061220d565b50610a4a88886115c8565b98975050505050505050565b6040516323b872dd60e01b8152336004820152306024820152604481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303815f875af1158015610ac8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aec919061220d565b50610af783836115c8565b9392505050565b5f610606600754610b1a6008548561179190919063ffffffff16565b9061179c565b610b286117a7565b600a8190556040518181527fa17080f00be20ecb963811801c804b46ccabe1339f9fbd76e2c858be73dcb189906020015b60405180910390a150565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bc2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610be6919061222c565b4211610c71577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c48573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c6c919061222c565b610e23565b676765c793fa10079d601b1b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cd9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cfd919061222c565b610e0f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663487bf0826040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d80919061222c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ddc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e00919061222c565b610e0a9042612257565b61180f565b610e19919061226a565b610e239190612281565b9050676765c793fa10079d601b1b81600954610e3f919061226a565b610e499190612281565b91505090565b5f80610e5a83610afe565b9050610e678533836112f4565b610e728585856118fb565b610e7e85858386611a04565b949350505050565b6001600160a01b0381165f9081526006602052604081205461060690610afe565b610eaf6117a7565b610eb85f6112a5565b565b610ec26117a7565b600854811015610ee557604051631559074d60e21b815260040160405180910390fd5b60058190556040518181527f50e5341d7a4ad030a1a03c7b2bccfa67438c0bdf5c398a3b1d7a64babfbf97fe90602001610b59565b5f610f263384846118fb565b5f610f3083610afe565b9050610af733858386611a04565b60028054610574906120b7565b6040516305f5d64360e11b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630bebac8690602401602060405180830381865afa158015610faf573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610616919061222c565b5f610fdf338484611384565b50600192915050565b610ff06117a7565b600b8190556040518181527f48dcfdaa10944928da945a9017941a9e4118df541fb7d429104f5372e9eb994f90602001610b59565b5f61102e611aa4565b905061103b816001611be5565b50565b5f6008545f0361104c575090565b610606600854610b1a6007548561179190919063ffffffff16565b5f6110706117a7565b600c5461061690611089906001600160a01b0316610e86565b5f6113a7565b6110976117a7565b6001600160a01b0381166110be576040516378dca40760e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383169081179091556040517fbf9a9534339a9d6b81696e05dcfb614b7dc518a31d48be3cfb757988381fb323905f90a250565b61110f6117a7565b6001600160a01b0381166111795760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61103b816112a5565b6001600160a01b0383166111e45760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401611170565b6001600160a01b0382166112455760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401611170565b6001600160a01b038381165f8181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038381165f908152600360209081526040808320938616835292905220545f19811461137e57818110156113715760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401611170565b61137e8484848403611182565b50505050565b5f61138e8261103e565b905061139b8484836118fb565b61137e84848484611a04565b5f806113b1611aa4565b90505f6113c4612710610b1a8787611791565b90505f6113d18683611cb5565b90505f6113dd8361103e565b90505f6113e98361103e565b6008549091506113f99084611cb5565b600855600c546114149033906001600160a01b0316846118fb565b61141e3382611cc0565b505f61142a8487611dc8565b60095490915061143a9082611cb5565b600955604051637f8661a160e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690637f8661a1906024015f604051808303815f87803b15801561149c575f80fd5b505af11580156114ae573d5f803e3d5ffd5b505060405163ef693bed60e01b8152306004820152602481018790527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316925063ef693bed91506044015f604051808303815f87803b158015611517575f80fd5b505af1158015611529573d5f803e3d5ffd5b505060405163a9059cbb60e01b8152336004820152602481018790527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316925063a9059cbb91506044016020604051808303815f875af1158015611597573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115bb919061220d565b9998505050505050505050565b5f806005541180156115ed57506005546115eb836115e560085490565b90611dfc565b115b1561160b576040516378767b3960e01b815260040160405180910390fd5b5f611614611aa4565b90505f6116218483611e07565b905061162c8461103e565b92506116388584611e1e565b506008546116469085611dfc565b6008556009546116569082611dfc565b6009556040518481526001600160a01b038616905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3604051633b4da69f60e01b8152306004820152602481018590527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690633b4da69f906044015f604051808303815f87803b1580156116fe575f80fd5b505af1158015611710573d5f803e3d5ffd5b505060405163049878f360e01b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316925063049878f391506024015f604051808303815f87803b158015611773575f80fd5b505af1158015611785573d5f803e3d5ffd5b50505050505092915050565b5f610af7828461226a565b5f610af78284612281565b336117b95f546001600160a01b031690565b6001600160a01b031614610eb85760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401611170565b5f8280156118d45760018316801561182957849250611838565b676765c793fa10079d601b1b92505b506002909204916b019d971e4fe8401e740000005b83156118ce578485028586820414611863575f80fd5b81810181811015611872575f80fd5b676765c793fa10079d601b1b900495505060018416156118c35784830283868204141586151516156118a2575f80fd5b818101818110156118b1575f80fd5b676765c793fa10079d601b1b90049350505b60028404935061184d565b506118f4565b8280156118e3575f92506118f2565b676765c793fa10079d601b1b92505b505b5092915050565b6001600160a01b0383166119225760405163a0cf5f9160e01b815260040160405180910390fd5b6001600160a01b03821661194957604051631a61f75f60e21b815260040160405180910390fd5b306001600160a01b0383160361197257604051632217649360e01b815260040160405180910390fd5b6001600160a01b0383165f90815260066020526040902054808211156119ab5760405163014c20ef60e41b815260040160405180910390fd5b6119b58183611cb5565b6001600160a01b038086165f9081526006602052604080822093909355908516815220546119e39083611dfc565b6001600160a01b039093165f90815260066020526040902092909255505050565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611a4991815260200190565b60405180910390a3826001600160a01b0316846001600160a01b03167f9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb83604051611a9691815260200190565b60405180910390a350505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b01573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b25919061222c565b421115611b89577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639f678cca6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610faf573d5f803e3d5ffd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610faf573d5f803e3d5ffd5b6008546009545f90676765c793fa10079d601b1b90611c0590869061226a565b611c0f9190612281565b90505f80611c2860085484611e9d90919063ffffffff16565b909250905081158015611c3d57506001851515145b15611c6557604051631847a98b60e21b81526004810185905260248101849052604401611170565b8115155f03611c7657505050505050565b60088390555f611c8582611ec1565b90505f611c918261103e565b600c54909150611caa906001600160a01b031682611e1e565b505050505050505050565b5f610af78284612257565b5f6001600160a01b038316611ce8576040516360041ca360e11b815260040160405180910390fd5b6001600160a01b0383165f9081526006602052604090205480831115611d215760405163014c20ef60e41b815260040160405180910390fd5b5f611d2b84610afe565b600754909150611d3b9085611cb5565b60078190559250611d4c8285611cb5565b6001600160a01b0386165f90815260066020526040812091909155611d7085610afe565b60408051848152602081018390529081018790529091506001600160a01b038716907f8b2a1e1ad5e0578c3dd82494156e985dade827a87c573b5c1c7716a32162ad649060600160405180910390a250505092915050565b5f81611df2611de285676765c793fa10079d601b1b611791565b611ded856001611cb5565b611dfc565b610af79190612281565b5f610af782846122a0565b5f81611df284676765c793fa10079d601b1b611791565b5f6001600160a01b038316611e4657604051631a61f75f60e21b815260040160405180910390fd5b600754611e539083611dfc565b60078190556001600160a01b0384165f90815260066020526040902054909150611e7d9083611dfc565b6001600160a01b039093165f908152600660205260409020929092555090565b5f8083831115611eb157505f905080611eba565b50600190508183035b9250929050565b5f600a545f03611ed257505f919050565b610606612710610b1a600a548561179190919063ffffffff16565b5f6020808352835180828501525f5b81811015611f1857858101830151858201604001528201611efc565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114611f4e575f80fd5b919050565b5f8060408385031215611f64575f80fd5b611f6d83611f38565b946020939093013593505050565b5f805f8060808587031215611f8e575f80fd5b843593506020850135925060408501359150611fac60608601611f38565b905092959194509250565b5f805f60608486031215611fc9575f80fd5b611fd284611f38565b9250611fe060208501611f38565b9150604084013590509250925092565b5f60208284031215612000575f80fd5b5035919050565b5f805f805f805f60e0888a03121561201d575f80fd5b61202688611f38565b9650602088013595506040880135945060608801359350608088013560ff81168114612050575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b5f6020828403121561207d575f80fd5b610af782611f38565b5f8060408385031215612097575f80fd5b6120a083611f38565b91506120ae60208401611f38565b90509250929050565b600181811c908216806120cb57607f821691505b6020821081036120e957634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52604160045260245ffd5b601f82111561214c575f81815260208120601f850160051c810160208610156121295750805b601f850160051c820191505b8181101561214857828155600101612135565b5050505b505050565b815167ffffffffffffffff81111561216b5761216b6120ef565b61217f8161217984546120b7565b84612103565b602080601f8311600181146121b2575f841561219b5750858301515b5f19600386901b1c1916600185901b178555612148565b5f85815260208120601f198616915b828110156121e0578886015182559484019460019091019084016121c1565b50858210156121fd57878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f6020828403121561221d575f80fd5b81518015158114610af7575f80fd5b5f6020828403121561223c575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561060657610606612243565b808202811582820484141761060657610606612243565b5f8261229b57634e487b7160e01b5f52601260045260245ffd5b500490565b808201808211156106065761060661224356fea2646970667358221220de5dd9bb406381b6bc53b945eb6b702aa2e07ec055588a7129523f079bf4f33d64736f6c63430008140033

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610229575f3560e01c8063866512031161012a578063af14052c116100b4578063dd62ed3e11610079578063dd62ed3e146104ba578063e74b981b146104f2578063f2fde38b14610505578063f4b9fa7514610518578063f5eb42dc1461053f575f80fd5b8063af14052c14610467578063b8e107461461046f578063c11645bc14610482578063c8796572146104a9578063dbd5edc7146104b1575f80fd5b806395d89b41116100fa57806395d89b411461042857806398d704a214610430578063a75df49814610438578063a9059cbb14610441578063ac1e502514610454575f80fd5b806386651203146103e95780638bc7e8c4146103fc5780638da5cb5b146104055780638fcb4e5b14610415575f80fd5b80633d935d9e116101b65780635b56d6f51161017b5780635b56d6f51461039e57806367e70811146103b35780636d780459146103bb57806370a08231146103ce578063715018a6146103e1575f80fd5b80633d935d9e1461032b578063469048401461033e57806347e7ef241461035157806349986feb146103645780634ba2363a14610377575f80fd5b806323b872dd116101fc57806323b872dd146102975780632e1a7d4d146102aa578063313ce567146102bd57806336569e77146102e45780633a98ef3914610323575f80fd5b806306fdde031461022d578063095ea7b31461024b57806318160ddd1461026e5780631f8d519d14610284575b5f80fd5b610235610567565b6040516102429190611eed565b60405180910390f35b61025e610259366004611f53565b6105f3565b6040519015158152602001610242565b61027661060c565b604051908152602001610242565b61025e610292366004611f7b565b61061b565b61025e6102a5366004611fb7565b6108d4565b61025e6102b8366004611ff0565b6108f5565b6102767f000000000000000000000000000000000000000000000000000000000000001281565b61030b7f00000000000000000000000035d1b3f3d7966a1dfe207aa4514c12a259a0492b81565b6040516001600160a01b039091168152602001610242565b600754610276565b610276610339366004612007565b610902565b600c5461030b906001600160a01b031681565b61027661035f366004611f53565b610a56565b610276610372366004611ff0565b610afe565b61030b7f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf781565b6103b16103ac366004611ff0565b610b20565b005b610276610b64565b6102766103c9366004611fb7565b610e4f565b6102766103dc36600461206d565b610e86565b6103b1610ea7565b6103b16103f7366004611ff0565b610eba565b610276600b5481565b5f546001600160a01b031661030b565b610276610423366004611f53565b610f1a565b610235610f3e565b610276610f4b565b610276600a5481565b61025e61044f366004611f53565b610fd3565b6103b1610462366004611ff0565b610fe8565b6103b1611025565b61027661047d366004611ff0565b61103e565b61030b7f0000000000000000000000009759a6ac90977b93b58547b4a71c78317f391a2881565b61025e611067565b61027660055481565b6102766104c8366004612086565b6001600160a01b039182165f90815260036020908152604080832093909416825291909152205490565b6103b161050036600461206d565b61108f565b6103b161051336600461206d565b611107565b61030b7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f81565b61027661054d36600461206d565b6001600160a01b03165f9081526006602052604090205490565b60018054610574906120b7565b80601f01602080910402602001604051908101604052809291908181526020018280546105a0906120b7565b80156105eb5780601f106105c2576101008083540402835291602001916105eb565b820191905f5260205f20905b8154815290600101906020018083116105ce57829003601f168201915b505050505081565b5f33610600818585611182565b60019150505b92915050565b5f61061660085490565b905090565b6004545f9060ff16156106415760405163649b1ae560e11b815260040160405180910390fd5b61064a336112a5565b6040805180820190915260128152714c697175696420536176696e67732044414960701b60208201526001906106809082612151565b506040805180820190915260058152644c5344414960d81b60208201526002906106aa9082612151565b506106b485610eba565b6106bd8261108f565b6106c683610fe8565b6106cf84610b20565b6004805460ff191660011781556040516328ec8bf160e21b81526001600160a01b037f00000000000000000000000035d1b3f3d7966a1dfe207aa4514c12a259a0492b169163a3b22fc491610755917f0000000000000000000000009759a6ac90977b93b58547b4a71c78317f391a2891016001600160a01b0391909116815260200190565b5f604051808303815f87803b15801561076c575f80fd5b505af115801561077e573d5f803e3d5ffd5b50506040516328ec8bf160e21b81526001600160a01b037f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf7811660048301527f00000000000000000000000035d1b3f3d7966a1dfe207aa4514c12a259a0492b16925063a3b22fc491506024015f604051808303815f87803b158015610802575f80fd5b505af1158015610814573d5f803e3d5ffd5b505060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000009759a6ac90977b93b58547b4a71c78317f391a28811660048301525f1960248301527f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16925063095ea7b391506044016020604051808303815f875af11580156108a4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108c8919061220d565b50600195945050505050565b5f6108e08433846112f4565b6108eb848484611384565b5060019392505050565b5f61060682600b546113a7565b6040516323f2ebc360e21b815233600482015230602482015260448101869052606481018590526001608482015260ff841660a482015260c4810183905260e481018290525f907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031690638fcbaf0c90610104015f604051808303815f87803b158015610995575f80fd5b505af11580156109a7573d5f803e3d5ffd5b50506040516323b872dd60e01b8152336004820152306024820152604481018a90527f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031692506323b872dd91506064016020604051808303815f875af1158015610a1b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a3f919061220d565b50610a4a88886115c8565b98975050505050505050565b6040516323b872dd60e01b8152336004820152306024820152604481018290525f907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316906323b872dd906064016020604051808303815f875af1158015610ac8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aec919061220d565b50610af783836115c8565b9392505050565b5f610606600754610b1a6008548561179190919063ffffffff16565b9061179c565b610b286117a7565b600a8190556040518181527fa17080f00be20ecb963811801c804b46ccabe1339f9fbd76e2c858be73dcb189906020015b60405180910390a150565b5f807f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bc2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610be6919061222c565b4211610c71577f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c48573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c6c919061222c565b610e23565b676765c793fa10079d601b1b7f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cd9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cfd919061222c565b610e0f7f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b031663487bf0826040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d80919061222c565b7f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ddc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e00919061222c565b610e0a9042612257565b61180f565b610e19919061226a565b610e239190612281565b9050676765c793fa10079d601b1b81600954610e3f919061226a565b610e499190612281565b91505090565b5f80610e5a83610afe565b9050610e678533836112f4565b610e728585856118fb565b610e7e85858386611a04565b949350505050565b6001600160a01b0381165f9081526006602052604081205461060690610afe565b610eaf6117a7565b610eb85f6112a5565b565b610ec26117a7565b600854811015610ee557604051631559074d60e21b815260040160405180910390fd5b60058190556040518181527f50e5341d7a4ad030a1a03c7b2bccfa67438c0bdf5c398a3b1d7a64babfbf97fe90602001610b59565b5f610f263384846118fb565b5f610f3083610afe565b9050610af733858386611a04565b60028054610574906120b7565b6040516305f5d64360e11b81523060048201525f907f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b031690630bebac8690602401602060405180830381865afa158015610faf573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610616919061222c565b5f610fdf338484611384565b50600192915050565b610ff06117a7565b600b8190556040518181527f48dcfdaa10944928da945a9017941a9e4118df541fb7d429104f5372e9eb994f90602001610b59565b5f61102e611aa4565b905061103b816001611be5565b50565b5f6008545f0361104c575090565b610606600854610b1a6007548561179190919063ffffffff16565b5f6110706117a7565b600c5461061690611089906001600160a01b0316610e86565b5f6113a7565b6110976117a7565b6001600160a01b0381166110be576040516378dca40760e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383169081179091556040517fbf9a9534339a9d6b81696e05dcfb614b7dc518a31d48be3cfb757988381fb323905f90a250565b61110f6117a7565b6001600160a01b0381166111795760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61103b816112a5565b6001600160a01b0383166111e45760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401611170565b6001600160a01b0382166112455760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401611170565b6001600160a01b038381165f8181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038381165f908152600360209081526040808320938616835292905220545f19811461137e57818110156113715760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401611170565b61137e8484848403611182565b50505050565b5f61138e8261103e565b905061139b8484836118fb565b61137e84848484611a04565b5f806113b1611aa4565b90505f6113c4612710610b1a8787611791565b90505f6113d18683611cb5565b90505f6113dd8361103e565b90505f6113e98361103e565b6008549091506113f99084611cb5565b600855600c546114149033906001600160a01b0316846118fb565b61141e3382611cc0565b505f61142a8487611dc8565b60095490915061143a9082611cb5565b600955604051637f8661a160e01b8152600481018290527f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b031690637f8661a1906024015f604051808303815f87803b15801561149c575f80fd5b505af11580156114ae573d5f803e3d5ffd5b505060405163ef693bed60e01b8152306004820152602481018790527f0000000000000000000000009759a6ac90977b93b58547b4a71c78317f391a286001600160a01b0316925063ef693bed91506044015f604051808303815f87803b158015611517575f80fd5b505af1158015611529573d5f803e3d5ffd5b505060405163a9059cbb60e01b8152336004820152602481018790527f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316925063a9059cbb91506044016020604051808303815f875af1158015611597573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115bb919061220d565b9998505050505050505050565b5f806005541180156115ed57506005546115eb836115e560085490565b90611dfc565b115b1561160b576040516378767b3960e01b815260040160405180910390fd5b5f611614611aa4565b90505f6116218483611e07565b905061162c8461103e565b92506116388584611e1e565b506008546116469085611dfc565b6008556009546116569082611dfc565b6009556040518481526001600160a01b038616905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3604051633b4da69f60e01b8152306004820152602481018590527f0000000000000000000000009759a6ac90977b93b58547b4a71c78317f391a286001600160a01b031690633b4da69f906044015f604051808303815f87803b1580156116fe575f80fd5b505af1158015611710573d5f803e3d5ffd5b505060405163049878f360e01b8152600481018490527f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b0316925063049878f391506024015f604051808303815f87803b158015611773575f80fd5b505af1158015611785573d5f803e3d5ffd5b50505050505092915050565b5f610af7828461226a565b5f610af78284612281565b336117b95f546001600160a01b031690565b6001600160a01b031614610eb85760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401611170565b5f8280156118d45760018316801561182957849250611838565b676765c793fa10079d601b1b92505b506002909204916b019d971e4fe8401e740000005b83156118ce578485028586820414611863575f80fd5b81810181811015611872575f80fd5b676765c793fa10079d601b1b900495505060018416156118c35784830283868204141586151516156118a2575f80fd5b818101818110156118b1575f80fd5b676765c793fa10079d601b1b90049350505b60028404935061184d565b506118f4565b8280156118e3575f92506118f2565b676765c793fa10079d601b1b92505b505b5092915050565b6001600160a01b0383166119225760405163a0cf5f9160e01b815260040160405180910390fd5b6001600160a01b03821661194957604051631a61f75f60e21b815260040160405180910390fd5b306001600160a01b0383160361197257604051632217649360e01b815260040160405180910390fd5b6001600160a01b0383165f90815260066020526040902054808211156119ab5760405163014c20ef60e41b815260040160405180910390fd5b6119b58183611cb5565b6001600160a01b038086165f9081526006602052604080822093909355908516815220546119e39083611dfc565b6001600160a01b039093165f90815260066020526040902092909255505050565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611a4991815260200190565b60405180910390a3826001600160a01b0316846001600160a01b03167f9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb83604051611a9691815260200190565b60405180910390a350505050565b5f7f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b01573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b25919061222c565b421115611b89577f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b0316639f678cca6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610faf573d5f803e3d5ffd5b7f000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf76001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610faf573d5f803e3d5ffd5b6008546009545f90676765c793fa10079d601b1b90611c0590869061226a565b611c0f9190612281565b90505f80611c2860085484611e9d90919063ffffffff16565b909250905081158015611c3d57506001851515145b15611c6557604051631847a98b60e21b81526004810185905260248101849052604401611170565b8115155f03611c7657505050505050565b60088390555f611c8582611ec1565b90505f611c918261103e565b600c54909150611caa906001600160a01b031682611e1e565b505050505050505050565b5f610af78284612257565b5f6001600160a01b038316611ce8576040516360041ca360e11b815260040160405180910390fd5b6001600160a01b0383165f9081526006602052604090205480831115611d215760405163014c20ef60e41b815260040160405180910390fd5b5f611d2b84610afe565b600754909150611d3b9085611cb5565b60078190559250611d4c8285611cb5565b6001600160a01b0386165f90815260066020526040812091909155611d7085610afe565b60408051848152602081018390529081018790529091506001600160a01b038716907f8b2a1e1ad5e0578c3dd82494156e985dade827a87c573b5c1c7716a32162ad649060600160405180910390a250505092915050565b5f81611df2611de285676765c793fa10079d601b1b611791565b611ded856001611cb5565b611dfc565b610af79190612281565b5f610af782846122a0565b5f81611df284676765c793fa10079d601b1b611791565b5f6001600160a01b038316611e4657604051631a61f75f60e21b815260040160405180910390fd5b600754611e539083611dfc565b60078190556001600160a01b0384165f90815260066020526040902054909150611e7d9083611dfc565b6001600160a01b039093165f908152600660205260409020929092555090565b5f8083831115611eb157505f905080611eba565b50600190508183035b9250929050565b5f600a545f03611ed257505f919050565b610606612710610b1a600a548561179190919063ffffffff16565b5f6020808352835180828501525f5b81811015611f1857858101830151858201604001528201611efc565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114611f4e575f80fd5b919050565b5f8060408385031215611f64575f80fd5b611f6d83611f38565b946020939093013593505050565b5f805f8060808587031215611f8e575f80fd5b843593506020850135925060408501359150611fac60608601611f38565b905092959194509250565b5f805f60608486031215611fc9575f80fd5b611fd284611f38565b9250611fe060208501611f38565b9150604084013590509250925092565b5f60208284031215612000575f80fd5b5035919050565b5f805f805f805f60e0888a03121561201d575f80fd5b61202688611f38565b9650602088013595506040880135945060608801359350608088013560ff81168114612050575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b5f6020828403121561207d575f80fd5b610af782611f38565b5f8060408385031215612097575f80fd5b6120a083611f38565b91506120ae60208401611f38565b90509250929050565b600181811c908216806120cb57607f821691505b6020821081036120e957634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52604160045260245ffd5b601f82111561214c575f81815260208120601f850160051c810160208610156121295750805b601f850160051c820191505b8181101561214857828155600101612135565b5050505b505050565b815167ffffffffffffffff81111561216b5761216b6120ef565b61217f8161217984546120b7565b84612103565b602080601f8311600181146121b2575f841561219b5750858301515b5f19600386901b1c1916600185901b178555612148565b5f85815260208120601f198616915b828110156121e0578886015182559484019460019091019084016121c1565b50858210156121fd57878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f6020828403121561221d575f80fd5b81518015158114610af7575f80fd5b5f6020828403121561223c575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561060657610606612243565b808202811582820484141761060657610606612243565b5f8261229b57634e487b7160e01b5f52601260045260245ffd5b500490565b808201808211156106065761060661224356fea2646970667358221220de5dd9bb406381b6bc53b945eb6b702aa2e07ec055588a7129523f079bf4f33d64736f6c63430008140033

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  ]

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.