// File: contracts/D4AUniversalClaimer.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

import { ID4AProtocolReadable } from "contracts/interface/ID4AProtocolReadable.sol";
import { ID4AProtocol } from "contracts/interface/ID4AProtocol.sol";

contract D4AUniversalClaimer {
    struct ClaimMultiRewardParam {
        address protocol;
        bytes32[] canvasIds;
        bytes32[] daoIds;
    }

    function claimMultiReward(ClaimMultiRewardParam[] calldata params) public returns (uint256 tokenAmount) {
        for (uint256 i; i < params.length;) {
            for (uint256 j; j < params[i].canvasIds.length;) {
                tokenAmount += ID4AProtocol(params[i].protocol).claimCanvasReward(params[i].canvasIds[j]);
                unchecked {
                    ++j;
                }
            }
            for (uint256 j; j < params[i].daoIds.length;) {
                tokenAmount += ID4AProtocol(params[i].protocol).claimProjectERC20Reward(params[i].daoIds[j]);
                tokenAmount += ID4AProtocol(params[i].protocol).claimNftMinterReward(params[i].daoIds[j], msg.sender);
                unchecked {
                    ++j;
                }
            }
            unchecked {
                ++i;
            }
        }

        return tokenAmount;
    }
}

// File: contracts/interface/ID4AProtocolReadable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import { RewardStorage } from "contracts/storages/RewardStorage.sol";

interface ID4AProtocolReadable {
    // legacy functions
    function getProjectCanvasAt(bytes32 daoId, uint256 index) external view returns (bytes32);

    function getProjectInfo(bytes32 daoId)
        external
        view
        returns (
            uint256 startRound,
            uint256 mintableRound,
            uint256 maxNftAmount,
            address daoFeePool,
            uint96 royaltyFeeRatioInBps,
            uint256 index,
            string memory daoUri,
            uint256 erc20TotalSupply
        );

    function getProjectFloorPrice(bytes32 daoId) external view returns (uint256);

    function getProjectTokens(bytes32 daoId) external view returns (address token, address nft);

    function getCanvasNFTCount(bytes32 canvasId) external view returns (uint256);

    function getTokenIDAt(bytes32 canvasId, uint256 index) external view returns (uint256);

    function getCanvasProject(bytes32 canvasId) external view returns (bytes32);

    function getCanvasURI(bytes32 canvasId) external view returns (string memory);

    function getProjectCanvasCount(bytes32 daoId) external view returns (uint256);

    // new functions
    // DAO related functions
    function getDaoStartRound(bytes32 daoId) external view returns (uint256 startRound);

    function getDaoMintableRound(bytes32 daoId) external view returns (uint256 mintableRound);

    function getDaoIndex(bytes32 daoId) external view returns (uint256 index);

    function getDaoUri(bytes32 daoId) external view returns (string memory daoUri);

    function getDaoFeePool(bytes32 daoId) external view returns (address daoFeePool);

    function getDaoToken(bytes32 daoId) external view returns (address token);

    function getDaoTokenMaxSupply(bytes32 daoId) external view returns (uint256 tokenMaxSupply);

    function getDaoNft(bytes32 daoId) external view returns (address nft);

    function getDaoNftMaxSupply(bytes32 daoId) external view returns (uint256 nftMaxSupply);

    function getDaoNftTotalSupply(bytes32 daoId) external view returns (uint256 nftTotalSupply);

    function getDaoNftRoyaltyFeeRatioInBps(bytes32 daoId) external view returns (uint96 royaltyFeeRatioInBps);

    function getDaoExist(bytes32 daoId) external view returns (bool);

    function getDaoCanvases(bytes32 daoId) external view returns (bytes32[] memory canvases);

    function getDaoPriceTemplate(bytes32 daoId) external view returns (address priceTemplate);

    function getDaoPriceFactor(bytes32 daoId) external view returns (uint256 priceFactor);

    function getDaoRewardTemplate(bytes32 daoId) external view returns (address rewardTemplate);

    function getDaoMintCap(bytes32 daoId) external view returns (uint32);

    function getUserMintInfo(
        bytes32 daoId,
        address account
    )
        external
        view
        returns (uint32 minted, uint32 userMintCap);

    function getDaoFeePoolETHRatio(bytes32 daoId) external view returns (uint256);

    function getDaoFeePoolETHRatioFlatPrice(bytes32 daoId) external view returns (uint256);

    // canvas related functions
    function getCanvasDaoId(bytes32 canvasId) external view returns (bytes32 daoId);

    function getCanvasTokenIds(bytes32 canvasId) external view returns (uint256[] memory tokenIds);

    function getCanvasIndex(bytes32 canvasId) external view returns (uint256);

    function getCanvasUri(bytes32 canvasId) external view returns (string memory canvasUri);

    function getCanvasRebateRatioInBps(bytes32 canvasId) external view returns (uint256 rebateRatioInBps);

    function getCanvasExist(bytes32 canvasId) external view returns (bool);

    // prices related functions
    function getCanvasLastPrice(bytes32 canvasId) external view returns (uint256 round, uint256 price);

    function getCanvasNextPrice(bytes32 canvasId) external view returns (uint256 price);

    function getDaoMaxPriceInfo(bytes32 daoId) external view returns (uint256 round, uint256 price);

    function getDaoFloorPrice(bytes32 daoId) external view returns (uint256 floorPrice);

    // reward related functions
    function getDaoRewardStartRound(
        bytes32 daoId,
        uint256 rewardCheckpointIndex
    )
        external
        view
        returns (uint256 startRound);

    function getDaoRewardTotalRound(
        bytes32 daoId,
        uint256 rewardCheckpointIndex
    )
        external
        view
        returns (uint256 totalRound);

    function getDaoTotalReward(
        bytes32 daoId,
        uint256 rewardCheckpointIndex
    )
        external
        view
        returns (uint256 totalReward);

    function getDaoRewardDecayFactor(bytes32 daoId) external view returns (uint256 rewardDecayFactor);

    function getDaoRewardIsProgressiveJackpot(bytes32 daoId) external view returns (bool isProgressiveJackpot);

    function getDaoRewardLastActiveRound(
        bytes32 daoId,
        uint256 rewardCheckpointIndex
    )
        external
        view
        returns (uint256 lastActiveRound);

    function getDaoRewardActiveRounds(
        bytes32 daoId,
        uint256 rewardCheckpointIndex
    )
        external
        view
        returns (uint256[] memory activeRounds);

    function getDaoCreatorClaimableRound(
        bytes32 daoId,
        uint256 rewardCheckpointsIndex
    )
        external
        view
        returns (uint256 claimableRound);

    function getCanvasCreatorClaimableRound(
        bytes32 daoId,
        uint256 rewardCheckpointsIndex,
        bytes32 canvasId
    )
        external
        view
        returns (uint256 claimableRound);

    function getNftMinterClaimableRound(
        bytes32 daoId,
        uint256 rewardCheckpointsIndex,
        address nftMinter
    )
        external
        view
        returns (uint256 claimableRound);

    function getTotalWeight(bytes32 daoId, uint256 round) external view returns (uint256 totalWeight);

    function getProtocolWeight(bytes32 daoId, uint256 round) external view returns (uint256 protocolWeight);

    function getDaoCreatorWeight(bytes32 daoId, uint256 round) external view returns (uint256 creatorWeight);

    function getCanvasCreatorWeight(
        bytes32 daoId,
        uint256 round,
        bytes32 canvasId
    )
        external
        view
        returns (uint256 creatorWeight);

    function getNftMinterWeight(
        bytes32 daoId,
        uint256 round,
        address nftMinter
    )
        external
        view
        returns (uint256 minterWeight);

    function getDaoCreatorERC20Ratio(bytes32 daoId) external view returns (uint256 ratioInBps);

    function getCanvasCreatorERC20Ratio(bytes32 daoId) external view returns (uint256 ratioInBps);

    function getNftMinterERC20Ratio(bytes32 daoId) external view returns (uint256 ratioInBps);

    function getRoundReward(bytes32 daoId, uint256 round) external view returns (uint256);

    function getRewardTillRound(bytes32 daoId, uint256 round) external view returns (uint256);
}

