ETH Price: $2,091.85 (+0.88%)

Transaction Decoder

Block:
13533326 at Nov-01-2021 08:34:19 PM +UTC
Transaction Fee:
0.0138736694645055 ETH $29.02
Gas Used:
57,150 Gas / 242.75887077 Gwei

Emitted Events:

490 DFPgov.Transfer( from=[Sender] 0xacbd78844a4a51b32551c14b3263625f45fd270d, to=[Receiver] DeFiPlaza, value=7122416084172870755758 )
491 DFPgov.Approval( owner=[Sender] 0xacbd78844a4a51b32551c14b3263625f45fd270d, spender=[Receiver] DeFiPlaza, value=115792089237316195423570985008687907853269984665640564027335167923740258884177 )
492 DeFiPlaza.Swapped( sender=[Sender] 0xacbd78844a4a51b32551c14b3263625f45fd270d, inputToken=DFPgov, outputToken=0x00000000...000000000, inputAmount=7122416084172870755758, outputAmount=564466735325461554 )

Account State Difference:

  Address   Before After State Difference Code
0x2F57430a...4a71b8E6D
0xacbd7884...F45Fd270d
0.063326663087991867 Eth
Nonce: 315
0.613919728948947921 Eth
Nonce: 316
0.550593065860956054
0xE68c1d72...bc7514110 51.946937010439018219 Eth51.382470275113556665 Eth0.564466735325461554
(Ethermine)
3,575.771184609893557194 Eth3,575.771270334893557194 Eth0.000085725

Execution Trace

