// Contract: GoaldFlexibleDeployer
// Target function: function tokenURI(uint256 tokenId) external view returns (string memory)
00000;
    uint256 private constant FINALIZATION_TIMESTAMP_SHIFT = 197;

    uint256 private constant FINALIZED_MASK               = 0x20000000000000000000000000000000000000000000000000000000000000;
    uint256 private constant FINALIZED_SHIFT              = 245;

    // The divisor used to calculate the fee paid to the Goald DAO. This is currently 0.25%.
    uint256 private constant FEE_DIVISOR = 400;


    /** @dev The collateral token stored (e.g., WBTC). */
    address private _collateralAddress;

    /** @dev The payment token taken (e.g., WETH). */
    address private _paymentAddress;

    /** @dev The address of the Uniswap router which will facilitate the swap between the payment token and the collateral token. */
    address private _uniswapRouterAddress;

    /**
     * @dev The owner of the Goald can optionally appoint a steward. The steward's only authority is to update the uniswap router
     * address or replace themself. This is intended if the Goald needs separation between ownership and management (e.g., via DAOs).
     */
    address private _steward;

    /**
     * @dev Packed static values (all uints). The only one that will change is `finalized`, and only once.
     *
     *  bits    offset    variable name            description
     *  ----    ------    -------------            -----------
     *   168         0    fee                      The fee per interval of the goald.
     *     9       168    feeIntervalDays          How many days between each fee payment; minimum 1.
     *    20       177    totalIntervals           The total number of intervals this goald will have when it is finalized.
     *    48       197    finalizationTimestamp    When the Goald can be finalized if it isn't paid in full.
     *     1       245    finalized                If the Goald can be withdrawn from.
     */
    uint256 private _staticValues;

    /** @dev The total amount of the payment token that must be paid over the life of the Goald to finalize it. */
    uint256 private _requiredTotalFeePaid;

    /** @dev The total amount of the payment token that has been paid over the life of the Goald. */
    uint256 private _totalFeePaid;

    // We include this here instead of the `nonReentrant` modifier to reduce gas costs. We also might change the reentrancy state
    // multiple times within a single function depending on needs.
    // See OpenZeppelin - ReentrancyGuard for more.
    // Reentrancy reversions are the only calls to revert (in this contract) that do not have reasons.
    uint256 private constant RE_NOT_ENTERED = 1;
    uint256 private constant RE_ENTERED     = 2;
    uint256 private _status;

    /// Events ///

    /** @dev Emitted whenever a fee is paid. */
    event FeePaid(address paymentToken, uint256 feePaid, uint256 feeTokensSpent, uint256 collateralTokensReceived);

    /** @dev Emitted when the collateral pool has been withdrawn from. */
    event Withdrawal(uint256 amount);

    /** @dev Emitted when the Uniswap router address has changed. */
    event RouterAddressChanged(address newAddress);

    /** @dev Emitted when the steward address has changed. */
    event StewardChanged(address newSteward);

    /// Modifiers ///

    /** @dev Applied to functions that must be guarded against reentrancy which only the owner or steward can call. */
    modifier nonReentrant_OnlyOwnerSteward {
        require(_status == RE_NOT_ENTERED && (msg.sender == _owner || msg.sender == _steward));
        _status = RE_ENTERED;

        _;

        // Store the original amount to get a refund.
        _status = RE_NOT_ENTERED;
    }

    /**
     * @dev Applied to functions that must be guarded against reentrancy which only the owner or steward can call. This modifier
     * doesn't update the current value of `_status`, so it cannot make any non-internal function calls.
     */
    modifier nonReentrant_OnlyOwnerSteward_InternalCallsOnly {
        require(_status == RE_NOT_ENTERED && (msg.sender == _owner || msg.sender == _steward));

        _;
    }

    /** @dev Applied to functions that must be guarded against reentrancy, but are otherwise publicly accessible. */
    modifier nonReentrant_Public {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED);
        _status = RE_ENTERED;

        _;

        // Store the original amount to get a refund.
        _status = RE_NOT_ENTERED;
    }

    /// Constructor ///

    /**
     * @param daoAddress The address of the Goald DAO.
     * @param owner Who owns the goald and is required to pay the fees to maintain it (though anyone can pay the fees).
     * @param name The name of this goald.
     * @param goaldId The unique id of this Goald.
     * @param collateralToken Which token will be used as collateral in the collateral pool (e.g., WBTC).
     * @param paymentToken Which token will be used to pay for the collateral token (e.g., WETH).
     * @param uniswapRouterAddress The address of the liquidity swap router.
     * @param fee The periodic fee required to be paid to maintain the goald; minimum 1. Anyone can pay the fee.
     * @param feeIntervalDays How many days between each fee payment; minimum 1.
     * @param totalIntervals How many fee intervals must pass before the goald finalizes; minimum 1.
     */
    constructor(
        address daoAddress,
        address owner,
        string memory name,
        uint256 goaldId,
        address collateralToken,
        address paymentToken,
        address uniswapRouterAddress,
        uint256 fee,
        uint256 feeIntervalDays,
        uint256 totalIntervals
    ) Goald721(name, "GOALD", goaldId) public {
        // Do validation. We don't do any validation on the addresses being contracts, only that they are unique among themselves
        // and are not obviously invalid. It is up to the user to use addresses that are ERC20 compliant.
        require(
            // Validate the addresses.
               daoAddress      != address(0)
            && daoAddress      != address(this)
            && owner           != address(0)
            && owner           != address(this)
            && owner           != collateralToken
            && owner           != paymentToken
            && collateralToken != address(0)
            && collateralToken != address(this)
            && collateralToken != paymentToken
            && paymentToken    != address(0)
            && paymentToken    != address(this)

            // Validate the numbers.
            && fee             > 0 && fee             <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF // uint168
            && feeIntervalDays > 0 && feeIntervalDays <= 0x1FF                                        // uint9
            && totalIntervals  > 0 && totalIntervals  <= 0xFFFFF                                      // uint20
        , "Invalid parameters");

        // Clean any dirty bits from the number values.
        fee             = fee             & (FEE_MASK               >> FEE_SHIFT);
        feeIntervalDays = feeIntervalDays & (FEE_INTERVAL_DAYS_MASK >> FEE_INTERVAL_DAYS_SHIFT);
        totalIntervals  = totalIntervals  & (TOTAL_INTERVALS_MASK   >> TOTAL_INTERVALS_SHIFT);
        uint256 finalizationTimestamp = block.timestamp + (totalIntervals * feeIntervalDays * 1 days);

        // Set the addresses.
        _daoAddress           = daoAddress;
        _collateralAddress    = collateralToken;
        _paymentAddress       = paymentToken;
        _owner                = owner;
        _uniswapRouterAddress = uniswapRouterAddress;

        // Store the values.
        _requiredTotalFeePaid = fee * totalIntervals;
        _staticValues = 0
            | (fee                   << FEE_SHIFT)
            | (feeIntervalDays       << FEE_INTERVAL_DAYS_SHIFT)
            | (totalIntervals        << TOTAL_INTERVALS_SHIFT)
            | (finalizationTimestamp << FINALIZATION_TIMESTAMP_SHIFT);

        // Set us up for reentrancy guarding.
        _status = RE_NOT_ENTERED;
    }

    /// Views ///

    /**
     * Single view over all internal state variables.
     *
     * Returns:
     *     Proxy contract address
     *     Collateral token address
     *     Payment token address (or 0 for ETH)
     *     Uniswap router address
     *     Next fee timestamp
     *     Packed state values
     */
    function getDetails() external view returns (uint256[10] memory details) {
        address collateralAddress = _collateralAddress;

        // Important Addresses.
        details[0] = uint256(_daoAddress);
        details[1] = uint256(collateralAddress);
        details[2] = uint256(_paymentAddress);
        details[3] = uint256(_uniswapRouterAddress);
        details[4] = uint256(_steward);
        details[5] = uint256(_owner);

        // The id of this Goald.
        details[6] = _tokenId;

        // The total amount paid into the Goald so far.
        details[7] = _totalFeePaid;

        // Current balance.
        details[8] = IERC20(collateralAddress).balanceOf(address(this));

        // Packed values.
        details[9] = _staticValues;
    }

    /// External Functions ///

    /**
     * Pay out the stored collateral. This can only be called by and paid out to the owner, not a third party, even the steward. This
     * This can only be called once the finalization date has been reached, but can be called multiple times after that if desired.
     */
    function withdrawFromGoald(uint256 amount) external {
        // So we can extract the packed values without unnecessary SLOADs.
        uint256 values = _staticValues;
        uint256 finalized             = (values & FINALIZED_MASK)              >> FINALIZED_SHIFT;
        uint256 finalizationTimestamp = (values & FINALIZATION_TIMESTAMP_MASK) >> FINALIZATION_TIMESTAMP_SHIFT;

        // Rather than using a `nonReentrant_OnlyOwner` modifier, we include the reentrancy guard manually. This save gas since we're
        // caching `_owner` into `owner`.
        address owner = _owner;
        require(
               _status == RE_NOT_ENTERED
            && msg.sender == owner
            && (
                // This is only set during a call to `payFee` if the Goald has been fully paid out.
                   finalized > 0
                || block.timestamp >= finalizationTimestamp
            )
        , "Not authorized");
        _status = RE_ENTERED;

        // Make sure we have something to withdraw.
        IERC20 collateralToken = IERC20(_collateralAddress);
        uint256 currentGoaldBalance = collateralToken.balanceOf(address(this));
        uint256 currentOwnerBalance = collateralToken.balanceOf(owner);
        require(
               amount > 0
            && amount <= currentGoaldBalance
            && currentOwnerBalance + amount > currentOwnerBalance
        , "Invalid amount");

        // Transfer the collateral.
        require(collateralToken.transfer(owner, amount));

        // Validate the transfer.
        require(
               collateralToken.balanceOf(address(this)) == currentGoaldBalance - amount
            && collateralToken.balanceOf(owner)         == currentOwnerBalance + amount
        , "Post transfer balance wrong");

        // Hello world!
        emit Withdrawal(amount);

        // Store the original amount to get a refund.
        _status = RE_NOT_ENTERED;
    }
 

    /** Pay the goald fee. Overpayments change the finalization date. */
    function payFee(uint256 amount, address[] calldata swapPath, uint256 deadline) external nonReentrant_Public {
        // Make sure we have a valid swap path.
        require(swapPath.length > 1 && swapPath[swapPath.length - 1] == _collateralAddress, "Invalid swap path");

        // Clean up any dirty bits in the amount. We don't validate that this is more than zero. At best, the swap function will
        // revert (either on input being zero, or minimum output not being more than one). At worst, The user called this function
        // with invalid parameters.
        amount = amount & (FEE_MASK >> FEE_SHIFT);

        // Make sure we have the most up to date DAO address. If this call reverts, then no fees can be paid.
        address daoAddress = IGoaldDAO(_daoAddress).getProxyAddress();
        if (address(daoAddress) != daoAddress) {
            _daoAddress = daoAddress;
        }

        uint256[] memory amounts;
        uint256 receivedAmount;
        { // Scoped to prevent stack too deep error.
            // Transfer from the user.
            IERC20 paymentContract = IERC20(swapPath[0]);
            uint256 fee = amount / FEE_DIVISOR;
            amount -= fee;
            require(paymentContract.transferFrom(msg.sender, address(this), amount)); 
            if (fee > 0) {
                require(paymentContract.transferFrom(msg.sender, daoAddress, fee));
            }

            // We'll be verifying uniswap did what it said it did.
            IERC20 collateralContract = IERC20(_collateralAddress);
            uint256 currentCollateralBalance = collateralContract.balanceOf(address(this));

            // Set the router's allowance to cover the trade. We are only authorizing enough for the collateral amount.
            address uniswapRouterAddress = _uniswapRouterAddress;
            require(paymentContract.approve(uniswapRouterAddress, amount));
            
            // Try and transfer from the user to the goald. We use Uniswap to get the collateral token. We don't care how much of the
            // collateral was returned, so long as it is greater than zero.
            amounts = IUniswapV2Router02(uniswapRouterAddress).swapExactTokensForTokens(amount, 1, swapPath, address(this), deadline);

            // Double check the balance. Amounts is the same length as `_uniPath`: 3.
            receivedAmount = amounts[swapPath.length - 1];
            require(currentCollateralBalance + receivedAmount > currentCollateralBalance, "UNI: Overflow error");
            require(collateralContract.balanceOf(address(this)) == currentCollateralBalance + receivedAmount, "UNI: Wrong balance");

            // Reset the router's allowance to zero to prevent unauthorized spending / double spending. The refund would be larger if
            // it was kept non-zero, but though one token can be considered miniscule (e.g., for WETH, which has 18 decimal places),
            // we still must be diligent.
            require(paymentContract.approve(uniswapRouterAddress, 0));
        }

        // The Goald hasn't been finalized yet, so update the total amount paid. We don't keep track of how many payments have
        // been made afer the it has been finalized.
        if ((_staticValues & FINALIZED_MASK) >> FINALIZED_SHIFT == 0) {
            // Calculate the new total.
            uint256 totalFeePaid = _totalFeePaid;
            uint256 newTotalFeePaid = totalFeePaid + amount;

            // We'll consider an overflow on the amount paid to be the same as fully paying out the Goald.
            if (newTotalFeePaid < totalFeePaid) {
                _staticValues |= FINALIZED_MASK;
            } else if (newTotalFeePaid >= _requiredTotalFeePaid) {
                _staticValues |= FINALIZED_MASK;
            } else {
                _totalFeePaid = newTotalFeePaid;
            }
        }

        // Hello world!
        emit FeePaid(swapPath[0], amount, amounts[0], receivedAmount);
    }

    /** Failsafe so the owner can withdraw any tokens other than the collateral token that may end up within this Goald. */
    function transferERC20(address tokenAddress, uint256 amount) external nonReentrant_OnlyOwnerSteward {
        require(tokenAddress != _collateralAddress, "Invalid address");
        require(IERC20(tokenAddress).transfer(_owner, amount));
    }

    /**
     * Changes the uniswap router contract address for the swaps. This can be changed to anything that has the same API:
     * 
     * `swapExactTokensForTokens(
     *      uint256   amount,
     *      uint256   minAmount,
     *      address[] path,
     *      address   destination,
     *      uint256   deadline
     *  ) external returns (uint256[] amounts);`
     *
     * Only the owner or steward of the Goald can update the router.
     */
    function updateRouterAddress(address newAddress) external nonReentrant_OnlyOwnerSteward_InternalCallsOnly {
        // We're not doing address validation because this function is restricted to only two callers: the owner or steward. However,
        // the things we would validate against would be:
        //
        // 1) Not being the zero address. Since that address doesn't support the swap function it would revert on any payments.
        // 2) The goald address (or the deployer address). Again, neither support the swap function.
        // 3) The DAO address / a token address. Again, neither support the swap function (although a unified proxy DAO is possible).
        // 4) The steward / owner. It's possible this condition is legitimate.

        // Update the router address.
        _uniswapRouterAddress = newAddress;

        emit RouterAddressChanged(newAddress);
    }

    /** Changes the steward. This can be set to the zero address to disable steward functionality.*/
    function updateSteward(address newSteward) external nonReentrant_OnlyOwnerSteward_InternalCallsOnly {
        // We don't do any validation on the new steward. Either the new address can be aware of its stewardship abilities or it
        // cannot. In the second case, it doesn't matter what it is. In the first case, it would pass all theoretical validation.

        // Update the steward.
        _steward = newSteward;

        emit StewardChanged(newSteward);
    }

    /// "Overridden" Functions ///