// File: contracts/interface/ID4AProtocol.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import { DaoMetadataParam, UserMintCapParam, TemplateParam } from "./D4AStructs.sol";
import { IPermissionControl } from "contracts/interface/IPermissionControl.sol";

interface ID4AProtocol {
    event NewProject(
        bytes32 daoId, string daoUri, address daoFeePool, address token, address nft, uint256 royaltyFeeRatioInBps
    );

    event NewCanvas(bytes32 daoId, bytes32 canvasId, string canvasUri);

    event D4AMintNFT(bytes32 daoId, bytes32 canvasId, uint256 tokenId, string tokenUri, uint256 price);

    event D4AClaimProjectERC20Reward(bytes32 daoId, address token, uint256 amount);

    event D4AClaimCanvasReward(bytes32 daoId, bytes32 canvasId, address token, uint256 amount);

    event D4AClaimNftMinterReward(bytes32 daoId, address token, uint256 amount);

    event D4AExchangeERC20ToETH(bytes32 daoId, address owner, address to, uint256 tokenAmount, uint256 ethAmount);

    function createProject(
        uint256 startRound,
        uint256 mintableRound,
        uint256 daoFloorPriceRank,
        uint256 maxNftRank,
        uint96 royaltyFeeRatioInBps,
        string memory daoUri
    )
        external
        payable
        returns (bytes32 daoId);

    function createOwnerProject(DaoMetadataParam calldata daoMetadata) external payable returns (bytes32 daoId);

    function claimProjectERC20Reward(bytes32 daoId) external returns (uint256);

    function claimCanvasReward(bytes32 canvasId) external returns (uint256);

    function claimNftMinterReward(bytes32 daoId, address minter) external returns (uint256);

    function exchangeERC20ToETH(bytes32 daoId, uint256 amount, address to) external returns (uint256);
}

// File: contracts/storages/RewardStorage.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

library RewardStorage {
    struct RewardCheckpoint {
        uint256 startRound;
        uint256 totalRound;
        uint256 totalReward;
        uint256 lastActiveRound; // deprecated
        uint256[] activeRounds;
        // claimable round index
        uint256 daoCreatorClaimableRoundIndex;
        mapping(bytes32 canvasId => uint256 claimableRoundIndex) canvasCreatorClaimableRoundIndexes;
        mapping(address nftMinter => uint256 claimableRoundIndex) nftMinterClaimableRoundIndexes;
    }

    struct RewardInfo {
        RewardCheckpoint[] rewardCheckpoints;
        uint256 rewardIssuePendingRound;
        uint256 rewardDecayFactor;
        bool isProgressiveJackpot;
        // weights
        mapping(uint256 round => uint256 totalWeight) totalWeights; // also total ETH in DAO fee pool at given round
        mapping(uint256 round => uint256 weight) protocolWeights;
        mapping(uint256 round => uint256 weight) daoCreatorWeights;
        mapping(uint256 round => mapping(bytes32 canvasId => uint256 weight)) canvasCreatorWeights;
        mapping(uint256 round => mapping(address nftMinter => uint256 weight)) nftMinterWeights;
        uint256 daoCreatorERC20RatioInBps;
        uint256 canvasCreatorERC20RatioInBps;
    }

    struct Layout {
        mapping(bytes32 daoId => RewardInfo rewardInfo) rewardInfos;
    }

    bytes32 internal constant STORAGE_SLOT = keccak256("D4Av2.contracts.storage.RewardStorage");

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }
}

// File: contracts/interface/D4AStructs.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { PriceTemplateType, RewardTemplateType } from "./D4AEnums.sol";

struct DaoMetadataParam {
    uint256 startDrb;
    uint256 mintableRounds;
    uint256 floorPriceRank;
    uint256 maxNftRank;
    uint96 royaltyFee;
    string projectUri;
    uint256 projectIndex;
}

struct DaoMintInfo {
    uint32 daoMintCap;
    mapping(address minter => UserMintInfo) userMintInfos;
}

struct UserMintInfo {
    uint32 minted;
    uint32 mintCap;
}

struct DaoMintCapParam {
    uint32 daoMintCap;
    UserMintCapParam[] userMintCapParams;
}

struct UserMintCapParam {
    address minter;
    uint32 mintCap;
}