DeFiPlaza.swap( inputToken=0x2F57430a6ceDA85a67121757785877b4a71b8E6D, outputToken=0x0000000000000000000000000000000000000000, inputAmount=7122416084172870755758, minOutputAmount=558822067972206938 ) => ( outputAmount=564466735325461554 )
  • DFPgov.balanceOf( account=0xE68c1d72340aEeFe5Be76eDa63AE2f4bc7514110 ) => ( 647693375923156802411963 )
  • DFPgov.transferFrom( sender=0xacbd78844a4a51B32551c14B3263625F45Fd270d, recipient=0xE68c1d72340aEeFe5Be76eDa63AE2f4bc7514110, amount=7122416084172870755758 ) => ( True )
  • ETH 0.564466735325461554 0xacbd78844a4a51b32551c14b3263625f45fd270d.CALL( )
    File 1 of 2: DeFiPlaza
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.6;
    import "../interfaces/IDeFiPlaza.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    /**
     * @title DeFi Plaza exchange controct, multi token DEX.
     * @author Jazzer 9F
     * @notice Trades between two tokens follow the local bonding curve x*y=k
     * The number of tokens used is hard coded to 16 for efficiency reasons.
     */
    contract DeFiPlaza is IDeFiPlaza, Ownable, ERC20 {
      using SafeERC20 for IERC20;
      // States that each token can be in
      enum State {Unlisted, PreListing, Delisting, Listed}
      // Configuration per token. Still some bits available if needed
      struct TokenSettings {
        State state;                      // What state the token is currently in
        uint112 listingTarget;            // Amount of tokens needed to activate listing
      }
      // Exchange configuration
      struct Config {
        bool unlocked;                    // Locked for trading to prevent re-entrancy misery
        uint64 oneMinusTradingFee;        // One minus the swap fee (0.64 fixed point integer)
        uint64 delistingBonus;            // Amount of additional tokens to encourage immediate delisting (0.64 fixed point)
      }
      // Keeps track of whether there is a listing change underway and if so between which tokens
      struct ListingUpdate {
        address tokenToDelist;            // Token to be removed
        address tokenToList;              // Token to be listed
      }
      // Mapping to keep track of the listed tokens
      mapping(address => TokenSettings) public listedTokens;
      Config public DFPconfig;
      ListingUpdate public listingUpdate;
      address public admin;
      /**
      * Sets up default configuration
      * Initialize with ordered list of 15 token addresses (ETH is always listed)
      * Doesn't do any checks. Make sure you ONLY add well behaved ERC20s!!
      */
      constructor(address[] memory tokensToList, uint256 mintAmount, string memory name_, string memory symbol_) ERC20(name_, symbol_) {
        // Basic exchange configuration
        Config memory config;
        config.unlocked = false;
        config.oneMinusTradingFee = 0xffbe76c8b4395800;   // approximately 0.999
        config.delistingBonus = 0;
        DFPconfig = config;
        // Configure the listed tokens as such
        TokenSettings memory listed;
        listed.state = State.Listed;
        require(tokensToList.length == 15, "Incorrect number of tokens");
        address previous = address(0);
        address current = address(0);
        for (uint256 i = 0; i < 15; i++) {
          current = tokensToList[i];
          require(current > previous, "Require ordered list");
          listedTokens[current] = listed;
          previous = current;
        }
        // Generate the LP tokens reflecting the initial liquidity (to be loaded externally)
        _mint(msg.sender, mintAmount);
      }
      // For bootstrapping ETH liquidity
      receive() external payable {}
      // To safeguard some functionality is only applied to listed tokens
      modifier onlyListedToken(address token) {
        require(
          token == address(0) || listedTokens[token].state > State.Delisting,
          "DFP: Token not listed"
        );
        _;
      }
      modifier onlyAdmin() {
        require(
          msg.sender == admin || msg.sender == owner(),
          "DFP: admin rights required"
        );
        _;
      }
      /**
      * Allows users to swap between any two tokens listed on the DEX.
      * Follows the x*y=k swap invariant hyperbole
      * For ETH trades, send the ETH with the transaction and use the NULL address as inputToken.
      */
      function swap(
        address inputToken,
        address outputToken,
        uint256 inputAmount,
        uint256 minOutputAmount
      )
        external
        payable
        onlyListedToken(inputToken)
        onlyListedToken(outputToken)
        override
        returns (uint256 outputAmount)
      {
        // Check that the exchange is unlocked and thus open for business
        Config memory _config = DFPconfig;
        require(_config.unlocked, "DFP: Locked");
        // Pull in input token and check the exchange balance for that token
        uint256 initialInputBalance;
        if (inputToken == address(0)) {
          require(msg.value == inputAmount, "DFP: bad ETH amount");
          initialInputBalance = address(this).balance - inputAmount;
        } else {
          initialInputBalance = IERC20(inputToken).balanceOf(address(this));
          IERC20(inputToken).safeTransferFrom(msg.sender, address(this), inputAmount);
        }
        // Check dex balance of the output token
        uint256 initialOutputBalance;
        if (outputToken == address(0)) {
          initialOutputBalance = address(this).balance;
        } else {
          initialOutputBalance = IERC20(outputToken).balanceOf(address(this));
        }
        // Calculate the output amount through the x*y=k invariant
        // Can skip overflow/underflow checks on this calculation as they will always work against an attacker anyway.
        uint256 netInputAmount = inputAmount * _config.oneMinusTradingFee;
        outputAmount = netInputAmount * initialOutputBalance / ((initialInputBalance << 64) + netInputAmount);
        require(outputAmount > minOutputAmount, "DFP: No deal");
        // Send output tokens to whoever invoked the swap function
        if (outputToken == address(0)) {
          address payable sender = payable(msg.sender);
          sender.transfer(outputAmount);
        } else {
          IERC20(outputToken).safeTransfer(msg.sender, outputAmount);
        }
        // Emit swap event to enable better governance decision making
        emit Swapped(msg.sender, inputToken, outputToken, inputAmount, outputAmount);
      }
      /**
      * Single sided liquidity add. More economic at low/moderate liquidity amounts.
      * Mathematically works as adding all tokens and swapping back to 1 token at no fee.
      *
      *         R = (1 + X_supplied/X_initial)^(1/N) - 1
      *         LP_minted = R * LP_total
      *
      * When adding ETH, the inputToken address to be used is the NULL address.
      * A fee is applied to prevent zero fee swapping through liquidity add/remove.
      *
      * Note that this method suffers from two forms of slippage.
      *   1. Slippage from single sided add which is modeled with 15 internal swaps
      *   2. Slippage from the numerical approximation required for calculation.
      *
      * When adding a large amount of liquidity when compared with the existing
      * liquidity for the selected token, the slippage can become quite significant.
      * The smart contract limits the maximum input amount at 100% of the existing
      * liquidity, at which point the slippage is 29.2% (due to 1) + 9.3% (due to 2)
      */
      function addLiquidity(address inputToken, uint256 inputAmount, uint256 minLP)
        external
        payable
        onlyListedToken(inputToken)
        override
        returns (uint256 actualLP)
      {
        // Check that the exchange is unlocked and thus open for business
        Config memory _config = DFPconfig;
        require(_config.unlocked, "DFP: Locked");
        // Pull in input token and check the exchange balance for that token
        uint256 initialBalance;
        if (inputToken == address(0)) {
          require(msg.value == inputAmount, "DFP: Incorrect amount of ETH");
          initialBalance = address(this).balance - inputAmount;
        } else {
          initialBalance = IERC20(inputToken).balanceOf(address(this));
          IERC20(inputToken).safeTransferFrom(msg.sender, address(this), inputAmount);
        }
        // Prevent excessive liquidity add which runs of the approximation curve
        require(inputAmount < initialBalance, "DFP: Too much at once");
        // See https://en.wikipedia.org/wiki/Binomial_approximation for the below
        // Compute the 6th power binomial series approximation of R.
        //
        //                   X   15 X^2   155 X^3   7285 X^4   91791 X^5   2417163 X^6
        // (1+X)^1/16 - 1 ≈ -- - ------ + ------- - -------- + --------- - -----------
        //                  16    512      8192      524288     8388608     268435456
        //
        // Note that we need to terminate at an even order to guarantee an underestimate
        // for safety. The underestimation leads to slippage for higher amounts, but
        // protects funds of those that are already invested.
        uint256 X = (inputAmount * _config.oneMinusTradingFee) / initialBalance;  // 0.64 bits
        uint256 X_ = X * X;                                // X^2   0.128 bits
        uint256 R_ = (X >> 4) - (X_ * 15 >> 73);           // R2    0.64 bits
        X_ = X_ * X;                                       // X^3   0.192 bits
        R_ = R_ + (X_ * 155 >> 141);                       // R3    0.64 bits
        X_ = X_ * X >> 192;                                // X^4   0.64 bits
        R_ = R_ - (X_ * 7285 >> 19);                       // R4    0.64 bits
        X_ = X_ * X;                                       // X^5   0.128 bits
        R_ = R_ + (X_ * 91791 >> 87);                      // R5    0.64 bits
        X_ = X_ * X;                                       // X^6   0.192 bits
        R_ = R_ - (X_ * 2417163 >> 156);                   // R6    0.64 bits
        // Calculate and mint LPs to be awarded
        actualLP = R_ * totalSupply() >> 64;
        require(actualLP > minLP, "DFP: No deal");
        _mint(msg.sender, actualLP);
        // Emitting liquidity add event to enable better governance decisions
        emit LiquidityAdded(msg.sender, inputToken, inputAmount, actualLP);
      }
      /**
      * Multi-token liquidity add. More economic for large amounts of liquidity.
      * Simply takes in all 16 listed tokens in ratio and mints the LPs accordingly.
      * For ETH, the inputToken address to be used is the NULL address.
      * A fee is applied to prevent zero fee swapping through liquidity add/remove.
      */
      function addMultiple(address[] calldata tokens, uint256[] calldata maxAmounts)
        external
        payable
        override
        returns (uint256 actualLP)
      {
        // Perform basic checks
        Config memory _config = DFPconfig;
        require(_config.unlocked, "DFP: Locked");
        require(tokens.length == 16, "DFP: Bad tokens array length");
        require(maxAmounts.length == 16, "DFP: Bad maxAmount array length");
        // Check ETH amount/ratio first
        require(tokens[0] == address(0), "DFP: No ETH found");
        require(maxAmounts[0] == msg.value, "DFP: Incorrect ETH amount");
        uint256 dexBalance = address(this).balance - msg.value;
        uint256 actualRatio = msg.value * (1<<128) / dexBalance;
        // Check ERC20 amounts/ratios
        uint256 currentRatio;
        address previous;
        address token;
        for (uint256 i = 1; i < 16; i++) {
          token = tokens[i];
          require(token > previous, "DFP: Require ordered list");
          require(
            listedTokens[token].state > State.Delisting,
            "DFP: Token not listed"
          );
          dexBalance = IERC20(token).balanceOf(address(this));
          currentRatio = maxAmounts[i] * (1 << 128) / dexBalance;
          if (currentRatio < actualRatio) {
            actualRatio = currentRatio;
          }
          previous = token;
        }
        // Calculate how many LP will be generated
        actualLP = (actualRatio * totalSupply() >> 64) * DFPconfig.oneMinusTradingFee >> 128;
        // Collect ERC20 tokens
        for (uint256 i = 1; i < 16; i++) {
          token = tokens[i];
          dexBalance = IERC20(token).balanceOf(address(this));
          IERC20(token).safeTransferFrom(msg.sender, address(this), dexBalance * actualRatio >> 128);
        }
        // Mint the LP tokens
        _mint(msg.sender, actualLP);
        emit MultiLiquidityAdded(msg.sender, actualLP, totalSupply());
        // Refund ETH change
        dexBalance = address(this).balance - msg.value;
        address payable sender = payable(msg.sender);
        sender.transfer(msg.value - (dexBalance * actualRatio >> 128));
      }
      /**
      * Single sided liquidity withdrawal. More efficient at lower liquidity amounts.
      * Mathematically withdraws 16 tokens in ratio and then swaps 15 back in at no fees.
      * Calculates the following:
      *
      *        R = LP_burnt / LP_initial
      *        X_out = X_initial * (1 - (1 - R)^N)
      *
      * No fee is applied for withdrawals. For ETH output, use the NULL address as outputToken.
      */
      function removeLiquidity(uint256 LPamount, address outputToken, uint256 minOutputAmount)
        external
        onlyListedToken(outputToken)
        override
        returns (uint256 actualOutput)
      {
        // Checks the initial balance of the token desired as output token
        uint256 initialBalance;
        if (outputToken == address(0)) {
          initialBalance = address(this).balance;
        } else {
          initialBalance = IERC20(outputToken).balanceOf(address(this));
        }
        // Calculates intermediate variable F = (1-R)^16 and then the resulting output amount.
        uint256 F_;
        F_ = (1 << 64) - (LPamount << 64) / totalSupply();   // (1-R)      (0.64 bits)
        F_ = F_ * F_;                                       // (1-R)^2    (0.128 bits)
        F_ = F_ * F_ >> 192;                                // (1-R)^4    (0.64 bits)
        F_ = F_ * F_;                                       // (1-R)^8    (0.128 bits)
        F_ = F_ * F_ >> 192;                                // (1-R)^16   (0.64 bits)
        actualOutput = initialBalance * ((1 << 64) - F_) >> 64;
        require(actualOutput > minOutputAmount, "DFP: No deal");
        // Burns the LP tokens and sends the output tokens
        _burn(msg.sender, LPamount);
        if (outputToken == address(0)) {
          address payable sender = payable(msg.sender);
          sender.transfer(actualOutput);
        } else {
          IERC20(outputToken).safeTransfer(msg.sender, actualOutput);
        }
        // Emitting liquidity removal event to enable better governance decisions
        emit LiquidityRemoved(msg.sender, outputToken, actualOutput, LPamount);
      }
      /**
      * Multi-token liquidity removal. More economic for large amounts of liquidity.
      * Returns all 16 listed tokens in ratio and burns the LPs accordingly.
      */
      function removeMultiple(uint256 LPamount, address[] calldata tokens)
        external
        override
        returns (bool success)
      {
        // Perform basic validation (no lock check here on purpose)
        require(tokens.length == 16, "DFP: Bad tokens array length");
        // Calculate fraction of total liquidity to be returned
        uint256 fraction = (LPamount << 128) / totalSupply();
        // Send the ETH first (use transfer to prevent reentrancy)
        uint256 dexBalance = address(this).balance;
        address payable sender = payable(msg.sender);
        sender.transfer(fraction * dexBalance >> 128);
        // Send the ERC20 tokens
        address previous;
        for (uint256 i = 1; i < 16; i++) {
          address token = tokens[i];
          require(token > previous, "DFP: Require ordered list");
          require(
            listedTokens[token].state > State.Delisting,
            "DFP: Token not listed"
          );
          dexBalance = IERC20(token).balanceOf(address(this));
          IERC20(token).safeTransfer(msg.sender, fraction * dexBalance >> 128);
          previous = token;
        }
        // Burn the LPs
        _burn(msg.sender, LPamount);
        emit MultiLiquidityRemoved(msg.sender, LPamount, totalSupply());
        // That's all folks
        return true;
      }
      /**
      * When a token is delisted and another one gets listed in its place, the users can
      * call this function to provide liquidity for the new token in exchange for the old
      * token. The ratio should be set such that the users have a financial incentive to
      * perform this transaction.
      */
      function bootstrapNewToken(
        address inputToken,
        uint256 maxInputAmount,
        address outputToken
      ) public override returns (uint64 fractionBootstrapped) {
        // Check whether the valid token is being bootstrapped
        TokenSettings memory tokenToList = listedTokens[inputToken];
        require(
          tokenToList.state == State.PreListing,
          "DFP: Wrong token"
        );
        // Calculate how many tokens to actually take in (clamp at max available)
        uint256 initialInputBalance = IERC20(inputToken).balanceOf(address(this));
        uint256 availableAmount;
        // Intentionally underflow (zero clamping) is the cheapest way to gracefully prevent failing when target is already met
        unchecked { availableAmount = tokenToList.listingTarget - initialInputBalance; }
        if (initialInputBalance >= tokenToList.listingTarget) { availableAmount = 1; }
        uint256 actualInputAmount = maxInputAmount > availableAmount ? availableAmount : maxInputAmount;
        // Actually pull the tokens in
        IERC20(inputToken).safeTransferFrom(msg.sender, address(this), actualInputAmount);
        // Check whether the output token requested is indeed being delisted
        TokenSettings memory tokenToDelist = listedTokens[outputToken];
        require(
          tokenToDelist.state == State.Delisting,
          "DFP: Wrong token"
        );
        // Check how many of the output tokens should be given out and transfer those
        uint256 initialOutputBalance = IERC20(outputToken).balanceOf(address(this));
        uint256 outputAmount = actualInputAmount * initialOutputBalance / availableAmount;
        IERC20(outputToken).safeTransfer(msg.sender, outputAmount);
        fractionBootstrapped = uint64((actualInputAmount << 64) / tokenToList.listingTarget);
        // Emit event for better governance decisions
        emit Bootstrapped(
          msg.sender,
          inputToken,
          actualInputAmount,
          outputToken,
          outputAmount
        );
        // If the input token liquidity is now at the target we complete the (de)listing
        if (actualInputAmount == availableAmount) {
          tokenToList.state = State.Listed;
          listedTokens[inputToken] = tokenToList;
          delete listedTokens[outputToken];
          delete listingUpdate;
          DFPconfig.delistingBonus = 0;
          emit BootstrapCompleted(outputToken, inputToken);
        }
      }
      /**
       * Emergency bonus withdrawal when bootstrapping is expected to remain incomplete
       * A fraction is specified (for example 5%) that is then rewarded in bonus tokens
       * on top of the regular bootstrapping output tokens.
       */
      function bootstrapNewTokenWithBonus(
        address inputToken,
        uint256 maxInputAmount,
        address outputToken,
        address bonusToken
      ) external onlyListedToken(bonusToken) override returns (uint256 bonusAmount) {
        // Check whether the output token requested is indeed being delisted
        TokenSettings memory tokenToDelist = listedTokens[outputToken];
        require(
          tokenToDelist.state == State.Delisting,
          "DFP: Wrong token"
        );
        // Collect parameters required to calculate bonus
        uint256 bonusFactor = uint256(DFPconfig.delistingBonus);
        uint64 fractionBootstrapped = bootstrapNewToken(inputToken, maxInputAmount, outputToken);
        // Balance of selected bonus token
        uint256 bonusBalance;
        if (bonusToken == address(0)) {
          bonusBalance = address(this).balance;
        } else {
          bonusBalance = IERC20(bonusToken).balanceOf(address(this));
        }
        // Calculate bonus amount
        bonusAmount = uint256(fractionBootstrapped) * bonusFactor * bonusBalance >> 128;
        // Payout bonus tokens
        if (bonusToken == address(0)) {
          address payable sender = payable(msg.sender);
          sender.transfer(bonusAmount);
        } else {
          IERC20(bonusToken).safeTransfer(msg.sender, bonusAmount);
        }
        // Emit event to enable data driven governance
        emit BootstrapBonus(
          msg.sender,
          bonusToken,
          bonusAmount
        );
      }
      /**
       * Initiates process to delist one token and list another.
       */
      function changeListing(
        address tokenToDelist,              // Address of token to be delisted
        address tokenToList,                // Address of token to be listed
        uint112 listingTarget               // Amount of tokens needed to activate listing
      ) external onlyListedToken(tokenToDelist) onlyOwner() {
        // Basic validity checks. ETH cannot be delisted, only one delisting at a time.
        require(tokenToDelist != address(0), "DFP: Cannot delist ETH");
        ListingUpdate memory update = listingUpdate;
        require(update.tokenToDelist == address(0), "DFP: Previous update incomplete");
        // Can't list an already listed token
        TokenSettings memory _token = listedTokens[tokenToList];
        require(_token.state == State.Unlisted, "DFP: Token already listed");
        // Set the delisting/listing struct.
        update.tokenToDelist = tokenToDelist;
        update.tokenToList = tokenToList;
        listingUpdate = update;
        // Configure the token states for incoming/outgoing tokens
        _token.state = State.PreListing;
        _token.listingTarget = listingTarget;
        listedTokens[tokenToList] = _token;
        listedTokens[tokenToDelist].state = State.Delisting;
      }
      /**
      * Sets trading fee (actually calculates using 1-fee) as a 0.64 fixed point number.
      */
      function setTradingFee(uint64 oneMinusFee) external onlyOwner() {
        DFPconfig.oneMinusTradingFee = oneMinusFee;
      }
      /**
      * Sets delisting bonus as emergency measure to complete a (de)listing when it gets stuck.
      */
      function setDeListingBonus(uint64 delistingBonus) external onlyOwner() {
        ListingUpdate memory update = listingUpdate;
        require(update.tokenToDelist != address(0), "DFP: No active delisting");
        DFPconfig.delistingBonus = delistingBonus;
      }
      /**
      * Sets admin address for emergency exchange locking
      */
      function setAdmin(address adminAddress) external onlyOwner() {
        admin = adminAddress;
      }
      /**
      * Sets exchange lock, under which swap and liquidity add (but not remove) are disabled
      */
      function lockExchange() external onlyAdmin() {
        DFPconfig.unlocked = false;
      }
      /**
      * Resets exchange lock.
      */
      function unlockExchange() external onlyAdmin() {
        DFPconfig.unlocked = true;
      }
    }
    // SPDX-License-Identifier: Unlicensed
    pragma solidity ^0.8.6;
    interface IDeFiPlaza {
      function swap(
        address inputToken,
        address outputToken,
        uint256 inputAmount,
        uint256 minOutputAmount
      ) external payable returns (uint256 outputAmount);
      function addLiquidity(
        address inputToken,
        uint256 inputAmount,
        uint256 minLP
      ) external payable returns (uint256 deltaLP);
      function addMultiple(
        address[] calldata tokens,
        uint256[] calldata maxAmounts
      ) external payable returns (uint256 actualLP);
      function removeLiquidity(
        uint256 LPamount,
        address outputToken,
        uint256 minOutputAmount
      ) external returns (uint256 actualOutput);
      function removeMultiple(
        uint256 LPamount,
        address[] calldata tokens
      ) external returns (bool success);
      function bootstrapNewToken(
        address inputToken,
        uint256 maxInputAmount,
        address outputToken
      ) external returns (uint64 fractionBootstrapped);
      function bootstrapNewTokenWithBonus(
        address inputToken,
        uint256 maxInputAmount,
        address outputToken,
        address bonusToken
      ) external returns (uint256 bonusAmount);
      event Swapped(
        address sender,
        address inputToken,
        address outputToken,
        uint256 inputAmount,
        uint256 outputAmount
      );
      event LiquidityAdded(
        address sender,
        address token,
        uint256 tokenAmount,
        uint256 LPs
      );
      event MultiLiquidityAdded(
        address sender,
        uint256 LPs,
        uint256 totalLPafter
      );
      event LiquidityRemoved(
        address recipient,
        address token,
        uint256 tokenAmount,
        uint256 LPs
      );
      event MultiLiquidityRemoved(
        address sender,
        uint256 LPs,
        uint256 totalLPafter
      );
      event Bootstrapped(
        address sender,
        address inputToken,
        uint256 inputAmount,
        address outputToken,
        uint256 outputAmount
      );
      event BootstrapBonus(
        address sender,
        address bonusToken,
        uint256 bonusAmount
      );
      event BootstrapCompleted(
        address delistedToken,
        address listedToken
      );
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @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.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _setOwner(_msgSender());
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _setOwner(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");
            _setOwner(newOwner);
        }
        function _setOwner(address newOwner) private {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * We have followed general OpenZeppelin guidelines: functions revert instead
     * of returning `false` on failure. This behavior is nonetheless conventional
     * and does not conflict with the expectations of ERC20 applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * The default value of {decimals} is 18. To select a different value for
         * {decimals} you should overload it.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless this function is
         * overridden;
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `recipient` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            _approve(_msgSender(), spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * Requirements:
         *
         * - `sender` and `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         * - the caller must have allowance for ``sender``'s tokens of at least
         * `amount`.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) public virtual override returns (bool) {
            _transfer(sender, recipient, amount);
            uint256 currentAllowance = _allowances[sender][_msgSender()];
            require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
            unchecked {
                _approve(sender, _msgSender(), currentAllowance - amount);
            }
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            uint256 currentAllowance = _allowances[_msgSender()][spender];
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(_msgSender(), spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `sender` to `recipient`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `sender` cannot be the zero address.
         * - `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         */
        function _transfer(
            address sender,
            address recipient,
            uint256 amount
        ) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(sender, recipient, amount);
            uint256 senderBalance = _balances[sender];
            require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[sender] = senderBalance - amount;
            }
            _balances[recipient] += amount;
            emit Transfer(sender, recipient, amount);
            _afterTokenTransfer(sender, recipient, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            _balances[account] += amount;
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
            }
            _totalSupply -= amount;
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    import "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        function safeTransfer(
            IERC20 token,
            address to,
            uint256 value
        ) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
        }
        function safeTransferFrom(
            IERC20 token,
            address from,
            address to,
            uint256 value
        ) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        function safeIncreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            uint256 newAllowance = token.allowance(address(this), spender) + value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            unchecked {
                uint256 oldAllowance = token.allowance(address(this), spender);
                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                uint256 newAllowance = oldAllowance - value;
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            if (returndata.length > 0) {
                // Return data is optional
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
            uint256 size;
            assembly {
                size := extcodesize(account)
            }
            return size > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
            (bool success, bytes memory returndata) = target.staticcall(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        function _verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) private pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /*
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    

    File 2 of 2: DFPgov
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.6;
    import "../interfaces/IDeFiPlazaGov.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    /**
     * @title DeFi Plaza governance token (DFPgov)
     * @author Jazzer 9F
     * @notice Implements lean on gas liquidity reward program for DeFi Plaza
     */
    contract DFPgov is IDeFiPlazaGov, Ownable, ERC20 {
      // global staking contract state parameters squeezed in 256 bits
      struct StakingState {
        uint96 totalStake;                      // Total LP tokens currently staked
        uint96 rewardsAccumulatedPerLP;         // Rewards accumulated per staked LP token (16.80 bits)
        uint32 lastUpdate;                      // Timestamp of last update
        uint32 startTime;                       // Timestamp rewards started
      }
      // data per staker, some bits remaining available
      struct StakeData {
        uint96 stake;                           // Amount of LPs staked for this staker
        uint96 rewardsPerLPAtTimeStaked;        // Baseline rewards at the time these LPs were staked
      }
      address public founder;
      address public multisig;
      address public indexToken;
      StakingState public stakingState;
      mapping(address => StakeData) public stakerData;
      uint256 public multisigAllocationClaimed;
      uint256 public founderAllocationClaimed;
      /**
      * Basic setup
      */
      constructor(address founderAddress, uint256 mintAmount, uint32 startTime) ERC20("Defi Plaza governance", "DFP2") {
        // contains the global state of the staking progress
        StakingState memory state;
        state.startTime = startTime;
        stakingState = state;
        // generate the initial 4M founder allocation
        founder = founderAddress;
        _mint(founderAddress, mintAmount);
      }
      /**
      * For staking LPs to accumulate governance token rewards.
      * Maintains a single stake per user, but allows to add on top of existing stake.
      */
      function stake(uint96 LPamount)
        external
        override
        returns(bool success)
      {
        // Collect LPs
        require(
          IERC20(indexToken).transferFrom(msg.sender, address(this), LPamount),
          "DFP: Transfer failed"
        );
        // Update global staking state
        StakingState memory state = stakingState;
        if ((block.timestamp >= state.startTime) && (state.lastUpdate < 365 days)) {
          uint256 t1 = block.timestamp - state.startTime;       // calculate time relative to start time
          uint256 t0 = uint256(state.lastUpdate);
          t1 = (t1 > 365 days) ? 365 days : t1;                 // clamp at 1 year
          uint256 R1 = 100e24 * t1 / 365 days - 50e24 * t1 * t1 / (365 days)**2;
          uint256 R0 = 100e24 * t0 / 365 days - 50e24 * t0 * t0 / (365 days)**2;
          uint256 totalStake = (state.totalStake < 1600e18) ? 1600e18 : state.totalStake;  // Clamp at 1600 for numerical reasons
          state.rewardsAccumulatedPerLP += uint96(((R1 - R0) << 80) / totalStake);
          state.lastUpdate = uint32(t1);
        }
        state.totalStake += LPamount;
        stakingState = state;
        // Update staker data for this user
        StakeData memory staker = stakerData[msg.sender];
        if (staker.stake == 0) {
          staker.stake = LPamount;
          staker.rewardsPerLPAtTimeStaked = state.rewardsAccumulatedPerLP;
        } else {
          uint256 LP1 = staker.stake + LPamount;
          uint256 RLP0_ = (uint256(LPamount) * state.rewardsAccumulatedPerLP + uint256(staker.stake) * staker.rewardsPerLPAtTimeStaked) / LP1;
          staker.stake = uint96(LP1);
          staker.rewardsPerLPAtTimeStaked = uint96(RLP0_);
        }
        stakerData[msg.sender] = staker;
        // Emit staking event
        emit Staked(msg.sender, LPamount);
        return true;
      }
      /**
      * For unstaking LPs and collecting rewards accumulated up to this point.
      * Any unstake action distributes and resets rewards. Simply claiming rewards
      * without unstaking can be done by unstaking zero LPs.
      */
      function unstake(uint96 LPamount)
        external
        override
        returns(uint256 rewards)
      {
        // Collect data for this user
        StakeData memory staker = stakerData[msg.sender];
        require(
          staker.stake >= LPamount,
          "DFP: Insufficient stake"
        );
        // Update the global staking state
        StakingState memory state = stakingState;
        if ((block.timestamp >= state.startTime) && (state.lastUpdate < 365 days)) {
          uint256 t1 = block.timestamp - state.startTime;       // calculate time relative to start time
          uint256 t0 = uint256(state.lastUpdate);
          t1 = (t1 > 365 days) ? 365 days : t1;                 // clamp at 1 year
          uint256 R1 = 100e24 * t1 / 365 days - 50e24 * t1 * t1 / (365 days)**2;
          uint256 R0 = 100e24 * t0 / 365 days - 50e24 * t0 * t0 / (365 days)**2;
          uint256 totalStake = (state.totalStake < 1600e18) ? 1600e18 : state.totalStake;  // Clamp at 1600 for numerical reasons
          state.rewardsAccumulatedPerLP += uint96(((R1 - R0) << 80) / totalStake);
          state.lastUpdate = uint32(t1);
        }
        state.totalStake -= LPamount;
        stakingState = state;
        // Calculate rewards
        rewards = ((uint256(state.rewardsAccumulatedPerLP) - staker.rewardsPerLPAtTimeStaked) * staker.stake) >> 80;
        // Update user data
        if (LPamount == staker.stake) delete stakerData[msg.sender];
        else {
          staker.stake -= LPamount;
          staker.rewardsPerLPAtTimeStaked = state.rewardsAccumulatedPerLP;
          stakerData[msg.sender] = staker;
        }
        // Distribute reward and emit event
        _mint(msg.sender, rewards);
        require(
          IERC20(indexToken).transfer(msg.sender, LPamount),
          "DFP: Kernel panic"
        );
        emit Unstaked(msg.sender, LPamount, rewards);
      }
      /**
      * Helper function to check unclaimed rewards for any address
      */
      function rewardsQuote(address stakerAddress)
        external
        view
        override
        returns(uint256 rewards)
      {
        // Collect user data
        StakeData memory staker = stakerData[stakerAddress];
        // Calculate distribution since last on chain update
        StakingState memory state = stakingState;
        if ((block.timestamp >= state.startTime) && (state.lastUpdate < 365 days)) {
          uint256 t1 = block.timestamp - state.startTime;       // calculate time relative to start time
          uint256 t0 = uint256(state.lastUpdate);
          t1 = (t1 > 365 days) ? 365 days : t1;                 // clamp at 1 year
          uint256 R1 = 100e24 * t1 / 365 days - 50e24 * t1 * t1 / (365 days)**2;
          uint256 R0 = 100e24 * t0 / 365 days - 50e24 * t0 * t0 / (365 days)**2;
          uint256 totalStake = (state.totalStake < 1600e18) ? 1600e18 : state.totalStake;  // Clamp at 1600 for numerical reasons
          state.rewardsAccumulatedPerLP += uint96(((R1 - R0) << 80) / totalStake);
        }
        // Calculate unclaimed rewards
        rewards = ((uint256(state.rewardsAccumulatedPerLP) - staker.rewardsPerLPAtTimeStaked) * staker.stake) >> 80;
      }
      /**
      * Configure which token is accepted as stake. Can only be done once.
      */
      function setIndexToken(address indexTokenAddress)
        external
        onlyOwner
        returns(bool success)
      {
        require(indexToken==address(0), "Already configured");
        indexToken = indexTokenAddress;
        _mint(indexTokenAddress, 36e23);
        return true;
      }
      /**
      * Set community multisig address
      */
      function setMultisigAddress(address multisigAddress)
        external
        onlyOwner
        returns(bool success)
      {
        multisig = multisigAddress;
        return true;
      }
      /**
      * Community is allocated 5M governance tokens which are released on the same
      * curve as the tokens that users can stake for. No staking required for this.
      * Rewards accumulated can be claimed into the multisig address anytime.
      */
      function claimMultisigAllocation()
        external
        returns(uint256 amountReleased)
      {
        // Collect global staking state
        StakingState memory state = stakingState;
        require(block.timestamp > state.startTime, "Too early guys");
        // Calculate total community allocation until now
        uint256 t1 = block.timestamp - state.startTime;       // calculate time relative to start time
        t1 = (t1 > 365 days) ? 365 days : t1;                 // clamp at 1 year
        uint256 R1 = 5e24 * t1 / 365 days - 25e23 * t1 * t1 / (365 days)**2;
        // Calculate how much is to be released now & update released counter
        amountReleased = R1 - multisigAllocationClaimed;
        multisigAllocationClaimed = R1;
        // Grant rewards and emit event for logging
        _mint(multisig, amountReleased);
        emit MultisigClaim(multisig, amountReleased);
      }
      /**
      * Founder is granted 5M governance tokens after 1 year.
      */
      function claimFounderAllocation(uint256 amount, address destination)
        external
        returns(uint256 actualAmount)
      {
        // Basic validity checks
        require(msg.sender == founder, "Not yours man");
        StakingState memory state = stakingState;
        require(block.timestamp - state.startTime >= 365 days, "Too early man");
        // Calculate how many rewards are still available & update claimed counter
        uint256 availableAmount = 25e23 - founderAllocationClaimed;
        actualAmount = (amount > availableAmount) ? availableAmount : amount;
        founderAllocationClaimed += actualAmount;
        // Grant rewards and emit event for logging
        _mint(destination, actualAmount);
        emit FounderClaim(destination, actualAmount);
      }
      /**
      * Freeze program (makes it easier to migrate if required)
      * This is a one-way thing, only to be used in case of migration.
      */
      function stopProgram()
        external
        onlyOwner()
      {
        // Update the global staking state
        StakingState memory state = stakingState;
        if ((block.timestamp >= state.startTime) && (state.lastUpdate < 365 days)) {
          uint256 t1 = block.timestamp - state.startTime;       // calculate time relative to start time
          uint256 t0 = uint256(state.lastUpdate);
          t1 = (t1 > 365 days) ? 365 days : t1;                 // clamp at 1 year
          uint256 R1 = 100e24 * t1 / 365 days - 50e24 * t1 * t1 / (365 days)**2;
          uint256 R0 = 100e24 * t0 / 365 days - 50e24 * t0 * t0 / (365 days)**2;
          uint256 totalStake = (state.totalStake < 1600e18) ? 1600e18 : state.totalStake;  // Clamp at 1600 for numerical reasons
          state.rewardsAccumulatedPerLP += uint96(((R1 - R0) << 80) / totalStake);
          state.lastUpdate = uint32(t1);
        }
        // Freeze by setting the startTime when we're all going to be dead
        state.startTime = type(uint32).max;
        stakingState = state;
      }
    }
    // SPDX-License-Identifier: Unlicensed
    pragma solidity ^0.8.6;
    interface IDeFiPlazaGov {
      function stake(
        uint96 LPamount
      ) external returns(bool success);
      function unstake(
        uint96 LPamount
      ) external returns(uint256 rewards);
      function rewardsQuote(
        address stakerAddress
      ) external view returns(uint256 rewards);
      event Staked(
        address staker,
        uint256 LPamount
      );
      event Unstaked(
        address staker,
        uint256 LPamount,
        uint256 rewards
      );
      event MultisigClaim(
        address multisig,
        uint256 amount
      );
      event FounderClaim(
        address claimant,
        uint256 amount
      );
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @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.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _setOwner(_msgSender());
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _setOwner(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");
            _setOwner(newOwner);
        }
        function _setOwner(address newOwner) private {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * We have followed general OpenZeppelin guidelines: functions revert instead
     * of returning `false` on failure. This behavior is nonetheless conventional
     * and does not conflict with the expectations of ERC20 applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * The default value of {decimals} is 18. To select a different value for
         * {decimals} you should overload it.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless this function is
         * overridden;
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `recipient` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            _approve(_msgSender(), spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * Requirements:
         *
         * - `sender` and `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         * - the caller must have allowance for ``sender``'s tokens of at least
         * `amount`.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) public virtual override returns (bool) {
            _transfer(sender, recipient, amount);
            uint256 currentAllowance = _allowances[sender][_msgSender()];
            require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
            unchecked {
                _approve(sender, _msgSender(), currentAllowance - amount);
            }
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            uint256 currentAllowance = _allowances[_msgSender()][spender];
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(_msgSender(), spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `sender` to `recipient`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `sender` cannot be the zero address.
         * - `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         */
        function _transfer(
            address sender,
            address recipient,
            uint256 amount
        ) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(sender, recipient, amount);
            uint256 senderBalance = _balances[sender];
            require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[sender] = senderBalance - amount;
            }
            _balances[recipient] += amount;
            emit Transfer(sender, recipient, amount);
            _afterTokenTransfer(sender, recipient, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            _balances[account] += amount;
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
            }
            _totalSupply -= amount;
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /*
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }