Sepolia Testnet

Contract

0xf7e0A86084720a26ddFB794ce8FEb5921f8C77E1

Overview

ETH Balance

0 ETH

Token Holdings

Multichain Info

N/A
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction and 2 Token Transfers found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block
From
To
55791202024-03-28 15:39:36297 days ago1711640376  Contract Creation0 ETH
Loading...
Loading

Minimal Proxy Contract for 0x26a6082fa9606c5ac62f46170b9fc806a5c406f4

Contract Name:
Pool

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 30 : a-pool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Params, Constants, Validator, Controller as ORController, OpenRewards as ORData, OpenRewardsProvider as provider, Positions} from "../../providers/deployables/a-pool.sol";
import "../standards/enumerable.sol";
import {Eunice as Helper} from "../../providers/common/eunice.sol";

contract Pool is ORController, NonFungibleAndEnumerable {
    constructor(
        address reward,
        address meToken,
        Params.ConfigForTypeAOpenRewards memory config
    ) NonFungible(Constants.ME_PROTOCOL, Constants.ME_P) {
        provider.setUpOpenRewards(reward, meToken, config);
    }

    function initialize(
        address reward,
        address meToken,
        Params.ConfigForTypeAOpenRewards memory config
    ) external returns (bool) {
        return provider.setUpOpenRewards(reward, meToken, config);
    }

    function startOpenRewards()
        external
        returns (uint256 optimalRatioInPrecision)
    {
        optimalRatioInPrecision = provider.startOpenRewards();
    }

    function pauseOpenRewards() external returns (bool) {
        return provider.pauseOpenRewards();
    }

    function resumeOpenRewards() external returns (bool) {
        return provider.resumeOpenRewards();
    }

    function recordLiquidityProvided(
        Params.LiquidityInfo memory info
    ) external returns (uint256 position) {
        (uint256 inputPosition, uint256 potentialPosition) = provider
            .recordLiquidityProvided(info);
        if (Validator.isZero(inputPosition)) _mint(info.to, potentialPosition);
        position = potentialPosition;
    }

    function recordMeTokensProvidedByProtocolTeam(
        uint256 amount
    ) external returns (uint256 recordedAmount) {
        recordedAmount = provider.recordMeTokensProvidedByProtocolTeam(amount);
    }

    function withdrawLiquidity(
        Params.LiquidityInfo memory info
    ) external returns (uint256 rewardAmount, uint256 meAmount) {
        (rewardAmount, meAmount) = provider.withdrawLiquidity(info);
    }

    function releaseMeTokensProvidedByProtocolTeam(
        uint256 amount,
        address to,
        bytes32 releaseType
    ) external returns (uint256 rewardAmount, uint256 meAmount) {
        (rewardAmount, meAmount) = provider
            .releaseMeTokensProvidedByProtocolTeam(amount, to, releaseType);
    }

    function addOpenRewardsManager(address account) external returns (bool) {
        return provider.addOpenRewardsManager(account);
    }

    function addLiquidityManager(address account) external returns (bool) {
        return provider.addLiquidityManager(account);
    }

    function removeOpenRewardsManager(address account) external returns (bool) {
        return provider.removeOpenRewardsManager(account);
    }

    function removeLiquidityManager(address account) external returns (bool) {
        return provider.removeLiquidityManager(account);
    }

    function getLiquidityRatios()
        external
        view
        returns (uint256 rOptimalInPrecision, uint256 rlastInPrecision)
    {
        (rOptimalInPrecision, rlastInPrecision) = provider.getLiquidityRatios();
    }

    function getOpenRewardsState()
        external
        pure
        returns (ORData.State memory state)
    {
        state = ORData.getState();
    }

    function setBusy() external returns (bool) {
        return ORData.setBusy();
    }

    function getOpenRewardsConfigurations()
        external
        pure
        returns (ORData.Config memory config)
    {
        config = ORData.getConfig();
    }

    function setUpConfig(
        Params.ConfigForTypeAOpenRewards memory _config
    ) external returns (bool) {
        return ORData.setUpConfig(_config);
    }

    function getLiquidityIds()
        external
        view
        returns (address initiator, address reward, address meToken)
    {
        (initiator, reward, meToken) = ORData.getLiqudityIds();
    }

    function initiateOutgoingConversation(
        Params.OutgoingConversationInfo memory info
    )
        external
        returns (Params.InitiateConversationReturnType memory conversionDigest)
    {
        conversionDigest = provider.initiateOutgoingConversation(info);
    }

    function engageIncomingConversation(
        uint256 _outputRewardAmount,
        address to
    ) external returns (uint256 outputRewardAmount) {
        outputRewardAmount = provider.engageIncomingConversation(
            _outputRewardAmount,
            to
        );
    }

    function changeConfigExceptOptimalRatio(
        Params.EditableConfigForTypeAOpenRewards memory editableConfig,
        bool ignoreDefault
    ) external returns (bool) {
        return
            provider.changeOpenRewardsConfigExceptROptimal(
                editableConfig,
                ignoreDefault
            );
    }

    function changeOptimalRatio(
        uint256 newOptimalRatio
    ) external returns (bool) {
        return provider.changeROptimal(newOptimalRatio);
    }

    function getLiquidityPositionByIndex(
        uint256 index,
        address account
    ) external view returns (uint256 position) {
        position = provider.getPositionByIndex(index, account);
    }

    function getAllLiquidityPositionForAccount(
        address account
    ) external view returns (uint256[] memory positions) {
        positions = provider.getAllPositions(account);
    }

    function getOptimalRatio() external view returns (uint256 r) {
        r = ORData.getOptimalRatio();
    }

    function leverageOutgoingConversationInsight(
        uint256 meAmount,
        uint256 slippageInPrecision
    ) external view returns (uint256 rewardAmount) {
        return
            provider.leverageOutgoingConversationInsight(
                meAmount,
                slippageInPrecision
            );
    }

    function leverageIncomingConversationInsight(
        uint256 rewardAmount
    ) external view returns (uint256, uint256) {
        return provider.leverageIncomingConversationInsight(rewardAmount);
    }

    function getOutgoingConversationInsight(
        uint256 meAmount,
        uint256 slippageInPrecision
    ) external view returns (uint256 rewardAmount, uint256 lastRewardAmount) {
        return
            provider.getOutgoingConversationInsight(
                meAmount,
                slippageInPrecision
            );
    }

    function getIncomingConversationInsight(
        uint256 rewardAmount
    )
        external
        view
        returns (uint256 optimalMeAmount, uint256 lastRewardAmount)
    {
        return provider.getIncomingConversationInsight(rewardAmount);
    }

    function getPositionData(
        uint256 _positionId
    ) external view returns (Positions.PositionMetadata memory data) {
        return provider.getPositionData(_positionId);
    }
}

File 2 of 30 : a-pool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Context} from "../common/context.sol";
import {OpenRewardsData as OpenRewards, Positions, Params, Errors, Constants} from "../data/a-pool.sol";
import {RoleGuard as Guard, Validator, Roles as SEEDS} from "../common/roleguard.sol";
import {Eunice as Helper, Fungible} from "../common/eunice.sol";
import {OpenRewardsController as Controller, NonFungible} from "../../controllers/deployables/a-pool.controller.sol";
import {OpenRewardsLogs as Logs} from "../logs/a-pool.sol";