struct DaoETHAndERC20SplitRatioParam {
    uint256 daoCreatorERC20Ratio;
    uint256 canvasCreatorERC20Ratio;
    uint256 nftMinterERC20Ratio;
    uint256 daoFeePoolETHRatio;
    uint256 daoFeePoolETHRatioFlatPrice;
}

struct TemplateParam {
    PriceTemplateType priceTemplateType;
    uint256 priceFactor;
    RewardTemplateType rewardTemplateType;
    uint256 rewardDecayFactor;
    bool isProgressiveJackpot;
}

struct UpdateRewardParam {
    bytes32 daoId;
    bytes32 canvasId;
    address token;
    uint256 startRound;
    uint256 currentRound;
    uint256 totalRound;
    uint256 daoFeeAmount;
    uint256 protocolERC20RatioInBps;
    uint256 daoCreatorERC20RatioInBps;
    uint256 canvasCreatorERC20RatioInBps;
    uint256 nftMinterERC20RatioInBps;
    uint256 canvasRebateRatioInBps;
}

struct MintNftInfo {
    string tokenUri;
    uint256 flatPrice;
}

struct MintVars {
    uint256 price;
    uint256 daoTotalShare;
    uint256 totalPrice;
    uint256 daoFee;
    bool needUpdatePrice;
}

struct Blacklist {
    address[] minterAccounts;
    address[] canvasCreatorAccounts;
}

struct Whitelist {
    bytes32 minterMerkleRoot;
    address[] minterNFTHolderPasses;
    bytes32 canvasCreatorMerkleRoot;
    address[] canvasCreatorNFTHolderPasses;
}

// File: contracts/interface/IPermissionControl.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

import { Whitelist, Blacklist } from "contracts/interface/D4AStructs.sol";
import { ID4AOwnerProxy } from "contracts/interface/ID4AOwnerProxy.sol";

interface IPermissionControl {
    event MinterBlacklisted(bytes32 indexed daoId, address indexed account);

    event CanvasCreatorBlacklisted(bytes32 indexed daoId, address indexed account);

    event MinterUnBlacklisted(bytes32 indexed daoId, address indexed account);

    event CanvasCreatorUnBlacklisted(bytes32 indexed daoId, address indexed account);

    event WhitelistModified(bytes32 indexed daoId, Whitelist whitelist);

    function getWhitelist(bytes32 daoId) external view returns (Whitelist calldata whitelist);

    function addPermissionWithSignature(
        bytes32 daoId,
        Whitelist calldata whitelist,
        Blacklist calldata blacklist,
        bytes calldata signature
    )
        external;

    function addPermission(bytes32 daoId, Whitelist calldata whitelist, Blacklist calldata blacklist) external;

    function modifyPermission(
        bytes32 daoId,
        Whitelist calldata whitelist,
        Blacklist calldata blacklist,
        Blacklist calldata unblacklist
    )
        external;

    function isMinterBlacklisted(bytes32 daoId, address _account) external view returns (bool);

    function isCanvasCreatorBlacklisted(bytes32 daoId, address _account) external view returns (bool);

    function inMinterWhitelist(
        bytes32 daoId,
        address _account,
        bytes32[] calldata _proof
    )
        external
        view
        returns (bool);

    function inCanvasCreatorWhitelist(
        bytes32 daoId,
        address _account,
        bytes32[] calldata _proof
    )
        external
        view
        returns (bool);

    function setOwnerProxy(ID4AOwnerProxy _ownerProxy) external;
}

// File: contracts/interface/D4AEnums.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

enum PriceTemplateType {
    EXPONENTIAL_PRICE_VARIATION,
    LINEAR_PRICE_VARIATION
}

enum RewardTemplateType {
    LINEAR_REWARD_ISSUANCE,
    EXPONENTIAL_REWARD_ISSUANCE
}

enum TemplateChoice {
    PRICE,
    REWARD
}

// File: contracts/interface/ID4AOwnerProxy.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

interface ID4AOwnerProxy {
    function ownerOf(bytes32 hash) external view returns (address);
    function initOwnerOf(bytes32 hash, address addr) external returns (bool);
    function transferOwnership(bytes32 hash, address newOwner) external;
}