library OpenRewardsProvider {
    function setUpOpenRewards(
        address reward,
        address meToken,
        Params.ConfigForTypeAOpenRewards memory _config
    ) internal returns (bool) {
        {
            Validator.ensureAddressIsNotZeroAddress(reward);
            Validator.ensureAddressIsNotZeroAddress(meToken);
        }
        OpenRewards.State storage state = OpenRewards.writableState();

        if (state.initialized) {
            revert Errors.OPEN_REWARDS_ALREADY_INITIALIZED();
        }
        
        address initiator = Context.sender();
        state.initiator = initiator;
        state.reward = reward;
        state.meToken = meToken;
        state.lastTransactionTime = uint40(Context.timestamp());
        OpenRewards.setUpConfig(_config);
        address openRewardsId = address(this);
        bytes32[] memory seeds = new bytes32[](3);
        seeds[0] = SEEDS.OPEN_REWARDS_MANAGER;
        seeds[1] = SEEDS.LIQUIDITY_MANAGER;
        seeds[2] = SEEDS.PROTOCOL_ADMIN;

        OpenRewards.setInitialized();

        return
            Guard.createNewAccessKeysForOpenRewards(
                initiator,
                openRewardsId,
                seeds
            );
    }

    function startOpenRewards() internal returns (uint256 rOptimal_) {
        address openRewardsId = address(this);
        address _requestor = Context.sender();
        Guard.ensureAccountIsAuthorized(
            openRewardsId,
            SEEDS.OPEN_REWARDS_MANAGER,
            _requestor
        );

        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();

        if (state.started) revert Errors.OPEN_REWARDS_ALREADY_STARTED();
        if (state.active) revert Errors.OPEN_REWARDS_IS_ACTIVE();

        if (config.rOptimal == 0) {
            revert Errors.OPTIMAL_REWARD_RATIO_CANNOT_BE_ZERO();
        }

        if (config.maximumRLimit < config.rOptimal) {
            revert Errors
                .MAXIMUM_REWARD_RATIO_CANNOT_BE_LESS_THAN_THE_OPTIMAL_RATIO();
        }

        (uint256 currentRewardAmount, uint256 currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(
                openRewardsId,
                state.reward,
                state.meToken
            );

        if (
            Helper.calculateLiquidityRatio(
                currentRewardAmount,
                currentMeAmount
            ) > config.rOptimal
        ) {
            revert Errors.OPEN_REWARDS_SHOULD_START_AT_R_OPTIMAL_OR_LESS();
        }

        state.meTokensFromProtocolTeam = currentMeAmount;
        state.started = true;
        state.active = true;

        rOptimal_ = config.rOptimal;

        emit Logs.openRewardsStarted(_requestor, rOptimal_);
    }

    function pauseOpenRewards() internal returns (bool) {
        address openRewardsId = address(this);
        address _requestor = Context.sender();
        Guard.ensureAccountIsAuthorized(
            openRewardsId,
            SEEDS.OPEN_REWARDS_MANAGER,
            _requestor
        );

        OpenRewards.State storage state = OpenRewards.writableState();
        if (!state.active) revert Errors.OPEN_REWARDS_IS_NOT_ACTIVE();
        state.active = false;

        emit Logs.openRewardsPaused(_requestor);
        return true;
    }

    function resumeOpenRewards() internal returns (bool) {
        address openRewardsId = address(this);
        address _requestor = Context.sender();
        Guard.ensureAccountIsAuthorized(
            openRewardsId,
            SEEDS.OPEN_REWARDS_MANAGER,
            _requestor
        );
        OpenRewards.State storage state = OpenRewards.writableState();
        if (state.active) revert Errors.OPEN_REWARDS_IS_ACTIVE();
        state.active = true;

        emit Logs.openRewardsResumed(_requestor);
        return true;
    }

    function recordLiquidityProvided(
        Params.LiquidityInfo memory info
    ) internal returns (uint256, uint256) {
        {
            Validator.ensureAddressIsNotZeroAddress(info.to);
            Validator.ensureAddressIsNotZeroAddress(info.requestor);
        }

        address openRewardsId = address(this);
        address requestor = Context.sender();

        {
            bytes32[] memory seeds = new bytes32[](2);
            seeds[0] = SEEDS.OPEN_REWARDS_MANAGER;
            seeds[1] = SEEDS.LIQUIDITY_MANAGER;

            Guard.ensureAccountIsAuthorized(openRewardsId, seeds, requestor);
        }

        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();

        (
            uint256 currentRewarAmount,
            uint256 currentMeAmount,
            uint256 addedRewardAmount,
            uint256 addedMeAmount
        ) = validateDepositRequest(
                state.reward,
                state.meToken,
                openRewardsId,
                info.rewardAmount,
                info.meAmount,
                state.lastRewardAmount,
                state.lastMeAmount
            );

        updateOpenRewardsState(
            state,
            config,
            currentRewarAmount,
            currentMeAmount,
            uint40(Context.timestamp())
        );

        if (
            currentMeAmount > config.notifyMeAmount &&
            currentRewarAmount > config.notifyRewardAmount
        ) {
            OpenRewards.incrementDepositNonce();
        }

        if (info.position != 0) {
            requestor = requestor == state.initiator
                ? info.requestor
                : requestor;

            NonFungible(openRewardsId).ensureRequestorIsPositionOwner(
                info.position,
                requestor
            );
        }

        uint256 position = info.position == 0
            ? OpenRewards.createNewPosition(addedRewardAmount, addedMeAmount)
            : OpenRewards.addToPosition(
                info.position,
                addedRewardAmount,
                addedMeAmount
            );

        emit Logs.liquidityProvided(
            addedRewardAmount,
            addedMeAmount,
            requestor,
            info.to
        );
        return (info.position, position);
    }

    function withdrawLiquidity(
        Params.LiquidityInfo memory info
    ) internal returns (uint256 rewardAmount, uint256 meAmount) {
        {
            Validator.ensureAddressIsNotZeroAddress(info.to);
            Validator.ensureAddressIsNotZeroAddress(info.requestor);
            Validator.ensureValueIsNotZero(info.position);
        }

        address openRewardsId = address(this);
        bytes32[] memory seeds = new bytes32[](2);
        seeds[0] = SEEDS.OPEN_REWARDS_MANAGER;
        seeds[1] = SEEDS.LIQUIDITY_MANAGER;
        address requestor = Context.sender();
        Guard.ensureAccountIsAuthorized(openRewardsId, seeds, requestor);

        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();

        if (state.busy) revert Errors.POOL_IS_BUSY();
        state.busy = true;

        if (info.rewardAmount == 0 && info.meAmount == 0) {
            revert Errors.BOTH_WITHDRAWALS_CANNOT_BE_ZERO();
        }

        NonFungible(openRewardsId).ensureRequestorIsPositionOwner(
            info.position,
            info.requestor
        );

        OpenRewards.removeFromPosition(
            info.position,
            info.rewardAmount,
            info.meAmount
        );

        (rewardAmount, meAmount) = obtainAmountOfLiquidityToWithdraw(
            Params.ExtendedLiquidityInfo(
                info.rewardAmount,
                info.meAmount,
                state.lastRewardAmount,
                state.lastMeAmount,
                config.minimumRewardAmountForConversation,
                config.minimumMeAmountForConversation,
                state.meTokensFromProtocolTeam,
                config.rOptimal
            )
        );
        if (rewardAmount != 0) {
            Helper.transferFungibleRewards(state.reward, info.to, rewardAmount);
        }
        if (meAmount != 0) {
            Helper.transferMeTokens(state.meToken, info.to, meAmount);
        }

        (uint256 _currentRewardAmount, uint256 _currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(
                openRewardsId,
                state.reward,
                state.meToken
            );

        updateOpenRewardsState(
            state,
            config,
            _currentRewardAmount,
            _currentMeAmount,
            uint40(Context.timestamp())
        );
        state.busy = false;

        emit Logs.liqudityWithdrawn(
            rewardAmount,
            meAmount,
            info.requestor,
            info.to
        );
    }

    function getPositionData(
        uint256 _positionId
    ) internal view returns (Positions.PositionMetadata memory data) {
        Validator.ensureValueIsNotZero(_positionId);
        return OpenRewards.getPositionData(_positionId);
    }

    function getLiquidityRatios()
        internal
        view
        returns (uint256 rOptimal, uint256 rLast)
    {
        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        rOptimal = config.rOptimal;
        rLast = Helper.calculateLiquidityRatio(
            state.lastRewardAmount,
            state.lastMeAmount
        );
    }

    function initiateOutgoingConversation(
        Params.OutgoingConversationInfo memory info
    )
        internal
        returns (Params.InitiateConversationReturnType memory returnType)
    {
        {
            Validator.ensureValueIsNotZero(info.rewardAmountIn);
            Validator.ensureValueIsNotZero(info.expectedAmountOfOutputReward);
            Validator.ensureValueIsNotZero(info.listenerROptimal);
            Validator.ensureAddressIsNotZeroAddress(info.listener);
            Validator.ensureAddressIsNotZeroAddress(info.requestor);
            Validator.ensureAddressIsNotZeroAddress(info.outputRewardReceiver);
        }

        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        if (!state.active) revert Errors.OPEN_REWARDS_IS_NOT_ACTIVE();
        if (state.busy) revert Errors.POOL_IS_BUSY();
        state.busy = true;

        address openRewardsId = address(this);

        uint256 currentRewardAmount = Helper.objectivelyObtainSingleBalance(
            openRewardsId,
            state.reward
        );

        uint256 actuallyDepositedRewardAmount = currentRewardAmount -
            state.lastRewardAmount;

        if (actuallyDepositedRewardAmount < info.rewardAmountIn) {
            Helper.ensureIsWithinAcceptablePercentRange(
                actuallyDepositedRewardAmount,
                info.rewardAmountIn
            );
        }

        uint256 neededMeTokenAmount = Helper
            .determineOptimalMeAmountForConversationGivenRewardAmount(
                info.listenerROptimal,
                info.expectedAmountOfOutputReward
            );

        if (
            neededMeTokenAmount >
            state.lastMeAmount - config.minimumMeAmountForConversation
        ) {
            revert Errors
                .ACTION_WILL_TAKE_POOL_ME_TOKENS_BELOW_CONVERSATION_LIMIT();
        }

        uint256 rLast = Helper.calculateLiquidityRatio(
            state.lastRewardAmount,
            state.lastMeAmount
        );

        uint256 needRewardAmount = Helper
            .determineRewardAmountForConversationGivenMeAmount(
                rLast,
                config.rOptimal,
                neededMeTokenAmount,
                state.lastMeAmount
            );
        if (actuallyDepositedRewardAmount < needRewardAmount) {
            revert Errors.INSUFFICENT_REWARD_AMOUNT_DEPOSITED_FOR_CONVERSATION();
        }

        Helper.transferMeTokens(
            state.meToken,
            info.listener,
            neededMeTokenAmount
        );

        OpenRewards.State memory poolTwoState = Controller(info.listener)
            .getOpenRewardsState();
        OpenRewards.Config memory poolTwoConfig = Controller(info.listener)
            .getOpenRewardsConfigurations();
        returnType = Params.InitiateConversationReturnType(
            state.lastMeAmount,
            state.lastRewardAmount,
            config.notifyRewardAmount,
            config.notifyMeAmount,
            poolTwoState.lastMeAmount,
            poolTwoState.lastRewardAmount,
            poolTwoConfig.notifyRewardAmount,
            poolTwoConfig.notifyMeAmount,
            Helper.makeConversation(info),
            state.depositNonce,
            poolTwoState.depositNonce
        );

        Helper.ensureIsWithinAcceptablePercentRange(
            info.expectedAmountOfOutputReward,
            returnType.outputRewardsAmount
        );

        uint256 excessDepositedRewardAmount = actuallyDepositedRewardAmount -
            needRewardAmount;
        if (excessDepositedRewardAmount > 0) {
            Helper.transferFungibleRewards(
                state.reward,
                info.requestor,
                excessDepositedRewardAmount
            );
        }

        (uint256 _currentRewardAmount, uint256 _currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(
                openRewardsId,
                state.reward,
                state.meToken
            );

        updateOpenRewardsState(
            state,
            config,
            _currentRewardAmount,
            _currentMeAmount,
            uint40(Context.timestamp())
        );
        state.busy = false;
    }

    function engageIncomingConversation(
        uint256 expectedRewardAmount,
        address outputRewardReceiver
    ) internal returns (uint256) {
        {
            Validator.ensureValueIsNotZero(expectedRewardAmount);
            Validator.ensureAddressIsNotZeroAddress(outputRewardReceiver);
        }
        Helper.ensureIsOpenRewardsConversation();
        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        if (!state.active) revert Errors.OPEN_REWARDS_IS_NOT_ACTIVE();

        address openRewardsId = address(this);

        uint256 currentMeAmount = Helper.objectivelyObtainSingleBalance(
            openRewardsId,
            state.meToken
        );

        uint256 meAmountFromConversation = currentMeAmount - state.lastMeAmount;

        uint256 outputRewardAmount = Helper
            .determineOptimalRewardAmountForConversationGivenMeAmount(
                config.rOptimal,
                meAmountFromConversation
            );

        Helper.ensureIsWithinAcceptablePercentRange(
            expectedRewardAmount,
            outputRewardAmount
        );

        if (
            expectedRewardAmount >
            state.lastRewardAmount - config.minimumRewardAmountForConversation
        ) {
            revert Errors
                .CONVERSATION_WILL_CAUSE_OPEN_REWARDS_TO_GO_OUT_OF_RANGE();
        }

        Helper.transferFungibleRewards(
            state.reward,
            outputRewardReceiver,
            outputRewardAmount
        );

        (uint256 _currentRewardAmount, uint256 _currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(
                openRewardsId,
                state.reward,
                state.meToken
            );

        updateOpenRewardsState(
            state,
            config,
            _currentRewardAmount,
            _currentMeAmount,
            uint40(Context.timestamp())
        );

        emit Logs.conversationMade(
            outputRewardAmount,
            expectedRewardAmount,
            outputRewardReceiver,
            Context.sender()
        );
        return outputRewardAmount;
    }

    function recordMeTokensProvidedByProtocolTeam(
        uint256 amount
    ) internal returns (uint256) {
        Validator.ensureValueIsNotZero(amount);

        address openRewardsId = address(this);
        address _requestor = Context.sender();
        uint256 _time = Context.timestamp();
        Guard.ensureAccountIsAuthorized(
            openRewardsId,
            SEEDS.PROTOCOL_ADMIN,
            _requestor
        );

        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        address OpenRewardsId = address(this);
        uint256 currentMeBalance = Helper.objectivelyObtainSingleBalance(
            OpenRewardsId,
            state.meToken
        );
        uint256 actualMeOffset = currentMeBalance - state.lastMeAmount;

        if (actualMeOffset < amount) {
            Helper.ensureIsWithinAcceptablePercentRange(amount, actualMeOffset);
        }

        (uint256 _currentRewardAmount, uint256 _currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(
                OpenRewardsId,
                state.reward,
                state.meToken
            );

        updateOpenRewardsState(
            state,
            config,
            _currentRewardAmount,
            _currentMeAmount,
            uint40(_time)
        );

        state.meTokensFromProtocolTeam += actualMeOffset;

        emit Logs.meTokensProvidedByProtocolTeam(actualMeOffset);

        return actualMeOffset;
    }

    function releaseMeTokensProvidedByProtocolTeam(
        uint256 amountToRelease,
        address to,
        bytes32 releaseType
    ) internal returns (uint256 rewardAmount, uint256 meAmount) {
        {
            Validator.ensureValueIsNotZero(amountToRelease);
            Validator.ensureAddressIsNotZeroAddress(to);
        }

        address openRewardsId = address(this);
        address _requestor = Context.sender();

        {
            Guard.ensureAccountIsAuthorized(
                openRewardsId,
                SEEDS.PROTOCOL_ADMIN,
                _requestor
            );
        }

        {
            (rewardAmount, meAmount) = releaseType ==
                Constants.WITH_REWARDS_IF_NEED_BE
                ? _withdrawProtocolMeOffsetWithRewardsIfNeedBe(
                    amountToRelease,
                    to
                )
                : _withdrawProtocolMeOffsetWithdrawable(amountToRelease, to);
        }

        emit Logs.meTokensRemovedByProtocolTeam(meAmount);
    }

    function changeOpenRewardsConfigExceptROptimal(
        Params.EditableConfigForTypeAOpenRewards memory editableConfig,
        bool ignoreDefault
    ) internal returns (bool) {
        address openRewardsId = address(this);
        address _requestor = Context.sender();

        Guard.ensureAccountIsAuthorized(
            openRewardsId,
            SEEDS.OPEN_REWARDS_MANAGER,
            _requestor
        );

        emit Logs.openRewardsConfigChanged(editableConfig, ignoreDefault);

        return
            ignoreDefault
                ? OpenRewards.updateConfigIgnoreDefault(editableConfig)
                : OpenRewards.updateConfig(editableConfig);
    }

    function changeROptimal(uint256 _newROptimal) internal returns (bool) {
        if (_newROptimal == 0) {
            revert Errors.R_OPTIMAL_CANNOT_BE_ZERO();
        }

        address openRewardsId = address(this);
        address _requestor = Context.sender();

        Guard.ensureAccountIsAuthorized(
            openRewardsId,
            SEEDS.OPEN_REWARDS_MANAGER,
            _requestor
        );

        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();

        // uint256 actualROptimalInPrecision = _newROptimal * Constants.PRECISION;
        uint256 actualROptimalInPrecision = _newROptimal; // with this r-optimal should come in in precision, it is more flexible this way

        (uint256 currentRewardAmount, uint256 currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(
                openRewardsId,
                state.reward,
                state.meToken
            );

        if (config.maximumRLimit < actualROptimalInPrecision) {
            revert Errors
                .MAXIMUM_REWARD_RATIO_CANNOT_BE_LESS_THAN_THE_OPTIMAL_RATIO();
        }

        if (
            Helper.calculateLiquidityRatio(
                currentRewardAmount,
                currentMeAmount
            ) > actualROptimalInPrecision
        ) {
            revert Errors
                .LIQUIDITY_RATIO_DURING_RESET_OF_OPTIMAL_RATIO_CANNOT_BE_GREATER_THAN_THE_OPTIMAL_RATIO();
        }

        config.rOptimal = actualROptimalInPrecision;

        emit Logs.optimalRatioChanged(
            config.rOptimal,
            actualROptimalInPrecision
        );

        return true;
    }

    function addOpenRewardsManager(address account) internal returns (bool) {
        {
            Validator.ensureAddressIsNotZeroAddress(account);
        }
        address openRewardsId = address(this);
        address _requestor = Context.sender();

        OpenRewards.State storage state = OpenRewards.writableState();
        if (_requestor != state.initiator) {
            revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
        }
        (bytes32 adminAccessKey, bytes32 accessKey) = Guard
            .synthesizeAdminAndAccessKeysForOpenRewards(
                state.initiator,
                openRewardsId,
                SEEDS.OPEN_REWARDS_MANAGER
            );

        emit Logs.openRewardsManagerAdded(account, _requestor);
        return Guard.grantAccessToAccount(accessKey, adminAccessKey, account);
    }

    function addLiquidityManager(address account) internal returns (bool) {
        {
            Validator.ensureAddressIsNotZeroAddress(account);
        }
        address openRewardsId = address(this);
        address _requestor = Context.sender();

        OpenRewards.State storage state = OpenRewards.writableState();

        if (_requestor != state.initiator) {
            revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
        }

        (bytes32 adminAccessKey, bytes32 accessKey) = Guard
            .synthesizeAdminAndAccessKeysForOpenRewards(
                state.initiator,
                openRewardsId,
                SEEDS.LIQUIDITY_MANAGER
            );

        emit Logs.liqudityManagerAdded(account, _requestor);
        return Guard.grantAccessToAccount(accessKey, adminAccessKey, account);
    }

    function removeOpenRewardsManager(
        address poolManager
    ) internal returns (bool) {
        Validator.ensureAddressIsNotZeroAddress(poolManager);

        address openRewardsId = address(this);
        address _requestor = Context.sender();

        OpenRewards.State storage state = OpenRewards.writableState();
        if (_requestor != state.initiator) {
            revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
        }
        (bytes32 adminAccessKey, bytes32 accessKey) = Guard
            .synthesizeAdminAndAccessKeysForOpenRewards(
                state.initiator,
                openRewardsId,
                SEEDS.OPEN_REWARDS_MANAGER
            );

        emit Logs.openRewardsManagerRemoved(poolManager, _requestor);
        return
            Guard.revokeAccessFromAccount(
                accessKey,
                adminAccessKey,
                poolManager
            );
    }

    function removeLiquidityManager(
        address liqudityManager
    ) internal returns (bool) {
        Validator.ensureAddressIsNotZeroAddress(liqudityManager);

        address openRewardsId = address(this);
        address _requestor = Context.sender();

        OpenRewards.State storage state = OpenRewards.writableState();
        if (_requestor != state.initiator) {
            revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
        }
        (bytes32 adminAccessKey, bytes32 accessKey) = Guard
            .synthesizeAdminAndAccessKeysForOpenRewards(
                state.initiator,
                openRewardsId,
                SEEDS.LIQUIDITY_MANAGER
            );

        emit Logs.liquidityManagerRemoved(liqudityManager, _requestor);
        return
            Guard.revokeAccessFromAccount(
                accessKey,
                adminAccessKey,
                liqudityManager
            );
    }

    function getPositionByIndex(
        uint256 _index,
        address _originalRequestor
    ) internal view returns (uint256 position) {
        {
            Validator.ensureAddressIsNotZeroAddress(_originalRequestor);
        }
        address openRewardsId = address(this);
        uint256 totalNumberOfPostion = NonFungible(openRewardsId).balanceOf(
            _originalRequestor
        );

        if (_index > totalNumberOfPostion) {
            revert Errors.INVALID_POSITION_INDEX();
        }

        position = NonFungible(openRewardsId).tokenOfOwnerByIndex(
            _originalRequestor,
            _index
        );
    }

    function getAllPositions(
        address _originalRequestor
    ) internal view returns (uint256[] memory) {
        Validator.ensureAddressIsNotZeroAddress(_originalRequestor);
        address openRewardsId = address(this);
        uint256 totalNumberOfPostion = NonFungible(openRewardsId).balanceOf(
            _originalRequestor
        );

        // if (totalNumberOfPostion == 0) {
        //     revert Errors.REQUESTOR_HAS_NO_POSITION();
        // }

        if (totalNumberOfPostion > 20) {
            revert Errors
                .POSITIONS_ARE_MORE_THAN_TWENTY_TRY_GETTING_THEM_ONE_AT_A_TIME();
        }

        uint256[] memory positionPlaceholder = new uint256[](
            totalNumberOfPostion
        );

        for (uint256 i; i < totalNumberOfPostion; ) {
            positionPlaceholder[i] = NonFungible(openRewardsId)
                .tokenOfOwnerByIndex(_originalRequestor, i);
            ++i;
        }

        return positionPlaceholder;
    }

    function implementsOpenReward() internal pure returns (bytes4) {
        return Constants.OPEN_REWARDS;
    }

    function leverageOutgoingConversationInsight(
        uint256 meAmount,
        uint256 slippageInPrecision
    ) internal view returns (uint256 rewardAmount) {
        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        uint256 lastRewardAmount = state.lastRewardAmount;
        uint256 lastMeAmount = state.lastMeAmount;
        uint256 rOptimal = config.rOptimal;
        if (slippageInPrecision == 0) {
            slippageInPrecision = config.defaultSlippageInPrecision;
        }
        uint256 rLast = Helper.calculateLiquidityRatio(
            lastRewardAmount,
            lastMeAmount
        );
        uint256 exactRewardAmount = Helper
            .determineRewardAmountForConversationGivenMeAmount(
                rLast,
                rOptimal,
                meAmount,
                lastMeAmount
            );
        if (
            exactRewardAmount >
            state.lastRewardAmount - config.minimumRewardAmountForConversation
        ) {
            revert Errors
                .CONVERSATION_WILL_CAUSE_OPEN_REWARDS_TO_GO_OUT_OF_RANGE();
        }
        rewardAmount = Helper.determineRewardAmountForConversationGivenMeAmount(
                rLast,
                rOptimal,
                meAmount,
                lastMeAmount
            );
    }

    function leverageIncomingConversationInsight(
        uint256 rewardAmount
    ) internal view returns (uint256 optimalMeAmount, uint256 rOptimal) {
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        OpenRewards.State storage state = OpenRewards.writableState();
        rOptimal = config.rOptimal;
        optimalMeAmount = Helper
            .determineOptimalMeAmountForConversationGivenRewardAmount(
                rOptimal,
                rewardAmount
            );
        if (
            rewardAmount >
            state.lastRewardAmount - config.minimumRewardAmountForConversation
        ) {
            revert Errors
                .CONVERSATION_WILL_CAUSE_OPEN_REWARDS_TO_GO_OUT_OF_RANGE();
        }
    }

    function getOutgoingConversationInsight(
        uint256 meAmount,
        uint256 slippageInPrecision
    ) internal view returns (uint256 rewardAmount, uint256 lastRewardAmount) {
        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        lastRewardAmount = state.lastRewardAmount;
        uint256 lastMeAmount = state.lastMeAmount;
        uint256 rOptimal = config.rOptimal;
        if (slippageInPrecision == 0) {
            slippageInPrecision = config.defaultSlippageInPrecision;
        }
        uint256 rLast = Helper.calculateLiquidityRatio(
            lastRewardAmount,
            lastMeAmount
        );
        rewardAmount = Helper.determineRewardAmountForConversationGivenMeAmount(
                rLast,
                rOptimal,
                meAmount,
                lastMeAmount
            );
    }

    function getIncomingConversationInsight(
        uint256 rewardAmount
    )
        internal
        view
        returns (uint256 optimalMeAmount, uint256 lastRewardAmount)
    {
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        OpenRewards.State storage state = OpenRewards.writableState();
        optimalMeAmount = Helper
            .determineOptimalMeAmountForConversationGivenRewardAmount(
                config.rOptimal,
                rewardAmount
            );
        lastRewardAmount = state.lastRewardAmount;
    }

    // =========================================================
    // FUNCTIONS INTERNALS
    // =========================================================

    function updateOpenRewardsState(
        OpenRewards.State storage state,
        OpenRewards.Config storage config,
        uint256 _currentRewardAmount,
        uint256 _currentMeAmount,
        uint40 _transactionTime
    ) internal {
        uint256 r = Helper.calculateLiquidityRatio(
            _currentRewardAmount,
            _currentMeAmount
        );
        Validator.ensureRIsWithinAcceptableRange(r, config.maximumRLimit);

        state.lastRewardAmount = _currentRewardAmount;
        state.lastMeAmount = _currentMeAmount;
        state.lastTransactionTime = _transactionTime;

        emit Logs.liquidityUpdated(
            _currentRewardAmount,
            _currentMeAmount,
            _transactionTime
        );
    }

    function validateDepositRequest(
        address _reward,
        address _meToken,
        address _OpenRewardsId,
        uint256 _rewardAmount,
        uint256 _meAmount,
        uint256 _lastRewardAmount,
        uint256 _lastMeAmount
    ) internal view returns (uint256, uint256, uint256, uint256) {
        (uint256 _currentRewardAmount, uint256 _currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(_OpenRewardsId, _reward, _meToken);

        uint256 addedRewardAmount = _currentRewardAmount - _lastRewardAmount;
        uint256 addedMeAmount = _currentMeAmount - _lastMeAmount;

        if (addedRewardAmount < _rewardAmount) {
            revert Errors.REQUEST_IS_NOT_WITHIN_ACCEPTED_RANGE();
        }

        if (addedMeAmount < _meAmount) {
            revert Errors.REQUEST_IS_NOT_WITHIN_ACCEPTED_RANGE();
        }

        return (
            _currentRewardAmount,
            _currentMeAmount,
            addedRewardAmount,
            addedMeAmount
        );
    }

    function rewardWithdrawalTypeA(
        uint256 _positionRewardAmount
    ) internal pure returns (uint256, uint256) {
        return (_positionRewardAmount, 0);
    }

    function rewardWithdrawalTypeC(
        uint256 _positionRewardAmount,
        uint256 _currentRewardAmount,
        uint256 _minimumRewardAmountForConversation,
        uint256 _minimumMeAmountForConversation,
        uint256 _currentMeAmount,
        uint256 _protocolMeOffset,
        uint256 _rOptimal
    ) internal pure returns (uint256, uint256) {
        (uint256 base, string memory errorCode) = _protocolMeOffset >
            _minimumMeAmountForConversation
            ? (_protocolMeOffset, "PROTOCOL_OFFSET_MUST_BE_CONSIDERED")
            : (
                _minimumMeAmountForConversation,
                "ACTION_WILL_TAKE_OpenRewards_ME_TOKENS_BELOW_CONVERSATION_LIMIT"
            );

        if (_currentMeAmount > base) {
            revert Errors.REWARD_WITHDRAWAL_TYPE_C_ERROR(errorCode);
        }

        uint256 rewardAmountToWithdraw = _currentRewardAmount -
            _minimumRewardAmountForConversation;
        uint256 meAmountToWithdraw = ((_positionRewardAmount -
            rewardAmountToWithdraw) * Constants.PRECISION) / _rOptimal;

        if (_currentMeAmount - meAmountToWithdraw < base) {
            revert Errors.REWARD_WITHDRAWAL_TYPE_C_ERROR(errorCode);
        }

        return (rewardAmountToWithdraw, meAmountToWithdraw);
    }

    function meWithdrawalTypeA(
        uint256 _positionMeAmount
    ) internal pure returns (uint256, uint256) {
        return (0, _positionMeAmount);
    }

    function meWithdrawalTypeC(
        uint256 _positionMeAmount,
        uint256 _currentMeAmount,
        uint256 _currentRewardAmount,
        uint256 _minimumRewardAmountForConversation,
        uint256 _base,
        uint256 _rOptimal
    ) internal pure returns (uint256, uint256) {
        uint256 meAmountToWithdraw = 0;
        uint256 rewardAmountToWithdraw = 0;

        if (_currentMeAmount < _base) {
            rewardAmountToWithdraw =
                (_positionMeAmount * _rOptimal) /
                Constants.PRECISION;

            if (
                _currentRewardAmount - rewardAmountToWithdraw <
                _minimumRewardAmountForConversation
            ) {
                revert Errors
                    .ACTION_WOULD_TAKE_POOL_REWARDS_BELOW_CONVERSATION_LIMIT();
            }
        } else {
            meAmountToWithdraw = _currentMeAmount - _base;
            rewardAmountToWithdraw =
                ((_positionMeAmount - meAmountToWithdraw) * _rOptimal) /
                Constants.PRECISION;

            if (
                _currentRewardAmount - rewardAmountToWithdraw <
                _minimumRewardAmountForConversation
            ) {
                revert Errors
                    .ACTION_WOULD_TAKE_POOL_REWARDS_BELOW_CONVERSATION_LIMIT();
            }
        }

        return (rewardAmountToWithdraw, meAmountToWithdraw);
    }

    function obtainAmountOfLiquidityToWithdraw(
        Params.ExtendedLiquidityInfo memory _params
    ) internal pure returns (uint256, uint256) {
        uint256 rewardAmountToWithdrawA;
        uint256 rewardAmountToWithdrawB;
        uint256 meAmountToWithdrawA;
        uint256 meAmountToWithdrawB;

        uint256 base = _params.meTokensFromProtocolTeam >
            _params.minimumMeAmountForConversation
            ? (_params.meTokensFromProtocolTeam)
            : (_params.minimumMeAmountForConversation);

        if (
            _params.currentRewardAmount < _params.rewardAmount ||
            _params.currentRewardAmount - _params.rewardAmount <
            _params.minimumRewardAmountForConversation
        ) {
            (
                rewardAmountToWithdrawA,
                meAmountToWithdrawA
            ) = rewardWithdrawalTypeC(
                _params.rewardAmount,
                _params.currentRewardAmount,
                _params.minimumRewardAmountForConversation,
                _params.minimumMeAmountForConversation,
                _params.currentMeAmount,
                _params.meTokensFromProtocolTeam,
                _params.rOptimal
            );
        } else {
            (
                rewardAmountToWithdrawA,
                meAmountToWithdrawA
            ) = rewardWithdrawalTypeA(_params.rewardAmount);
        }

        if (
            _params.currentMeAmount < _params.meAmount ||
            _params.currentRewardAmount - _params.meAmount < base
        ) {
            (rewardAmountToWithdrawB, meAmountToWithdrawB) = meWithdrawalTypeC(
                _params.meAmount,
                _params.currentMeAmount,
                _params.currentRewardAmount,
                _params.minimumRewardAmountForConversation,
                base,
                _params.rOptimal
            );
        } else {
            (rewardAmountToWithdrawB, meAmountToWithdrawB) = meWithdrawalTypeA(
                _params.meAmount
            );
        }

        return (
            rewardAmountToWithdrawA + rewardAmountToWithdrawB,
            meAmountToWithdrawA + meAmountToWithdrawB
        );
    }

    function _withDrawProtocolMeOffset(
        uint256 _meAmountToWithdraw,
        address to,
        uint256 time
    ) internal returns (uint256, uint256) {
        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();
        if (state.busy) revert Errors.POOL_IS_BUSY();
        state.busy = true;
        address OpenRewardsId = address(this);
        uint256 currentMeOffset = state.meTokensFromProtocolTeam;

        if (_meAmountToWithdraw > currentMeOffset) {
            revert Errors.EXPECTED_PROTOCOL_ME_OFFSET_EXCEEDS_ACTUAL_ME_OFFSET();
        }

        uint256 currentMeAmount = Helper.objectivelyObtainSingleBalance(
            OpenRewardsId,
            state.meToken
        );

        if (currentMeAmount < config.minimumMeAmountForConversation) {
            revert Errors.LIQUIDITY_IS_CURRENTLY_BELOW_CONVERSATION_LIMIT();
        }

        if (
            currentMeAmount < _meAmountToWithdraw ||
            currentMeAmount - _meAmountToWithdraw <
            config.minimumMeAmountForConversation
        ) {
            revert Errors
                .ACTION_WOULD_TAKE_POOL_REWARDS_BELOW_CONVERSATION_LIMIT();
        }

        state.meTokensFromProtocolTeam -= _meAmountToWithdraw;

        Helper.transferMeTokens(state.meToken, to, _meAmountToWithdraw);

        (uint256 currentRewardAmount, uint256 currentMeAmount_) = Helper
            .objectivelyObtainDoubleBalances(
                OpenRewardsId,
                state.reward,
                state.meToken
            );

        updateOpenRewardsState(
            state,
            config,
            currentRewardAmount,
            currentMeAmount_,
            uint40(time)
        );
        state.busy = false;
        return (0, _meAmountToWithdraw);
    }

    function _withdrawProtocolMeOffsetWithRewardsIfNeedBe(
        uint256 _meAmountToWithdraw,
        address to
    ) internal returns (uint256, uint256) {
        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();

        if (state.busy) revert Errors.POOL_IS_BUSY();
        state.busy = true;

        address OpenRewardsId = address(this);

        uint256 withdrawableMeAmount;
        uint256 withdrawableRewardAmount;
        uint256 currentMeOffset = state.meTokensFromProtocolTeam;

        if (_meAmountToWithdraw > currentMeOffset) {
            revert Errors.EXPECTED_PROTOCOL_ME_OFFSET_EXCEEDS_ACTUAL_ME_OFFSET();
        }

        (uint256 currentRewardAmount, uint256 currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(
                OpenRewardsId,
                state.reward,
                state.meToken
            );

        if (currentMeAmount < config.minimumMeAmountForConversation) {
            revert Errors.LIQUIDITY_IS_CURRENTLY_BELOW_CONVERSATION_LIMIT();
        }

        if (
            currentMeAmount <= _meAmountToWithdraw ||
            currentMeAmount - _meAmountToWithdraw <
            config.minimumMeAmountForConversation
        ) {
            withdrawableMeAmount =
                currentMeAmount -
                config.minimumMeAmountForConversation;
            uint256 rewardAmount = Helper
                .determineOptimalRewardAmountForConversationGivenMeAmount(
                    config.rOptimal,
                    _meAmountToWithdraw - withdrawableMeAmount
                );

            if (
                currentRewardAmount > config.minimumRewardAmountForConversation
            ) {
                if (currentRewardAmount <= rewardAmount) {
                    withdrawableRewardAmount =
                        currentRewardAmount -
                        config.minimumRewardAmountForConversation;
                } else {
                    withdrawableRewardAmount = rewardAmount;
                }
            }
        } else {
            withdrawableMeAmount = _meAmountToWithdraw;
            withdrawableRewardAmount = 0;
        }
        state.meTokensFromProtocolTeam =
            state.meTokensFromProtocolTeam -
            withdrawableMeAmount;

        if (withdrawableMeAmount != 0) {
            Helper.transferMeTokens(state.meToken, to, withdrawableMeAmount);
        }

        if (withdrawableRewardAmount != 0) {
            Helper.transferFungibleRewards(
                state.reward,
                to,
                withdrawableRewardAmount
            );
        }
        (currentRewardAmount, currentMeAmount) = Helper
            .objectivelyObtainDoubleBalances(
                OpenRewardsId,
                state.reward,
                state.meToken
            );

        {
            updateOpenRewardsState(
                state,
                config,
                currentRewardAmount,
                currentMeAmount,
                uint40(Context.timestamp())
            );

            state.busy = false;
        }

        return (withdrawableRewardAmount, withdrawableMeAmount);
    }

    function _forcefullywithdrawProtocolOffsetMeTokens(
        uint256 _meAmountToWithdraw,
        address to,
        uint256 time
    ) internal returns (uint256, uint256) {
        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();

        if (state.busy) revert Errors.POOL_IS_BUSY();
        state.busy = true;

        address OpenRewardsId = address(this);
        uint256 withdrawableMeAmount;
        uint256 currentMeOffset = state.meTokensFromProtocolTeam;

        if (_meAmountToWithdraw > currentMeOffset) {
            revert Errors.EXPECTED_PROTOCOL_ME_OFFSET_EXCEEDS_ACTUAL_ME_OFFSET();
        }

        uint256 currentMeAmount = Helper.objectivelyObtainSingleBalance(
            OpenRewardsId,
            state.meToken
        );

        if (_meAmountToWithdraw > currentMeAmount) {
            withdrawableMeAmount = currentMeAmount;
        } else {
            withdrawableMeAmount = _meAmountToWithdraw;
        }

        if (withdrawableMeAmount != 0) {
            Helper.transferMeTokens(state.meToken, to, withdrawableMeAmount);
        }

        state.meTokensFromProtocolTeam =
            state.meTokensFromProtocolTeam -
            withdrawableMeAmount;

        (uint256 currentRewardAmount, uint256 currentMeAmount_) = Helper
            .objectivelyObtainDoubleBalances(
                OpenRewardsId,
                state.reward,
                state.meToken
            );

        updateOpenRewardsState(
            state,
            config,
            currentRewardAmount,
            currentMeAmount_,
            uint40(time)
        );

        state.busy = false;
        return (0, withdrawableMeAmount);
    }

    function _withdrawProtocolMeOffsetWithdrawable(
        uint256 _meAmountToWithdraw,
        address to
    ) internal returns (uint256, uint256) {
        OpenRewards.State storage state = OpenRewards.writableState();
        OpenRewards.Config storage config = OpenRewards.writableConfig();

        if (state.busy) revert Errors.POOL_IS_BUSY();
        state.busy = true;

        address OpenRewardsId = address(this);
        uint256 withdrawableAmount;
        uint256 currentMeOffset = state.meTokensFromProtocolTeam;

        if (_meAmountToWithdraw > currentMeOffset) {
            revert Errors.EXPECTED_PROTOCOL_ME_OFFSET_EXCEEDS_ACTUAL_ME_OFFSET();
        }

        uint256 currentMeAmount = Helper.objectivelyObtainSingleBalance(
            OpenRewardsId,
            state.meToken
        );

        if (currentMeAmount < config.minimumMeAmountForConversation) {
            revert Errors.LIQUIDITY_IS_CURRENTLY_BELOW_CONVERSATION_LIMIT();
        }

        if (
            currentMeAmount <= _meAmountToWithdraw ||
            currentMeAmount - _meAmountToWithdraw <
            config.minimumMeAmountForConversation
        ) {
            withdrawableAmount =
                currentMeAmount -
                config.minimumMeAmountForConversation;
        } else {
            withdrawableAmount = _meAmountToWithdraw;
        }
        state.meTokensFromProtocolTeam -= withdrawableAmount;
        {
            Helper.transferMeTokens(state.meToken, to, withdrawableAmount);
            (uint256 currentRewardAmount, uint256 currentMeAmount_) = Helper
                .objectivelyObtainDoubleBalances(
                    OpenRewardsId,
                    state.reward,
                    state.meToken
                );

            updateOpenRewardsState(
                state,
                config,
                currentRewardAmount,
                currentMeAmount_,
                uint40(Context.timestamp())
            );
        }

        state.busy = false;
        return (0, withdrawableAmount);
    }
}

File 3 of 30 : enumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.20;

import {ERC721 as NonFungible, IERC165} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {Errors} from "../../providers/common/errors.sol";

abstract contract NonFungibleAndEnumerable is NonFungible, IERC721Enumerable {
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    mapping(uint256 => uint256) private _ownedTokensIndex;

    uint256[] private _allTokens;

    mapping(uint256 => uint256) private _allTokensIndex;

    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, NonFungible) returns (bool) {
        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < NonFungible.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    function totalSupply() public view virtual override returns (uint256) {
        return _allTokens.length;
    }

    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < NonFungibleAndEnumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize)
        internal
        virtual
        override
    {
        super._beforeTokenTransfer(from, to, firstTokenId, batchSize);

        if (batchSize > 1) {
            revert("ERC721Enumerable: consecutive transfers not supported");
        }

        uint256 tokenId = firstTokenId;

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = NonFungible.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        uint256 lastTokenIndex = NonFungible.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId;
            _ownedTokensIndex[lastTokenId] = tokenIndex;
        }

        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId;
        _allTokensIndex[lastTokenId] = tokenIndex;

        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }

    ///added Logic

    function ensureRequestorIsPositionOwner(uint256 position, address requestor) external view returns (uint256) {
        if (_ownerOf(position) == address(0)) revert Errors.INVALID_POSITION();
        if (requestor != _ownerOf(position)) {
            revert Errors.REQUESTOR_IS_NOT_OWNER_OF_POSITION();
        }
        return position;
    }
}

File 4 of 30 : eunice.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Validator, Errors} from "./validators.sol";
import {Constants} from "./constants.sol";
import {FungibleController as Fungible} from "../../controllers/standards/fungible.controller.sol";
import {Params} from "./params.sol";

library Eunice {
    function calculateLiquidityRatio(uint256 _rewardAmount, uint256 _meAmount) internal pure returns (uint256 ratio) {
        Validator.ensureValueIsNotZero(_meAmount);
        ratio = (_rewardAmount * Constants.PRECISION) / _meAmount;
    }

    function objectivelyObtainSingleBalance(address _pool, address _reward) internal view returns (uint256 bal) {
        bal = Fungible(_reward).balanceOf(_pool);
    }

    function objectivelyObtainDoubleBalances(address _pool, address _reward, address _me)
        internal
        view
        returns (uint256 rewardAmount, uint256 meAmount)
    {
        rewardAmount = Fungible(_reward).balanceOf(_pool);
        meAmount = Fungible(_me).balanceOf(_pool);
    }

    function objectivelyObtainLiquidityRatio(address _pool, address _reward, address _me)
        internal
        view
        returns (uint256 ratio)
    {
        (uint256 a, uint256 b) = objectivelyObtainDoubleBalances(_pool, _reward, _me);
        ratio = calculateLiquidityRatio(a, b);
    }

    function calculateExchangeRatio(uint256 rLast, uint256 percentageChange, uint256 rOptimal)
        internal
        pure
        returns (uint256 exchangeRatio)
    {
        exchangeRatio = (rLast * (1 * Constants.PRECISION + (Constants.EXCHANGE_SLOPE * percentageChange) / 100))
            / Constants.PRECISION;
        exchangeRatio = exchangeRatio < rOptimal ? rOptimal : exchangeRatio;
    }

    function determineRewardAmountForConversationGivenMeAmount(
        uint256 _rLast,
        uint256 _rOptimal,
        uint256 _meAmount,
        uint256 _lastMeAmount
    ) internal pure returns (uint256 t) {
        uint256 percentageChange = (_meAmount * 100) / _lastMeAmount;
        uint256 actualReward =
            (_meAmount * calculateExchangeRatio(_rLast, percentageChange, _rOptimal)) / Constants.PRECISION;
        t = actualReward;
    }

    function determineOptimalMeAmountForConversationGivenRewardAmount(uint256 _rOptimal, uint256 _rewardAmount)
        internal
        pure
        returns (uint256 t)
    {
        Validator.ensureValueIsNotZero(_rOptimal);
        t = (_rewardAmount * Constants.PRECISION) / _rOptimal;
    }

    function determineOptimalRewardAmountForConversationGivenMeAmount(uint256 _rOptimal, uint256 _meAmount)
        internal
        pure
        returns (uint256 t)
    {
        t = (_meAmount * _rOptimal) / Constants.PRECISION;
    }

    function checkIfWithinAcceptableSlippageRange(
        uint256 _obtainedValue,
        uint256 _actualValue,
        uint256 _slippageInPrecision
    ) internal pure returns (bool t) {
        Validator.ensureValueIsNotZero(_actualValue);

        uint256 a = _obtainedValue > _actualValue ? (_obtainedValue - _actualValue) : (_actualValue - _obtainedValue);
        t = (a * Constants.PRECISION) / _actualValue <= (Constants.PRECISION * _slippageInPrecision) / 100;
    }

    function checkIfWithinAcceptablePercentRange(uint256 _obtainedValue, uint256 _actualValue)
        internal
        pure
        returns (bool t)
    {
        Validator.ensureValueIsNotZero(_actualValue);

        uint256 a = _obtainedValue > _actualValue ? (_obtainedValue - _actualValue) : (_actualValue - _obtainedValue);

        t = (a * Constants.PRECISION) / _actualValue <= Constants.PRECISION / 100;
    }

    function ensureIsWithinAcceptablePercentRange(uint256 _obtainedValue, uint256 _actualValue) internal pure {
        if (!checkIfWithinAcceptablePercentRange(_obtainedValue, _actualValue)) {
            revert Errors.REQUEST_IS_NOT_WITHIN_ACCEPTED_RANGE();
        }
    }

    function ensureIsWithinAcceptableSlippageRange(uint256 _obtainedValue, uint256 _actualValue, uint256 _slippage)
        internal
        pure
    {
        if (!checkIfWithinAcceptableSlippageRange(_obtainedValue, _actualValue, _slippage)) {
            revert Errors.REQUEST_NOT_WITHIN_SLIPPAGE_RANGE();
        }
    }

    function transferFungibleRewards(address _erc20address, address _to, uint256 _amount) internal {
        (bool success, bytes memory data) =
            _erc20address.call(abi.encodeWithSelector(bytes4(keccak256("transfer(address,uint256)")), _to, _amount));

        if (!(success && (data.length == 0 || abi.decode(data, (bool))))) {
            revert Errors.REWARDS_TRANSFER_FAILED();
        }
    }

    function approveFungibleRewards(address _erc20address, address _to, uint256 _amount) internal {
        (bool success, bytes memory data) =
            _erc20address.call(abi.encodeWithSelector(bytes4(keccak256("approve(address,uint256)")), _to, _amount));

        if (!(success && (data.length == 0 || abi.decode(data, (bool))))) {
            revert Errors.REWARDS_APPROVAL_FAILED();
        }
    }

    function transferMeTokens(address _erc20address, address _to, uint256 _amount) internal {
        (bool success, bytes memory data) =
            _erc20address.call(abi.encodeWithSelector(bytes4(keccak256("transfer(address,uint256)")), _to, _amount));

        if (!(success && (data.length == 0 || abi.decode(data, (bool))))) {
            revert Errors.REWARDS_TRANSFER_FAILED();
        }
    }

    function transferFungibleRewardsFromAccount(address _rewardAddress, address _from, address _to, uint256 _amount)
        internal
    {
        (bool success, bytes memory data) = _rewardAddress.call(
            abi.encodeWithSelector(bytes4(keccak256("transferFrom(address,address,uint256)")), _from, _to, _amount)
        );

        if (!(success && (data.length == 0 || abi.decode(data, (bool))))) {
            revert Errors.REWARDS_TRANSFER_FAILED();
        }
    }

    function transferFungibleRewardsFromAccountWithPermit(
        address _rewardAddress,
        address _from,
        address _to,
        uint256 _amount,
        Params.PermitParam memory permitParam
    ) internal {
        (bool success_p, bytes memory data_p) = _rewardAddress.call(
            abi.encodeWithSelector(
                bytes4(keccak256("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)")),
                _from,
                address(this),
                _amount,
                permitParam._deadline,
                permitParam._v,
                permitParam._r,
                permitParam._s
            )
        );

        if (!(success_p && (data_p.length == 0 || abi.decode(data_p, (bool))))) {
            revert Errors.REWARDS_PERMIT_FAILED();
        }

        (bool success, bytes memory data) = _rewardAddress.call(
            abi.encodeWithSelector(bytes4(keccak256("transferFrom(address,address,uint256)")), _from, _to, _amount)
        );

        if (!(success && (data.length == 0 || abi.decode(data, (bool))))) {
            revert Errors.REWARDS_TRANSFER_FAILED();
        }
    }

    function transferMeTokensFromAccount(address _meAddress, address _from, address _to, uint256 _amount) internal {
        (bool success, bytes memory data) = _meAddress.call(
            abi.encodeWithSelector(bytes4(keccak256("transferFrom(address,address,uint256)")), _from, _to, _amount)
        );

        if (!(success && (data.length == 0 || abi.decode(data, (bool))))) {
            revert Errors.REWARDS_TRANSFER_FAILED();
        }
    }

    function takeSlippageInPercentageToPrecision(uint256 slippage) internal pure returns (uint256) {
        return (slippage * Constants.PRECISION) / Constants.PERCENTAGE;
    }

    // function makeConversation(address listener, uint256 outputRewardAmount, address receiver)
    function makeConversation(Params.OutgoingConversationInfo memory info) internal returns (uint256 result) {
        bytes memory tre = abi.encodeWithSelector(
            bytes4(keccak256("engageIncomingConversation(uint256,address)")),
            info.expectedAmountOfOutputReward,
            info.outputRewardReceiver
        );

        // bytes memory tre = abi.encodeWithSignature("engageIncomingConversation(uint256,address)", outputRewardAmount, receiver);

        bytes memory conversationData = abi.encodePacked(tre, Constants.OPEN_REWARDS);

        (bool success, bytes memory returnData) = info.listener.call(conversationData);

        if (!success) revert Errors.CONVERSATION_FAILED();
        result = abi.decode(returnData, (uint256));
    }

    function ensureIsOpenRewardsConversation() internal pure {
        bytes4 data;
        assembly {
            data := calldataload(68) // 68 is the offset to where the extra data starts
        }

        if (data != Constants.OPEN_REWARDS) {
            revert Errors.NOT_OPEN_REWARDS_CONVERSATION();
        }
    }

    function addBountyContribution(uint256 rewardAmountForConversation, uint256 bountyContributionInPrecision)
        internal
        pure
        returns (uint256)
    {
        uint256 actualBountyContribution =
            (bountyContributionInPrecision * rewardAmountForConversation) / Constants.PRECISION;
        return actualBountyContribution + rewardAmountForConversation;
    }
}

File 5 of 30 : context.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Database} from "./database.sol";

library Context {
    struct ContextData {
        mapping(address => bool) isTrustedForwarder;
    }

    function writeableStorage() internal pure returns (ContextData storage ds) {
        bytes32 position = Database.CONTEXT_RECORD;

        assembly {
            ds.slot := position
        }
    }

    // This function must be called on diamnd init to set the trusted forwarder
    function setTrustedForwarder(address forwarder) internal {
        writeableStorage().isTrustedForwarder[forwarder] = true;
    }

    function isTrustedForwarder(address forwarder) internal view returns (bool) {
        return writeableStorage().isTrustedForwarder[forwarder];
    }

    function sender() internal view returns (address senderAddress) {
        if (isTrustedForwarder(msg.sender)) {
            assembly {
                senderAddress := shr(96, calldataload(sub(calldatasize(), 20)))
            }
        } else {
            return msg.sender;
        }
    }

    function data() internal view returns (bytes calldata) {
        if (isTrustedForwarder(msg.sender)) {
            return msg.data[:msg.data.length - 20];
        } else {
            return msg.data;
        }
    }

    function timestamp() internal view returns (uint256) {
        return block.timestamp;
    }

    function blocks() internal view returns (uint256) {
        return block.number;
    }
}

File 6 of 30 : a-pool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Positions} from "./a-position.sol";
import {Database, Params, Validator, Errors, Constants} from "../common/database.sol";

library OpenRewardsData {
    struct State {
        bool initialized;
        bool started;
        bool active;
        bool busy;
        address initiator;
        address reward;
        address meToken;
        uint256 lastRewardAmount;
        uint256 lastMeAmount;
        uint256 meTokensFromProtocolTeam;
        uint256 depositNonce;
        uint40 lastTransactionTime;
    }

    struct Config {
        uint256 rOptimal;
        uint256 maximumRLimit;
        uint256 minimumRewardAmountForConversation;
        uint256 minimumMeAmountForConversation;
        uint256 notifyRewardAmount;
        uint256 notifyMeAmount;
        uint256 defaultSlippageInPrecision;
        bool allowSwaps;
    }

    function writableState() internal pure returns (State storage state) {
        bytes32 position = Database.OPEN_REWARDS_STATE_TYPE_A;
        assembly {
            state.slot := position
        }
    }

    function writableConfig() internal pure returns (Config storage config) {
        bytes32 position = Database.OPEN_REWARDS_CONFIG_TYPE_A;
        assembly {
            config.slot := position
        }
    }

    function writablePositions()
        internal
        pure
        returns (Positions.Position storage positions)
    {
        bytes32 position = Database.OPEN_REWARDS_POSITIONS_TYPE_A;
        assembly {
            positions.slot := position
        }
    }

    function getDepositNonce() internal view returns (uint256 nonce) {
        State storage state = writableState();
        nonce = state.depositNonce;
    }

    function incrementDepositNonce() internal returns (uint256 nonce) {
        State storage state = writableState();
        nonce = state.depositNonce + 1;
        state.depositNonce = nonce;
    }

    function setBusy() internal returns (bool) {
        State storage state = writableState();
        return state.busy = true;
    }

    function createNewPosition(
        uint256 rewardAmount,
        uint256 meAmount
    ) internal returns (uint256 newPosition) {
        Positions.Position storage position = writablePositions();
        uint256 currentPosition = position.lastPosition;
        newPosition = currentPosition + 1;
        position.lastPosition = newPosition;
        position.positionMetadata[newPosition] = Positions.PositionMetadata(
            rewardAmount,
            meAmount
        );
    }

    function addToPosition(
        uint256 _position,
        uint256 rewardAmount,
        uint256 meAmount
    ) internal returns (uint256) {
        Positions.Position storage position = writablePositions();
        if (_position > position.lastPosition) revert Errors.INVALID_POSITION();
        Positions.PositionMetadata memory currentMetadata = position
            .positionMetadata[_position];
        position.positionMetadata[_position] = Positions.PositionMetadata(
            currentMetadata.rewardPosition + rewardAmount,
            currentMetadata.mePosition + meAmount
        );
        return _position;
    }

    function removeFromPosition(
        uint256 _position,
        uint256 rewardAmount,
        uint256 meAmount
    ) internal returns (uint256) {
        Positions.Position storage position = writablePositions();
        if (_position > position.lastPosition) revert Errors.INVALID_POSITION();
        Positions.PositionMetadata memory currentMetadata = position
            .positionMetadata[_position];
        if (currentMetadata.rewardPosition < rewardAmount) {
            revert Errors.INSUFFICIENT_REWARD_POSITION();
        }
        if (currentMetadata.mePosition < meAmount) {
            revert Errors.INSUFFICIENT_ME_POSITION();
        }
        position.positionMetadata[_position] = Positions.PositionMetadata(
            currentMetadata.rewardPosition - rewardAmount,
            currentMetadata.mePosition - meAmount
        );
        return _position;
    }

    function ensurePositionExists(
        uint256 _position
    ) internal view returns (uint256) {
        Positions.Position storage position = writablePositions();
        if (_position > position.lastPosition) revert Errors.INVALID_POSITION();
        return _position;
    }

    function getPositionData(
        uint256 _position
    ) internal view returns (Positions.PositionMetadata memory data) {
        Positions.Position storage position = writablePositions();
        if (_position > position.lastPosition) revert Errors.INVALID_POSITION();
        data = position.positionMetadata[_position];
    }

    function updateConfig(
        Params.EditableConfigForTypeAOpenRewards memory _config
    ) internal returns (bool) {
        Config storage config = writableConfig();
        config.maximumRLimit = _config.maximumRLimit;
        config.minimumRewardAmountForConversation = _config
            .minimumRewardAmountForConversation;
        config.minimumMeAmountForConversation = _config
            .minimumMeAmountForConversation;
        config.notifyRewardAmount = _config.notifyRewardAmount;
        config.notifyMeAmount = _config.notifyMeAmount;
        config.defaultSlippageInPrecision = _config.defaultSlippageInPrecision;
        config.allowSwaps = _config.allowSwaps;
        return true;
    }

    function updateConfigIgnoreDefault(
        Params.EditableConfigForTypeAOpenRewards memory _config
    ) internal returns (bool) {
        Config storage config = writableConfig();
        if (!Validator.isZero(_config.maximumRLimit)) {
            config.maximumRLimit = _config.maximumRLimit;
        }
        if (!Validator.isZero(_config.minimumRewardAmountForConversation)) {
            config.minimumRewardAmountForConversation = _config
                .minimumRewardAmountForConversation;
        }
        if (!Validator.isZero(_config.minimumMeAmountForConversation)) {
            config.minimumMeAmountForConversation = _config
                .minimumMeAmountForConversation;
        }
        if (!Validator.isZero(_config.notifyRewardAmount)) {
            config.notifyRewardAmount = _config.notifyRewardAmount;
        }
        if (!Validator.isZero(_config.notifyMeAmount)) {
            config.notifyMeAmount = _config.notifyMeAmount;
        }
        if (!Validator.isZero(_config.defaultSlippageInPrecision)) {
            config.defaultSlippageInPrecision = _config
                .defaultSlippageInPrecision;
        }
        if (!Validator.isFalse(_config.allowSwaps)) {
            config.allowSwaps = _config.allowSwaps;
        }
        return true;
    }

    function setUpConfig(
        Params.ConfigForTypeAOpenRewards memory _config
    ) internal returns (bool) {
        Config storage config = writableConfig();
        config.rOptimal = _config.rOptimal;
        config.maximumRLimit = _config.maximumRLimit;
        config.minimumRewardAmountForConversation = _config
            .minimumRewardAmountForConversation;
        config.minimumMeAmountForConversation = _config
            .minimumMeAmountForConversation;
        config.notifyRewardAmount = _config.notifyRewardAmount;
        config.notifyMeAmount = _config.notifyMeAmount;
        config.defaultSlippageInPrecision = _config.defaultSlippageInPrecision;
        config.allowSwaps = _config.allowSwaps;
        return true;
    }

    function getLastRecordedBalances()
        internal
        view
        returns (uint256 rewardBalance, uint256 meBalance)
    {
        State storage state = writableState();
        rewardBalance = state.lastRewardAmount;
        meBalance = state.lastMeAmount;
    }

    function getOptimalRatio() internal view returns (uint256 optimalRatio) {
        Config storage config = writableConfig();
        optimalRatio = config.rOptimal;
    }

    function getState() internal pure returns (State memory state) {
        state = writableState();
    }

    function getConfig() internal pure returns (Config memory config) {
        config = writableConfig();
    }

    function getLiqudityIds()
        internal
        view
        returns (address initiator, address reward, address meToken)
    {
        State storage state = writableState();
        initiator = state.initiator;
        reward = state.reward;
        meToken = state.meToken;
    }


    function setInitialized() internal returns (bool) {
        State storage state = writableState();
        return state.initialized = true;
    }

    function getIsInitialized() internal view returns (bool) {
        State storage state = writableState();
        return state.initialized;
    }
}

File 7 of 30 : roleguard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Database} from "./database.sol";
import {Errors} from "./errors.sol";
import {Validator} from "./validators.sol";
import {Params} from "./params.sol";
import {Constants} from "./constants.sol";
import {Roles} from "./roles.sol";

library RoleGuard {
    struct AccessData {
        bytes32 adminAccessKey;
        mapping(address => bool) members;
    }

    function writableRoleRecords()
        internal
        pure
        returns (mapping(bytes32 => AccessData) storage records)
    {
        bytes32 position = Database.ACCESS_RECORDS;
        assembly {
            records.slot := position
        }
    }

    function isAuthorized(
        bytes32 accessKey,
        address account
    ) internal view returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        return records[accessKey].members[account];
    }

    ///@dev many seeds, single reward with brandId input
    function ensureAccountIsAuthorized(
        address target,
        bytes32[] memory seeds,
        address account,
        bytes10 brandId
    ) internal view returns (bool) {
        if (seeds.length > 5) revert Errors.SEEDS_ARE_TOO_MUCH();
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();

        if (!Validator.isEmpty(brandId)) {
            (
                bytes32 adminAccessKey,
                bytes32 accessKey
            ) = synthesizeAdminAndAccessKeyForReward(brandId, target, seeds[0]);
            if (adminAccessKey == records[accessKey].adminAccessKey) {
                return true;
            }
        }
        for (uint256 i; i < seeds.length; ) {
            bytes32 accessKey = synthesizeAccessKeyForReward(target, seeds[i]);
            if (records[accessKey].members[account]) {
                return true;
            }
            ++i;
        }
        revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
    }

    ///@dev many seeds, with brandId target
    function ensureAccountIsAuthorized(
        bytes10 target,
        bytes32[] memory seeds,
        address account,
        bytes10 brandId
    ) internal view returns (bool) {
        if (seeds.length > 5) revert Errors.SEEDS_ARE_TOO_MUCH();
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (!Validator.isEmpty(brandId)) {
            if (target == brandId) return true;
        }
        for (uint256 i; i < seeds.length; ) {
            bytes32 accessKey = synthesizeAccessKeyForBrand(target, seeds[i]);
            if (records[accessKey].members[account]) {
                return true;
            }
            ++i;
        }
        revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
    }

    ///@dev used with openRewards
    function ensureAccountIsAuthorized(
        address target,
        bytes32[] memory seeds,
        address account
    ) internal view returns (bool) {
        if (seeds.length > 5) revert Errors.SEEDS_ARE_TOO_MUCH();
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();

        (
            bytes32 adminAccessKey,
            bytes32 _accessKey
        ) = synthesizeAdminAndAccessKeysForOpenRewards(
                account,
                target,
                seeds[0]
            );
        if (adminAccessKey == records[_accessKey].adminAccessKey) return true;

        for (uint256 i; i < seeds.length; ) {
            bytes32 accessKey = synthesizeAccessKeyForReward(target, seeds[i]);
            if (records[accessKey].members[account]) {
                return true;
            }
            ++i;
        }
        revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
    }

    ///@dev single seed, single reward with brand id
    function ensureAccountIsAuthorized(
        address target,
        bytes32 seed,
        address account,
        bytes10 brandId
    ) internal view returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        (
            bytes32 adminKey,
            bytes32 accessKey
        ) = synthesizeAdminAndAccessKeyForReward(brandId, target, seed);
        if (adminKey != records[accessKey].adminAccessKey) {
            if (!records[accessKey].members[account]) {
                revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
            }
        }
        return true;
    }

    ///@dev single seed, single target brand with brand id
    function ensureAccountIsAuthorized(
        bytes10 target,
        bytes32 seed,
        address account,
        bytes10 brandId
    ) internal view returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (brandId != target) {
            bytes32 accessKey = synthesizeAccessKeyForBrand(target, seed);
            if (!records[accessKey].members[account]) {
                revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
            }
        }
        return true;
    }

    ///@dev used for openrewards
    function ensureAccountIsAuthorized(
        address target,
        bytes32 seed,
        address account
    ) internal view returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();

        (
            bytes32 adminAccessKey,
            bytes32 accessKey
        ) = synthesizeAdminAndAccessKeysForOpenRewards(account, target, seed);
        if (adminAccessKey == records[accessKey].adminAccessKey) return true;
        if (records[accessKey].members[account]) return true;
        revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
    }

    ///@dev this is used foe the main me-protocol rule
    function ensureAccountIsAuthorized(
        bytes32 seed,
        address account
    ) internal view returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();

        (
            bytes32 adminAccessKey,
            bytes32 accessKey
        ) = synthesizeAdminAndAccessKeyForMeProtocol(account, seed);
        if (adminAccessKey == records[accessKey].adminAccessKey) return true;
        if (records[accessKey].members[account]) return true;
        revert Errors.ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
    }

    function grantAccessToAccount(
        bytes32 accessKey,
        bytes32 adminAccessKey,
        address account
    ) internal returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (adminAccessKey != records[accessKey].adminAccessKey) {
            revert Errors.REQUESTOR_IS_NOT_ADMIN_FOR_THIS_ACCESS_KEY();
        }
        if (records[accessKey].members[account]) {
            revert Errors.ACCOUNT_ALREADY_HAS_ACCESS();
        }
        records[accessKey].members[account] = true;
        return true;
    }

    function revokeAccessFromAccount(
        bytes32 accessKey,
        bytes32 adminAccessKey,
        address account
    ) internal returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (adminAccessKey != records[accessKey].adminAccessKey) {
            revert Errors.REQUESTOR_IS_NOT_ADMIN_FOR_THIS_ACCESS_KEY();
        }
        if (!records[accessKey].members[account]) {
            revert Errors.ACCOUNT_DOES_NOT_HAVE_ACCESS();
        }
        records[accessKey].members[account] = false;
        return true;
    }

    function createNewAccesskeyForReward(
        bytes10 brand,
        address target,
        bytes32 seed
    ) internal returns (bool) {
        (
            bytes32 adminAccessKey,
            bytes32 accessKey
        ) = synthesizeAdminAndAccessKeyForReward(brand, target, seed);
        return _createNewAccessKey(accessKey, adminAccessKey);
    }

    function createNewAccessKeysForReward(
        bytes10 brand,
        address target,
        bytes32[] memory seeds
    ) internal returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (seeds.length > 5) revert Errors.SEEDS_ARE_TOO_MUCH();
        for (uint256 i; i < seeds.length; ) {
            (
                bytes32 adminAccessKey,
                bytes32 accessKey
            ) = synthesizeAdminAndAccessKeyForReward(brand, target, seeds[i]);
            if (
                records[accessKey].adminAccessKey != Constants.EMPTY_ACCESS_KEY
            ) {
                revert Errors.ACCESS_KEY_ALREADY_EXISTS_PLEASE_CHANGE_INSTEAD();
            }
            records[accessKey].adminAccessKey = adminAccessKey;
            ++i;
        }
        return true;
    }

    function createNewAccessKeysForOpenRewards(
        address admin,
        address target,
        bytes32[] memory seeds
    ) internal returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (seeds.length > 5) revert Errors.SEEDS_ARE_TOO_MUCH();
        for (uint256 i; i < seeds.length; ) {
            (
                bytes32 adminAccessKey,
                bytes32 accessKey
            ) = synthesizeAdminAndAccessKeysForOpenRewards(
                    admin,
                    target,
                    seeds[i]
                );
            if (
                records[accessKey].adminAccessKey != Constants.EMPTY_ACCESS_KEY
            ) {
                revert Errors.ACCESS_KEY_ALREADY_EXISTS_PLEASE_CHANGE_INSTEAD();
            }
            records[accessKey].adminAccessKey = adminAccessKey;
            ++i;
        }
        return true;
    }

    function createNewAccessKeyForMeProtocol(
        address admin,
        bytes32[] memory seed
    ) internal returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (seed.length > 5) revert Errors.SEEDS_ARE_TOO_MUCH();
        for (uint256 i; i < seed.length; ) {
            (
                bytes32 adminAccessKey,
                bytes32 accessKey
            ) = synthesizeAdminAndAccessKeyForMeProtocol(admin, seed[i]);
            if (
                records[accessKey].adminAccessKey != Constants.EMPTY_ACCESS_KEY
            ) {
                revert Errors.ACCESS_KEY_ALREADY_EXISTS_PLEASE_CHANGE_INSTEAD();
            }
            records[accessKey].adminAccessKey = adminAccessKey;
            ++i;
        }

        return true;
    }

    function createNewAccessKeyForMeProtocol(
        address admin,
        bytes32 seed
    ) internal returns (bool) {
        (
            bytes32 adminAccessKey,
            bytes32 accessKey
        ) = synthesizeAdminAndAccessKeyForMeProtocol(admin, seed);
        return _createNewAccessKey(accessKey, adminAccessKey);
    }

    function createNewAccesskeyForBrand(
        bytes10 brand,
        bytes32 seed
    ) internal returns (bool) {
        (
            bytes32 adminAccessKey,
            bytes32 accessKey
        ) = synthesizeAdminAndAccessKeyForBrand(brand, seed);
        return _createNewAccessKey(accessKey, adminAccessKey);
    }

    function createNewAccessKeyForOpenRewards(
        address admin,
        address target,
        bytes32 seed
    ) internal returns (bool) {
        (
            bytes32 adminAccessKey,
            bytes32 accessKey
        ) = synthesizeAdminAndAccessKeysForOpenRewards(admin, target, seed);
        return _createNewAccessKey(accessKey, adminAccessKey);
    }

    function _createNewAccessKey(
        bytes32 accessKey,
        bytes32 adminAccessKey
    ) internal returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (records[accessKey].adminAccessKey != Constants.EMPTY_ACCESS_KEY) {
            revert Errors.ACCESS_KEY_ALREADY_EXISTS_PLEASE_CHANGE_INSTEAD();
        }
        records[accessKey].adminAccessKey = adminAccessKey;
        return true;
    }

    function changeAdminAccessKey(
        bytes32 accessKey,
        bytes32 adminAccessKey,
        bytes32 newAdminAccessKey
    ) internal returns (bool) {
        mapping(bytes32 => AccessData) storage records = writableRoleRecords();
        if (adminAccessKey != records[accessKey].adminAccessKey) {
            revert Errors.REQUESTOR_IS_NOT_ADMIN_FOR_THIS_ACCESS_KEY();
        }
        records[accessKey].adminAccessKey = newAdminAccessKey;
        return true;
    }

    function synthesizeAdminAndAccessKeyForMeProtocol(
        address admin,
        bytes32 seed
    ) internal pure returns (bytes32 adminAccessKey, bytes32 accessKey) {
        accessKey = synthesizeAccessKeyForMeProtocol(seed);
        adminAccessKey = synthesizeAdminAccessKeyForMeProtocol(admin, seed);
    }

    function synthesizeAdminAndAccessKeyForReward(
        bytes10 brand,
        address target,
        bytes32 seed
    ) internal pure returns (bytes32 adminAccessKey, bytes32 accessKey) {
        accessKey = synthesizeAccessKeyForReward(target, seed);
        adminAccessKey = synthesizeAdminAccessKeyForReward(brand, target);
    }

    function synthesizeAdminAndAccessKeyForBrand(
        bytes10 brand,
        bytes32 seed
    ) internal pure returns (bytes32 adminAccessKey, bytes32 accessKey) {
        accessKey = synthesizeAccessKeyForBrand(brand, seed);
        adminAccessKey = synthesizeAdminAcessKeyForBrand(brand);
    }

    function synthesizeAccessKeyForReward(
        address target,
        bytes32 seed
    ) internal pure returns (bytes32 accessKey) {
        accessKey = keccak256(abi.encodePacked(target, seed));
    }

    function synthesizeAccessKeyForOpenRewards(
        address target,
        bytes32 seed
    ) internal pure returns (bytes32 accessKey) {
        accessKey = keccak256(abi.encodePacked(target, seed));
    }

    function synthesizeAdminAccessKeyForOpenRewards(
        address admin,
        address target
    ) internal pure returns (bytes32 adminAccessKey) {
        adminAccessKey = keccak256(abi.encodePacked(admin, target));
    }

    function synthesizeAdminAcessKeyForBrand(
        bytes10 brand
    ) internal pure returns (bytes32 adminAccessKey) {
        adminAccessKey = keccak256(abi.encodePacked(brand));
    }

    function synthesizeAdminAccessKeyForMeProtocol(
        address admin,
        bytes32 seed
    ) internal pure returns (bytes32 accessKey) {
        accessKey = keccak256(abi.encodePacked(admin, seed));
    }

    function synthesizeAccessKeyForMeProtocol(
        bytes32 seed
    ) internal pure returns (bytes32 adminAccessKey) {
        adminAccessKey = keccak256(abi.encodePacked(seed));
    }

    function synthesizeAdminAccessKeyForReward(
        bytes32 brand,
        address target
    ) internal pure returns (bytes32 adminAccessKey) {
        adminAccessKey = keccak256(abi.encodePacked(brand, target));
    }

    function synthesizeAccessKeyForBrand(
        bytes10 brand,
        bytes32 seed
    ) internal pure returns (bytes32 accessKey) {
        accessKey = keccak256(abi.encodePacked(brand, seed));
    }

    function synthesizeAdminAndAccessKeysForOpenRewards(
        address admin,
        address target,
        bytes32 seed
    ) internal pure returns (bytes32 adminAccessKey, bytes32 accessKey) {
        accessKey = synthesizeAccessKeyForOpenRewards(target, seed);
        adminAccessKey = synthesizeAdminAccessKeyForOpenRewards(admin, target);
    }
}

File 8 of 30 : a-pool.controller.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {NonFungibleController as NonFungible} from "../standards/nonFungible.controller.sol";
import {Params, OpenRewardsData as ORData} from "../../providers/data/a-pool.sol";

interface OpenRewardsController {
    function startOpenRewards() external returns (uint256 optimalRewardValueInPrecision);

    function pauseOpenRewards() external returns (bool);

    function resumeOpenRewards() external returns (bool);

    function recordLiquidityProvided(Params.LiquidityInfo memory info) external returns (uint256);

    function withdrawLiquidity(Params.LiquidityInfo memory info) external returns (uint256, uint256);

    function addOpenRewardsManager(address newPoolManager) external returns (bool);

    function removeOpenRewardsManager(address poolManager) external returns (bool);

    function addLiquidityManager(address account) external returns (bool);

    function removeLiquidityManager(address liquidityManger) external returns (bool);

    function getLiquidityRatios() external view returns (uint256, uint256);

    function getLiquidityIds() external view returns (address, address, address);

    function getOpenRewardsState() external view returns (ORData.State memory);

    function getOpenRewardsConfigurations() external view returns (ORData.Config memory);

    function initiateOutgoingConversation(Params.OutgoingConversationInfo memory info)
        external
        returns (Params.InitiateConversationReturnType memory conversionDigest);

    function engageIncomingConversation(uint256 outputRewardAmount, address to) external returns (uint256);

    function recordMeTokensProvidedByProtocolTeam(uint256 expecteDeposit) external returns (uint256);

    function releaseMeTokensProvidedByProtocolTeam(uint256 amountToRelease, address to, bytes32 typeOfRelease)
        external
        returns (uint256, uint256);

    function changeConfigExceptOptimalRatio(
        Params.EditableConfigForTypeAOpenRewards memory editableConfig,
        bool ignoreDefault
    ) external returns (bool);

    function changeOptimalRatio(uint256 newOptimalRatio) external returns (bool);

    function getLiquidityPositionByIndex(uint256 index, address account) external view returns (uint256);

    function getAllLiquidityPositionForAccount(address requestor) external view returns (uint256[] memory);

    function getOptimalRatio() external view returns (uint256);

    function leverageOutgoingConversationInsight(uint256 meAmount, uint256 slippageInPrecision)
        external
        view
        returns (uint256 rewardAmount);

    function leverageIncomingConversationInsight(uint256 rewardAmount)
        external
        view
        returns (uint256 optimalMeAmount, uint256 rOptimal);

    function getOutgoingConversationInsight(uint256 meAmount, uint256 slippageInPrecision)
        external
        view
        returns (uint256 rewardAmount, uint256 lastRewardAmount);

    function getIncomingConversationInsight(uint256 rewardAmount)
        external
        view
        returns (uint256 optimalMeAmount, uint256 lastRewardAmount);

    function initialize(address reward, address meToken, Params.ConfigForTypeAOpenRewards memory config)
        external
        returns (bool);
}

File 9 of 30 : a-pool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Params} from "../common/params.sol";

library OpenRewardsLogs {
    event openRewardsStarted(address requestor, uint256 optimalRatioInPrecision);

    event openRewardsPaused(address requestor);

    event openRewardsResumed(address requestor);

    event liquidityProvided(uint256 rewardAmount, uint256 meAmount, address requestor, address to);

    event liqudityWithdrawn(uint256 rewardAmount, uint256 meAmount, address requestor, address to);

    event openRewardsManagerAdded(address newOpenRewardsManager, address requestor);

    event openRewardsManagerRemoved(address openRewardsManager, address requestor);

    event liqudityManagerAdded(address account, address requestor);

    event liquidityManagerRemoved(address liqudityManager, address requestor);

    event conversationMade(uint256 inputReward, uint256 outputReward, address outputOpenRewardsId, address requestor);

    event meTokensProvidedByProtocolTeam(uint256 amount);

    event meTokensRemovedByProtocolTeam(uint256 amount);

    event openRewardsConfigChanged(Params.EditableConfigForTypeAOpenRewards editableConfig, bool ignoreDefault);

    event optimalRatioChanged(uint256 oldRatio, uint256 newRatio);

    event refunded(address reward, address user, uint256 amount);

    event liquidityUpdated(uint256 rewardAmount, uint256 meAmount, uint40 timestamp);

    event currentPoolsState(
        address indexed rewardOne,
        uint256 currentAmountOfMeTokensrewardOne,
        uint256 currentAmountOfRewardTokensrewardOne,
        uint256 notifyRewardAmountrewardOne,
        uint256 notifyMeTokenAmountrewardOne,
        address indexed rewardTwo,
        uint256 currentAmountOfMeTokensRewardTwo,
        uint256 currentAmountOfRewardTokensRewardTwo,
        uint256 notifyRewardAmountRewardTwo,
        uint256 notifyMeTokenAmountRewardTwo
    );
}

File 10 of 30 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.2) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner or approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownerOf(tokenId) != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId, 1);

        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
        require(!_exists(tokenId), "ERC721: token already minted");

        unchecked {
            // Will not overflow unless all 2**256 token ids are minted to the same owner.
            // Given that tokens are minted one by one, it is impossible in practice that
            // this ever happens. Might change if we allow batch minting.
            // The ERC fails to describe this case.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId, 1);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId, 1);

        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
        owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        delete _tokenApprovals[tokenId];

        unchecked {
            // Cannot overflow, as that would require more tokens to be burned/transferred
            // out than the owner initially received through minting and transferring in.
            _balances[owner] -= 1;
        }
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId, 1);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId, 1);

        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];

        unchecked {
            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
            // `from`'s balance is the number of token held, which is at least one before the current
            // transfer.
            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
            // all 2**256 token ids to be minted, which in practice is impossible.
            _balances[from] -= 1;
            _balances[to] += 1;
        }
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId, 1);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
     * - When `from` is zero, the tokens will be minted for `to`.
     * - When `to` is zero, ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize
    ) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
     * - When `from` is zero, the tokens were minted for `to`.
     * - When `to` is zero, ``from``'s tokens were burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize
    ) internal virtual {}

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
     * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
     * that `ownerOf(tokenId)` is `a`.
     */
    // solhint-disable-next-line func-name-mixedcase
    function __unsafe_increaseBalance(address account, uint256 amount) internal {
        _balances[account] += amount;
    }
}

File 11 of 30 : IERC721Enumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

File 12 of 30 : errors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

library Errors {
    error ADDRESS_ZERO_NOT_ALLOWED();
    error ZERO_NOT_ALLOWED();
    error EMPTY_STRING_NOT_ALLOWED();
    error EMPTY_REASON_NOT_ALLOWED();
    error OPEN_REWARDS_NOT_STARTED();
    error OPEN_REWARDS_ALREADY_STARTED();
    error OPEN_REWARDS_IS_PAUSED();
    error OPEN_REWARDS_IS_ACTIVE();
    error OPEN_REWARDS_IS_NOT_ACTIVE();
    error OPTIMAL_REWARD_RATIO_CANNOT_BE_ZERO();
    error MAXIMUM_REWARD_RATIO_CANNOT_BE_LESS_THAN_THE_OPTIMAL_RATIO();
    error OPEN_REWARDS_SHOULD_START_AT_R_OPTIMAL_OR_LESS();
    error EXPECTED_PROTOCOL_ME_OFFSET_EXCEEDS_ACTUAL_ME_OFFSET();
    error CONVERSATION_WILL_CAUSE_POOL_TO_GO_OUT_OF_RANGE();
    error REQUEST_IS_NOT_WITHIN_ACCEPTED_RANGE();
    error REWARD_WITHDRAWAL_TYPE_C_ERROR(string);
    error ACTION_WOULD_TAKE_POOL_REWARDS_BELOW_CONVERSATION_LIMIT();
    error LIQUIDITY_IS_CURRENTLY_BELOW_CONVERSATION_LIMIT();
    error LIQUIDITY_RATIO_DURING_RESET_OF_OPTIMAL_RATIO_CANNOT_BE_GREATER_THAN_THE_OPTIMAL_RATIO();
    error REQESTOR_IS_NOT_THE_OWNER_OF_THIS_POSITION();
    error CANNOT_WITHDRAW_ZERO_ASSET_FROM_POOL();
    error INSUFFICENT_POSITION_BALANCE();
    error ACCOUNT_IS_ALREADY_POOL_MANAGER();
    error ACCOUNT_IS_NOT_A_POOL_MANAGER();
    error BOUNTY_DEPOSIT_NOT_RECOGNIZED();
    error REWARD_IS_NOT_BOUNTY_REWARD();
    error INSUFFICIENT_BOUNTY_REWARD();
    error BRAND_DOES_NOT_EXIST();
    error BRAND_ALREADY_EXISTS();
    error REWARD_DOES_NOT_EXIST();
    error REWARD_ALREADY_EXISTS();
    error REWARD_ALREADY_OPEN();
    error REWARD_IS_NOT_OPEN();
    error ACCOUNT_IS_NOT_TIED_TO_ANY_BRAND();
    error REQUESTOR_IS_NOT_REWARD_ISSUER();
    error REWARD_DOES_NOT_SUPPORT_BOUNTIES();
    error REWARD_ALREADY_SUPPORT_BOUNTIES();
    error INSUFFICIENT_BOUNTY_REWARD_BALANCE();
    error INSUFFICIENT_TREASURY_REWARD_BALANCE();
    error INSUFFICIENT_BRAND_ME_TREASURY_BALANCE();
    error ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST();
    error ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_BOUNTY_REQUEST();
    error ACCOUNT_ALREADY_HAS_ACCESS();
    error REQUESTOR_IS_NOT_ADMIN_FOR_THIS_ACCESS_KEY();
    error ACCESS_KEY_ALREADY_EXISTS_PLEASE_CHANGE_INSTEAD();
    error ACCOUNT_DOES_NOT_HAVE_ACCESS();
    error REWARDS_TRANSFER_FAILED();
    error REWARDS_PERMIT_FAILED();
    error BOUNTY_POOL_IS_CURRENTLY_BUSY();
    error BOTH_DEPOSITS_CAN_NOT_BE_ZERO();
    error TREASURY_DEPOSIT_NOT_RECOGNIZED();
    error BRAND_ID_CANNOT_BE_EMPTY();
    error BOTH_WITHDRAWALS_CANNOT_BE_ZERO();
    error INSUFFICIENT_TREASURY_ME_BALANCE();
    error TREASURY_IS_CURRENTLY_BUSY();
    error BOTH_TOP_UPS_CANNOT_BE_ZERO();
    error SEEDS_ARE_TOO_MUCH();
    error INCOHERENT_TARGET_AND_SEED_LENGTH();
    error PLEASE_READ_TERMS_AND_CONDITIONS();
    error REWARD_NAME_CANNOT_BE_EMPTY();
    error REWARD_SYMBOL_CANNOT_BE_EMPTY();
    error ACCOUNT_IS_NOT_REGISTERED_AS_A_BRAND();
    error INVALID_POSITION();
    error INSUFFICIENT_REWARD_POSITION();
    error INSUFFICIENT_ME_POSITION();
    error REQUESTOR_IS_NOT_OWNER_OF_POSITION();
    error CONVERSATION_WILL_CAUSE_OPEN_REWARDS_TO_GO_OUT_OF_RANGE();
    error ACTION_WILL_TAKE_POOL_ME_TOKENS_BELOW_CONVERSATION_LIMIT();
    error INSUFFICENT_REWARD_AMOUNT_DEPOSITED_FOR_CONVERSATION();
    error R_OPTIMAL_CANNOT_BE_ZERO();
    error REQUESTOR_HAS_NO_POSITION();
    error POSITIONS_ARE_MORE_THAN_TWENTY_TRY_GETTING_THEM_ONE_AT_A_TIME();
    error REQUEST_NOT_WITHIN_SLIPPAGE_RANGE();
    error POOL_IS_BUSY();
    error INVALID_POSITION_INDEX();
    error REQUESTOR_DOES_NOT_IMPLEMENT_OPEN_REWARDS();
    error CONVERSATION_FAILED();
    error NOT_OPEN_REWARDS_CONVERSATION();
    error INSUFFICIENT_DEPOSIT();
    error INSUFFICIENT_REWARD();
    error ALREADY_INITIALIZED();
    error REWARDS_APPROVAL_FAILED();
    error ACCOUNT_IS_ALREADY_REGISTERED_AS_A_BRAND();
    error NOT_AUTHORIZED_TO_MANAGER_RUNTIME();
    error BATCH_PROOF_ALREADY_SET();
    error VAULT_SIGNATURE_USED();
    error VAULT_INVALID_SIGNATURE();
    error VAULT_INVALID_ALLOWEE();
    error SIGNER_IS_NOT_RUNTIME();
    error VAULT_INVALID_HASH();
    error BRAND_ID_IS_ALREADY_TAKEN();
    error PERMIT_DEADLINE_EXPIRED();
    error ME_AMOUNT_CAN_NOT_BE_EQUAL_TO_PERMIT_AMOUNT();
    error VAULT_EXPIRED_SIGNATURE();
    error INVALID_INPUT();
    error OPEN_REWARDS_ALREADY_INITIALIZED();

    // Main (diamonds) errors

    string public constant CALLER_IS_NOT_CONTRACT_OWNER = "1";
    string public constant INCORRECT_UPGRADE_ACTION = "2";
    string public constant NO_SELECTOR_IN_SPECIFIED_MODULE = "3";
    string public constant ZERO_ADDRESS_NOT_ALLOWED = "4";
    string public constant MODULE_ALREADY_EXISTS = "5";
    string public constant MODULE_IS_SAME_AS_THAT_TO_BE_REPLACED = "6";
    string public constant REMOVE_MODULE_ADDRESS_MUST_BE_ZERO_ADDRESS = "7";
    string public constant SERVICE_OR_PROVIDER_DOES_NOT_EXIST = "8";
    string public constant CAN_NOT_REMOVE_THIS_SERVICE_OR_PROVIDER = "9";
    string public constant INIT_ADDRESS_HAS_NO_CODE = "10";
    string public constant MODULE_DOES_NOT_EXIST = "11";
    string public constant DELEGATE_CALL_NOT_ALLOWED = "12";
}

File 13 of 30 : validators.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Errors} from "./errors.sol";

library Validator {
    function ensureAddressIsNotZeroAddress(address _addr) internal pure {
        if (_addr == address(0)) {
            revert Errors.ADDRESS_ZERO_NOT_ALLOWED();
        }
    }

    function ensureValueIsNotZero(uint256 _value) internal pure {
        if (_value == 0) {
            revert Errors.ZERO_NOT_ALLOWED();
        }
    }

    function ensureInputIsNotEmpty(string memory _stringOpt) internal pure {
        if (bytes(_stringOpt).length == 0) {
            revert Errors.EMPTY_STRING_NOT_ALLOWED();
        }
    }

    function ensureReasonIsNotEmpty(bytes32 input) internal pure {
        if (input == bytes32(0)) revert Errors.EMPTY_REASON_NOT_ALLOWED();
    }

    function ensureBrandIdIsNotEmpty(bytes10 brandId) internal pure {
        if (brandId == bytes10(0)) {
            revert Errors.BRAND_ID_CANNOT_BE_EMPTY();
        }
    }

    function ensureRIsWithinAcceptableRange(uint256 r, uint256 rMaxLimit) internal pure {
        if (r > rMaxLimit) {
            revert Errors.CONVERSATION_WILL_CAUSE_POOL_TO_GO_OUT_OF_RANGE();
        }
    }

    function isEmpty(string memory _data) internal pure returns (bool) {
        return bytes(_data).length == 0;
    }

    function isEmpty(bytes10 brandId) internal pure returns (bool) {
        return brandId == bytes10(0);
    }

    function isEmptyB(bytes32 brandId) internal pure returns (bool) {
        return brandId == bytes32(0);
    }

    function isEmpty(uint256[] memory myArray) internal pure returns (bool) {
        return myArray.length == 0;
    }

    function isZero(uint256 _data) internal pure returns (bool) {
        return _data == 0;
    }

    function isFalse(bool _data) internal pure returns (bool) {
        return _data == false;
    }
}

File 14 of 30 : constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

library Constants {
    uint256 constant PRECISION = 1000000;
    uint256 constant GOLDEN_RATIO_IN_PRECISION = (1618 * PRECISION) / 100;
    uint256 constant EXCHANGE_SLOPE = GOLDEN_RATIO_IN_PRECISION / 2;
    uint256 constant STARTING_SEED = 1;
    uint256 constant EMPTY_POSITION = 0;
    bytes10 constant DEFAULT_BRAND_ID = bytes10(0);
    bytes32 constant EMPTY_ACCESS_KEY = bytes32(0);
    bytes32 constant CAI = keccak256("customer acquisition incentives");
    bytes32 constant PROTOCOL_FEES = keccak256("protocol fees");
    bytes32 constant GAS_COSTS = keccak256("gas costs");
    uint256 constant PERCENTAGE = 100;
    bytes32 constant NORMAL = keccak256("normal");
    bytes32 constant WITH_REWARDS_IF_NEED_BE = keccak256("with rewards");
    bytes32 constant WITHDRAWABLE = keccak256("withdrawable");
    bytes32 constant FORCED = keccak256("forced");
    string constant ME_PROTOCOL = "me protocol";
    string constant ME_P = "MeP";
    bytes4 constant OPEN_REWARDS = bytes4(keccak256("openrewards secret"));
    uint8 constant FUNGIBLE_TYPE_A = 1;
    string constant EMPTY_STRING = "";
    uint256 constant EMPTY_AMOUNT = 0;
    address constant ME_DISPENSER = 0x1111111111111111111111111111111111111111;
}

File 15 of 30 : fungible.controller.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface FungibleController {
    function name() external view returns (string memory);

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

    function decimals() external view returns (uint8);

    event Transfer(address indexed from, address indexed to, uint256 value);

    function totalSupply() external view returns (uint256);

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

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

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

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

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

File 16 of 30 : params.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

library Params {
    ///Brands
    struct EditableBrandDetails {
        string name;
        string onlinePresence;
    }

    struct EditableBrandConfig {
        bool enableBountyRewards;
        bool enableCais;
        bool payIncomingGasFees;
        bool payOutgoingGasFees;
    }

    ///Rewards
    struct EditableRewardDetails {
        string name;
        string symbol;
        string descriptionLink;
    }

    struct EditableRewardConfig {
        bool specificException;
        bool bountyEnables;
        bool caiEnabled;
        uint256 bountyTriggerLimit;
        uint256 bountyContributionInPrecision;
        bool payIncomingGasFee;
        bool payOutgoingGasFee;
    }

    //Protocol
    struct EditableProtocolConfig {
        uint256 defaultMinimumMeForConversation;
        uint256 defaultMinimumRewardForConversationInPercent;
        uint256 defaultMaximumRLimitForConversationInPrecision;
        uint256 defaultRewardNotifyThresholdInPercent;
        uint256 defaultNotifyMeAmount;
        uint256 defaultNotifyRewardAmountInPercent;
        uint256 caiInMe;
        uint256 protocolFee;
        uint256 bountyContributionInPrecision;
        uint256 conversationSlippageInPrecision;
        uint256 informationSlippageInPrecision;
    }

    struct EditableProtocolRecords {
        address meId;
        address bountyId;
        address treasuryId;
        address vaultId;
        bytes10 adminId;
    }

    ///Treasury
    struct EditableTreasuryRecords {
        address meId;
        uint256 meNofityLimit;
    }

    ///Pool
    struct ConfigForTypeAOpenRewards {
        uint256 rOptimal;
        uint256 maximumRLimit;
        uint256 minimumRewardAmountForConversation;
        uint256 minimumMeAmountForConversation;
        uint256 notifyRewardAmount;
        uint256 notifyMeAmount;
        uint256 defaultSlippageInPrecision;
        bool allowSwaps;
    }

    struct EditableConfigForTypeAOpenRewards {
        uint256 maximumRLimit;
        uint256 minimumRewardAmountForConversation;
        uint256 minimumMeAmountForConversation;
        uint256 notifyRewardAmount;
        uint256 notifyMeAmount;
        uint256 defaultSlippageInPrecision;
        bool allowSwaps;
    }

    struct CurrentStateOfTypeAOpenRewards {
        bool started;
        bool active;
        address initiator;
        address reward;
        address meToken;
        uint256 lastRewardAmount;
        uint256 lastMeAmount;
        uint256 meTokensFromProtocolTeam;
        uint256 setupMeAmount;
        uint40 lastTransactionTime;
    }

    struct LiquidityInfo {
        uint256 position;
        uint256 rewardAmount;
        uint256 meAmount;
        address requestor;
        address to;
    }

    struct ExtendedLiquidityInfo {
        uint256 rewardAmount;
        uint256 meAmount;
        uint256 currentRewardAmount;
        uint256 currentMeAmount;
        uint256 minimumRewardAmountForConversation;
        uint256 minimumMeAmountForConversation;
        uint256 meTokensFromProtocolTeam;
        uint256 rOptimal;
    }

    struct OutgoingConversationInfo {
        uint256 rewardAmountIn;
        uint256 expectedAmountOfOutputReward;
        address listener;
        uint256 listenerROptimal;
        address requestor;
        address outputRewardReceiver;
    }

    struct spendingInfo {
        address rewardAtHand;
        address targettedReward;
        uint256 amountOfRewardAtHand;
        uint256 expectedAmountOfTargetedReward;
    }

    struct PermitParam {
        uint256 _deadline;
        uint8 _v;
        bytes32 _r;
        bytes32 _s;
    }

    struct VaultPermitParams {
        address owner;
        address spender;
        address reward;
        uint256 value;
        uint256 count;
        bytes32 prefixedHash;
        bytes32 globalHash;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    struct InitialSupplyParams {
        address vaultAddress;
        address treasuryAddress;
        uint256 initialSupplyVaultAmount;
        uint256 initialSupplyTreasuryAmount;
    }

    struct InitialSupplyGenericParams {
        uint256 amountOne;
        uint256 amountTwo;
        address recipientOne;
        address recipientTwo;
    }

    struct TreasuryPermitParams {
        address owner; // brand
        address spender; // reward
        uint256 value;
        uint256 deadline;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    struct RoleParams {
        bytes32 role;
        address account;
    }

    struct InitiateConversationReturnType {
        uint256 currentAmountOfMeTokensrewardOne;
        uint256 currentAmountOfRewardTokensrewardOne;
        uint256 notifyRewardAmountrewardOne;
        uint256 notifyMeTokenAmountrewardOne;
        uint256 currentAmountOfMeTokensRewardTwo;
        uint256 currentAmountOfRewardTokensRewardTwo;
        uint256 notifyRewardAmountRewardTwo;
        uint256 notifyMeTokenAmountRewardTwo;
        uint256 outputRewardsAmount;
        uint256 currentDepositNonceRewardOne;
        uint256 currentDepositNonceRewardTwo;
    }
}

File 17 of 30 : database.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Errors} from "./errors.sol";
import {Validator} from "./validators.sol";
import {Params} from "./params.sol";
import {Constants} from "./constants.sol";
import {RoleGuard, Roles} from "./roleguard.sol";

library Database {
    bytes32 constant MAIN_STORAGE_POSITION = keccak256("me.protocol.pool.main.storage");
    bytes32 constant BRAND_RECORDS = keccak256("me.protocol.brand.records");
    bytes32 constant REWARD_RECORDS = keccak256("me.protocol.rewards.records");
    bytes32 constant BOUNTY_RECORDS = keccak256("me.protocol.bounty.records");
    bytes32 constant PROTOCOL_RECORDS = keccak256("me.protocol.protocol.records");
    bytes32 constant PROTOCOL_CONFIGS = keccak256("me.protocol.protocol.configs");
    bytes32 constant CONTEXT_RECORD = keccak256("me.protocol.protocol.context");

    bytes32 constant TREASURY_RECORDS = keccak256("me.protocol.treasury.records");
    bytes32 constant ACCESS_RECORDS = keccak256("me.protocol.access.records");
    bytes32 constant VAULT_RECORDS = keccak256("me.protocol.vault.records");
    bytes32 constant OPEN_REWARDS_STATE_TYPE_A = keccak256("me.protocol.openrewards.state.type.a");
    bytes32 constant OPEN_REWARDS_CONFIG_TYPE_A = keccak256("me.protocol.openrewards.config.type.a");
    bytes32 constant OPEN_REWARDS_POSITIONS_TYPE_A = keccak256("me.protocol.openrewards.positions.type.a");
    bytes32 constant RUNTIME_STORAGE_POSITION = keccak256("me.protocol.pool.runtime.storage");
}

File 18 of 30 : a-position.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

library Positions {
    struct Position {
        mapping(uint256 => PositionMetadata) positionMetadata;
        uint256 lastPosition;
    }

    struct PositionMetadata {
        uint256 rewardPosition;
        uint256 mePosition;
    }
}

File 19 of 30 : roles.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

library Roles {
    // protocol based roles
    bytes32 internal constant PROTOCOL = keccak256("PROTOCOL");
    bytes32 internal constant PROTOCOL_ADMIN = keccak256("PROTOCOL_ADMIN");
    bytes32 internal constant LIQUIDITY_MANAGER = keccak256("LIQUIDITY_MANAGER");
    bytes32 internal constant ONBOARDING_MANAGER = keccak256("ONBOARDING_MANAGER");

    // brand based roles
    bytes32 internal constant BRAND = keccak256("BRAND");
    bytes32 internal constant BRAND_ACCOUNT_MANAGER = keccak256("BRAND_ACCOUNT_MANAGER");
    bytes32 internal constant BOUNTY_MANAGER = keccak256("BOUNTY_MANAGER");
    bytes32 internal constant TREASURY_MANAGER = keccak256("TREASURY_MANAGER");

    // pool based roles
    bytes32 internal constant OPEN_REWARDS_MANAGER = keccak256("OPEN_REWARDS_MANAGER");

    // reward based roles
    bytes32 internal constant REWARD_MANAGER = keccak256("REWARD_MANAGER");

    // auto top-up based roles
    bytes32 internal constant AUTO_TOP_UP_MANAGER = keccak256("AUTO_TOP_UP_MANAGER");
}

File 20 of 30 : nonFungible.controller.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {Compatible} from "./compatible.sol";

interface NonFungibleController is Compatible {
    function balanceOf(address owner) external view returns (uint256 balance);

    function ownerOf(uint256 tokenId) external view returns (address owner);

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    function transferFrom(address from, address to, uint256 tokenId) external;

    function approve(address to, uint256 tokenId) external;

    function setApprovalForAll(address operator, bool approved) external;

    function getApproved(uint256 tokenId) external view returns (address operator);

    function isApprovedForAll(address owner, address operator) external view returns (bool);

    function totalSupply() external view returns (uint256);

    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    function tokenByIndex(uint256 index) external view returns (uint256);

    function ensureRequestorIsPositionOwner(uint256 position, address requestor) external view returns (uint256);
}

File 21 of 30 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

File 22 of 30 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 23 of 30 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 24 of 30 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 25 of 30 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

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 26 of 30 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

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

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

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

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

File 27 of 30 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 28 of 30 : compatible.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

interface Compatible {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 29 of 30 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "@openzeppelin/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"reward","type":"address"},{"internalType":"address","name":"meToken","type":"address"},{"components":[{"internalType":"uint256","name":"rOptimal","type":"uint256"},{"internalType":"uint256","name":"maximumRLimit","type":"uint256"},{"internalType":"uint256","name":"minimumRewardAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"minimumMeAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"notifyRewardAmount","type":"uint256"},{"internalType":"uint256","name":"notifyMeAmount","type":"uint256"},{"internalType":"uint256","name":"defaultSlippageInPrecision","type":"uint256"},{"internalType":"bool","name":"allowSwaps","type":"bool"}],"internalType":"struct Params.ConfigForTypeAOpenRewards","name":"config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ACCESS_KEY_ALREADY_EXISTS_PLEASE_CHANGE_INSTEAD","type":"error"},{"inputs":[],"name":"ACCOUNT_ALREADY_HAS_ACCESS","type":"error"},{"inputs":[],"name":"ACCOUNT_DOES_NOT_HAVE_ACCESS","type":"error"},{"inputs":[],"name":"ACCOUNT_IS_NOT_AUTHORIZED_TO_MAKE_THIS_REQUEST","type":"error"},{"inputs":[],"name":"ACTION_WILL_TAKE_POOL_ME_TOKENS_BELOW_CONVERSATION_LIMIT","type":"error"},{"inputs":[],"name":"ACTION_WOULD_TAKE_POOL_REWARDS_BELOW_CONVERSATION_LIMIT","type":"error"},{"inputs":[],"name":"ADDRESS_ZERO_NOT_ALLOWED","type":"error"},{"inputs":[],"name":"BOTH_WITHDRAWALS_CANNOT_BE_ZERO","type":"error"},{"inputs":[],"name":"CONVERSATION_FAILED","type":"error"},{"inputs":[],"name":"CONVERSATION_WILL_CAUSE_OPEN_REWARDS_TO_GO_OUT_OF_RANGE","type":"error"},{"inputs":[],"name":"CONVERSATION_WILL_CAUSE_POOL_TO_GO_OUT_OF_RANGE","type":"error"},{"inputs":[],"name":"EXPECTED_PROTOCOL_ME_OFFSET_EXCEEDS_ACTUAL_ME_OFFSET","type":"error"},{"inputs":[],"name":"INSUFFICENT_REWARD_AMOUNT_DEPOSITED_FOR_CONVERSATION","type":"error"},{"inputs":[],"name":"INSUFFICIENT_ME_POSITION","type":"error"},{"inputs":[],"name":"INSUFFICIENT_REWARD_POSITION","type":"error"},{"inputs":[],"name":"INVALID_POSITION","type":"error"},{"inputs":[],"name":"INVALID_POSITION_INDEX","type":"error"},{"inputs":[],"name":"LIQUIDITY_IS_CURRENTLY_BELOW_CONVERSATION_LIMIT","type":"error"},{"inputs":[],"name":"LIQUIDITY_RATIO_DURING_RESET_OF_OPTIMAL_RATIO_CANNOT_BE_GREATER_THAN_THE_OPTIMAL_RATIO","type":"error"},{"inputs":[],"name":"MAXIMUM_REWARD_RATIO_CANNOT_BE_LESS_THAN_THE_OPTIMAL_RATIO","type":"error"},{"inputs":[],"name":"NOT_OPEN_REWARDS_CONVERSATION","type":"error"},{"inputs":[],"name":"OPEN_REWARDS_ALREADY_INITIALIZED","type":"error"},{"inputs":[],"name":"OPEN_REWARDS_ALREADY_STARTED","type":"error"},{"inputs":[],"name":"OPEN_REWARDS_IS_ACTIVE","type":"error"},{"inputs":[],"name":"OPEN_REWARDS_IS_NOT_ACTIVE","type":"error"},{"inputs":[],"name":"OPEN_REWARDS_SHOULD_START_AT_R_OPTIMAL_OR_LESS","type":"error"},{"inputs":[],"name":"OPTIMAL_REWARD_RATIO_CANNOT_BE_ZERO","type":"error"},{"inputs":[],"name":"POOL_IS_BUSY","type":"error"},{"inputs":[],"name":"POSITIONS_ARE_MORE_THAN_TWENTY_TRY_GETTING_THEM_ONE_AT_A_TIME","type":"error"},{"inputs":[],"name":"REQUESTOR_IS_NOT_ADMIN_FOR_THIS_ACCESS_KEY","type":"error"},{"inputs":[],"name":"REQUESTOR_IS_NOT_OWNER_OF_POSITION","type":"error"},{"inputs":[],"name":"REQUEST_IS_NOT_WITHIN_ACCEPTED_RANGE","type":"error"},{"inputs":[],"name":"REWARDS_TRANSFER_FAILED","type":"error"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"REWARD_WITHDRAWAL_TYPE_C_ERROR","type":"error"},{"inputs":[],"name":"R_OPTIMAL_CANNOT_BE_ZERO","type":"error"},{"inputs":[],"name":"SEEDS_ARE_TOO_MUCH","type":"error"},{"inputs":[],"name":"ZERO_NOT_ALLOWED","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"inputReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputReward","type":"uint256"},{"indexed":false,"internalType":"address","name":"outputOpenRewardsId","type":"address"},{"indexed":false,"internalType":"address","name":"requestor","type":"address"}],"name":"conversationMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"requestor","type":"address"}],"name":"liqudityManagerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"meAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"requestor","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"liqudityWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liqudityManager","type":"address"},{"indexed":false,"internalType":"address","name":"requestor","type":"address"}],"name":"liquidityManagerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"meAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"requestor","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"liquidityProvided","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"meAmount","type":"uint256"},{"indexed":false,"internalType":"uint40","name":"timestamp","type":"uint40"}],"name":"liquidityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"meTokensProvidedByProtocolTeam","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"meTokensRemovedByProtocolTeam","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"maximumRLimit","type":"uint256"},{"internalType":"uint256","name":"minimumRewardAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"minimumMeAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"notifyRewardAmount","type":"uint256"},{"internalType":"uint256","name":"notifyMeAmount","type":"uint256"},{"internalType":"uint256","name":"defaultSlippageInPrecision","type":"uint256"},{"internalType":"bool","name":"allowSwaps","type":"bool"}],"indexed":false,"internalType":"struct Params.EditableConfigForTypeAOpenRewards","name":"editableConfig","type":"tuple"},{"indexed":false,"internalType":"bool","name":"ignoreDefault","type":"bool"}],"name":"openRewardsConfigChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOpenRewardsManager","type":"address"},{"indexed":false,"internalType":"address","name":"requestor","type":"address"}],"name":"openRewardsManagerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"openRewardsManager","type":"address"},{"indexed":false,"internalType":"address","name":"requestor","type":"address"}],"name":"openRewardsManagerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requestor","type":"address"}],"name":"openRewardsPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requestor","type":"address"}],"name":"openRewardsResumed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requestor","type":"address"},{"indexed":false,"internalType":"uint256","name":"optimalRatioInPrecision","type":"uint256"}],"name":"openRewardsStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldRatio","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRatio","type":"uint256"}],"name":"optimalRatioChanged","type":"event"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addLiquidityManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addOpenRewardsManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"maximumRLimit","type":"uint256"},{"internalType":"uint256","name":"minimumRewardAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"minimumMeAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"notifyRewardAmount","type":"uint256"},{"internalType":"uint256","name":"notifyMeAmount","type":"uint256"},{"internalType":"uint256","name":"defaultSlippageInPrecision","type":"uint256"},{"internalType":"bool","name":"allowSwaps","type":"bool"}],"internalType":"struct Params.EditableConfigForTypeAOpenRewards","name":"editableConfig","type":"tuple"},{"internalType":"bool","name":"ignoreDefault","type":"bool"}],"name":"changeConfigExceptOptimalRatio","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newOptimalRatio","type":"uint256"}],"name":"changeOptimalRatio","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_outputRewardAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"engageIncomingConversation","outputs":[{"internalType":"uint256","name":"outputRewardAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"position","type":"uint256"},{"internalType":"address","name":"requestor","type":"address"}],"name":"ensureRequestorIsPositionOwner","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAllLiquidityPositionForAccount","outputs":[{"internalType":"uint256[]","name":"positions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"name":"getIncomingConversationInsight","outputs":[{"internalType":"uint256","name":"optimalMeAmount","type":"uint256"},{"internalType":"uint256","name":"lastRewardAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityIds","outputs":[{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"address","name":"meToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"getLiquidityPositionByIndex","outputs":[{"internalType":"uint256","name":"position","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityRatios","outputs":[{"internalType":"uint256","name":"rOptimalInPrecision","type":"uint256"},{"internalType":"uint256","name":"rlastInPrecision","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOpenRewardsConfigurations","outputs":[{"components":[{"internalType":"uint256","name":"rOptimal","type":"uint256"},{"internalType":"uint256","name":"maximumRLimit","type":"uint256"},{"internalType":"uint256","name":"minimumRewardAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"minimumMeAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"notifyRewardAmount","type":"uint256"},{"internalType":"uint256","name":"notifyMeAmount","type":"uint256"},{"internalType":"uint256","name":"defaultSlippageInPrecision","type":"uint256"},{"internalType":"bool","name":"allowSwaps","type":"bool"}],"internalType":"struct OpenRewardsData.Config","name":"config","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getOpenRewardsState","outputs":[{"components":[{"internalType":"bool","name":"initialized","type":"bool"},{"internalType":"bool","name":"started","type":"bool"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"busy","type":"bool"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"address","name":"meToken","type":"address"},{"internalType":"uint256","name":"lastRewardAmount","type":"uint256"},{"internalType":"uint256","name":"lastMeAmount","type":"uint256"},{"internalType":"uint256","name":"meTokensFromProtocolTeam","type":"uint256"},{"internalType":"uint256","name":"depositNonce","type":"uint256"},{"internalType":"uint40","name":"lastTransactionTime","type":"uint40"}],"internalType":"struct OpenRewardsData.State","name":"state","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getOptimalRatio","outputs":[{"internalType":"uint256","name":"r","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"meAmount","type":"uint256"},{"internalType":"uint256","name":"slippageInPrecision","type":"uint256"}],"name":"getOutgoingConversationInsight","outputs":[{"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"internalType":"uint256","name":"lastRewardAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"name":"getPositionData","outputs":[{"components":[{"internalType":"uint256","name":"rewardPosition","type":"uint256"},{"internalType":"uint256","name":"mePosition","type":"uint256"}],"internalType":"struct Positions.PositionMetadata","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"},{"internalType":"address","name":"meToken","type":"address"},{"components":[{"internalType":"uint256","name":"rOptimal","type":"uint256"},{"internalType":"uint256","name":"maximumRLimit","type":"uint256"},{"internalType":"uint256","name":"minimumRewardAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"minimumMeAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"notifyRewardAmount","type":"uint256"},{"internalType":"uint256","name":"notifyMeAmount","type":"uint256"},{"internalType":"uint256","name":"defaultSlippageInPrecision","type":"uint256"},{"internalType":"bool","name":"allowSwaps","type":"bool"}],"internalType":"struct Params.ConfigForTypeAOpenRewards","name":"config","type":"tuple"}],"name":"initialize","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rewardAmountIn","type":"uint256"},{"internalType":"uint256","name":"expectedAmountOfOutputReward","type":"uint256"},{"internalType":"address","name":"listener","type":"address"},{"internalType":"uint256","name":"listenerROptimal","type":"uint256"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address","name":"outputRewardReceiver","type":"address"}],"internalType":"struct Params.OutgoingConversationInfo","name":"info","type":"tuple"}],"name":"initiateOutgoingConversation","outputs":[{"components":[{"internalType":"uint256","name":"currentAmountOfMeTokensrewardOne","type":"uint256"},{"internalType":"uint256","name":"currentAmountOfRewardTokensrewardOne","type":"uint256"},{"internalType":"uint256","name":"notifyRewardAmountrewardOne","type":"uint256"},{"internalType":"uint256","name":"notifyMeTokenAmountrewardOne","type":"uint256"},{"internalType":"uint256","name":"currentAmountOfMeTokensRewardTwo","type":"uint256"},{"internalType":"uint256","name":"currentAmountOfRewardTokensRewardTwo","type":"uint256"},{"internalType":"uint256","name":"notifyRewardAmountRewardTwo","type":"uint256"},{"internalType":"uint256","name":"notifyMeTokenAmountRewardTwo","type":"uint256"},{"internalType":"uint256","name":"outputRewardsAmount","type":"uint256"},{"internalType":"uint256","name":"currentDepositNonceRewardOne","type":"uint256"},{"internalType":"uint256","name":"currentDepositNonceRewardTwo","type":"uint256"}],"internalType":"struct Params.InitiateConversationReturnType","name":"conversionDigest","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"name":"leverageIncomingConversationInsight","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"meAmount","type":"uint256"},{"internalType":"uint256","name":"slippageInPrecision","type":"uint256"}],"name":"leverageOutgoingConversationInsight","outputs":[{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseOpenRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"position","type":"uint256"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"internalType":"uint256","name":"meAmount","type":"uint256"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address","name":"to","type":"address"}],"internalType":"struct Params.LiquidityInfo","name":"info","type":"tuple"}],"name":"recordLiquidityProvided","outputs":[{"internalType":"uint256","name":"position","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recordMeTokensProvidedByProtocolTeam","outputs":[{"internalType":"uint256","name":"recordedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes32","name":"releaseType","type":"bytes32"}],"name":"releaseMeTokensProvidedByProtocolTeam","outputs":[{"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"internalType":"uint256","name":"meAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"removeLiquidityManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"removeOpenRewardsManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resumeOpenRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setBusy","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rOptimal","type":"uint256"},{"internalType":"uint256","name":"maximumRLimit","type":"uint256"},{"internalType":"uint256","name":"minimumRewardAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"minimumMeAmountForConversation","type":"uint256"},{"internalType":"uint256","name":"notifyRewardAmount","type":"uint256"},{"internalType":"uint256","name":"notifyMeAmount","type":"uint256"},{"internalType":"uint256","name":"defaultSlippageInPrecision","type":"uint256"},{"internalType":"bool","name":"allowSwaps","type":"bool"}],"internalType":"struct Params.ConfigForTypeAOpenRewards","name":"_config","type":"tuple"}],"name":"setUpConfig","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startOpenRewards","outputs":[{"internalType":"uint256","name":"optimalRatioInPrecision","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"position","type":"uint256"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"internalType":"uint256","name":"meAmount","type":"uint256"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address","name":"to","type":"address"}],"internalType":"struct Params.LiquidityInfo","name":"info","type":"tuple"}],"name":"withdrawLiquidity","outputs":[{"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"internalType":"uint256","name":"meAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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