ETH Price: $1,826.31 (+0.09%)

Transaction Decoder

Block:
16320985 at Jan-02-2023 06:49:35 PM +UTC
Transaction Fee:
0.00199487052128125 ETH $3.64
Gas Used:
103,750 Gas / 19.227667675 Gwei

Emitted Events:

156 RewardDistributor.RewardClaimed( token=0xA52Fd396...cf56B077e, account=[Sender] 0x19e7feccdbeef55cdf6640ae0ed987fdb7764a5c, amount=37329757546244063, updateCount=10 )
157 BTRFLYV2.Transfer( from=[Receiver] RewardDistributor, to=[Sender] 0x19e7feccdbeef55cdf6640ae0ed987fdb7764a5c, amount=1714072968920286591 )
158 RewardDistributor.RewardClaimed( token=BTRFLYV2, account=[Sender] 0x19e7feccdbeef55cdf6640ae0ed987fdb7764a5c, amount=1714072968920286591, updateCount=10 )

Account State Difference:

  Address   Before After State Difference Code
0x19e7FECc...db7764a5c
0.173743275909276254 Eth
Nonce: 57
0.209078162934239067 Eth
Nonce: 58
0.035334887024962813
0xc5512605...e432de5Da
0xd7807E57...50522a76E
(Redacted Cartel: Rewards Distributor)
92.334415394222813651 Eth92.297085636676569588 Eth0.037329757546244063
(Flashbots: Builder)
1.214060230450514463 Eth1.214215855450514463 Eth0.000155625

Execution Trace

RewardDistributor.claim( claims= )
  • ETH 0.037329757546244063 0x19e7feccdbeef55cdf6640ae0ed987fdb7764a5c.CALL( )
  • BTRFLYV2.transfer( to=0x19e7FECcDBEef55CDf6640aE0ED987Fdb7764a5c, amount=1714072968920286591 ) => ( True )
    File 1 of 2: RewardDistributor
    /**
     *Submitted for verification at Etherscan.io on 2022-07-27
    */
    
    // Sources flattened with hardhat v2.9.2 https://hardhat.org
    
    // File @openzeppelin/contracts/utils/[email protected]
    
    
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    
    
    
    /**
     * @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 @openzeppelin/contracts/access/[email protected]
    
    
    // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
    
    
    
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    
    
    // File @rari-capital/solmate/src/utils/[email protected]
    
    
    pragma solidity >=0.8.0;
    
    /// @notice Gas optimized reentrancy protection for smart contracts.
    /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
    abstract contract ReentrancyGuard {
        uint256 private reentrancyStatus = 1;
    
        modifier nonReentrant() {
            require(reentrancyStatus == 1, "REENTRANCY");
    
            reentrancyStatus = 2;
    
            _;
    
            reentrancyStatus = 1;
        }
    }
    
    
    // File @rari-capital/solmate/src/tokens/[email protected]
    
    
    pragma solidity >=0.8.0;
    
    /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
    /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
    /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
    /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
    abstract contract ERC20 {
        /*///////////////////////////////////////////////////////////////
                                      EVENTS
        //////////////////////////////////////////////////////////////*/
    
        event Transfer(address indexed from, address indexed to, uint256 amount);
    
        event Approval(address indexed owner, address indexed spender, uint256 amount);
    
        /*///////////////////////////////////////////////////////////////
                                 METADATA STORAGE
        //////////////////////////////////////////////////////////////*/
    
        string public name;
    
        string public symbol;
    
        uint8 public immutable decimals;
    
        /*///////////////////////////////////////////////////////////////
                                  ERC20 STORAGE
        //////////////////////////////////////////////////////////////*/
    
        uint256 public totalSupply;
    
        mapping(address => uint256) public balanceOf;
    
        mapping(address => mapping(address => uint256)) public allowance;
    
        /*///////////////////////////////////////////////////////////////
                                 EIP-2612 STORAGE
        //////////////////////////////////////////////////////////////*/
    
        bytes32 public constant PERMIT_TYPEHASH =
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    
        uint256 internal immutable INITIAL_CHAIN_ID;
    
        bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
    
        mapping(address => uint256) public nonces;
    
        /*///////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
    
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
    
            INITIAL_CHAIN_ID = block.chainid;
            INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
        }
    
        /*///////////////////////////////////////////////////////////////
                                  ERC20 LOGIC
        //////////////////////////////////////////////////////////////*/
    
        function approve(address spender, uint256 amount) public virtual returns (bool) {
            allowance[msg.sender][spender] = amount;
    
            emit Approval(msg.sender, spender, amount);
    
            return true;
        }
    
        function transfer(address to, uint256 amount) public virtual returns (bool) {
            balanceOf[msg.sender] -= amount;
    
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
    
            emit Transfer(msg.sender, to, amount);
    
            return true;
        }
    
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual returns (bool) {
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
    
            if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
    
            balanceOf[from] -= amount;
    
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
    
            emit Transfer(from, to, amount);
    
            return true;
        }
    
        /*///////////////////////////////////////////////////////////////
                                  EIP-2612 LOGIC
        //////////////////////////////////////////////////////////////*/
    
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual {
            require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
    
            // Unchecked because the only math done is incrementing
            // the owner's nonce which cannot realistically overflow.
            unchecked {
                bytes32 digest = keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                    )
                );
    
                address recoveredAddress = ecrecover(digest, v, r, s);
    
                require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
    
                allowance[recoveredAddress][spender] = value;
            }
    
            emit Approval(owner, spender, value);
        }
    
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
            return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
        }
    
        function computeDomainSeparator() internal view virtual returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                        keccak256(bytes(name)),
                        keccak256("1"),
                        block.chainid,
                        address(this)
                    )
                );
        }
    
        /*///////////////////////////////////////////////////////////////
                           INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
    
        function _mint(address to, uint256 amount) internal virtual {
            totalSupply += amount;
    
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
    
            emit Transfer(address(0), to, amount);
        }
    
        function _burn(address from, uint256 amount) internal virtual {
            balanceOf[from] -= amount;
    
            // Cannot underflow because a user's balance
            // will never be larger than the total supply.
            unchecked {
                totalSupply -= amount;
            }
    
            emit Transfer(from, address(0), amount);
        }
    }
    
    
    // File @rari-capital/solmate/src/utils/[email protected]
    
    
    pragma solidity >=0.8.0;
    
    /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
    /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
    /// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
    /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
    library SafeTransferLib {
        /*///////////////////////////////////////////////////////////////
                                ETH OPERATIONS
        //////////////////////////////////////////////////////////////*/
    
        function safeTransferETH(address to, uint256 amount) internal {
            bool callStatus;
    
            assembly {
                // Transfer the ETH and store if it succeeded or not.
                callStatus := call(gas(), to, amount, 0, 0, 0, 0)
            }
    
            require(callStatus, "ETH_TRANSFER_FAILED");
        }
    
        /*///////////////////////////////////////////////////////////////
                               ERC20 OPERATIONS
        //////////////////////////////////////////////////////////////*/
    
        function safeTransferFrom(
            ERC20 token,
            address from,
            address to,
            uint256 amount
        ) internal {
            bool callStatus;
    
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
    
                // Write the abi-encoded calldata to memory piece by piece:
                mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
                mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
    
                // Call the token and store if it succeeded or not.
                // We use 100 because the calldata length is 4 + 32 * 3.
                callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
            }
    
            require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
        }
    
        function safeTransfer(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool callStatus;
    
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
    
                // Write the abi-encoded calldata to memory piece by piece:
                mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
    
                // Call the token and store if it succeeded or not.
                // We use 68 because the calldata length is 4 + 32 * 2.
                callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
            }
    
            require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
        }
    
        function safeApprove(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool callStatus;
    
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
    
                // Write the abi-encoded calldata to memory piece by piece:
                mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
    
                // Call the token and store if it succeeded or not.
                // We use 68 because the calldata length is 4 + 32 * 2.
                callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
            }
    
            require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
        }
    
        /*///////////////////////////////////////////////////////////////
                             INTERNAL HELPER LOGIC
        //////////////////////////////////////////////////////////////*/
    
        function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
            assembly {
                // Get how many bytes the call returned.
                let returnDataSize := returndatasize()
    
                // If the call reverted:
                if iszero(callStatus) {
                    // Copy the revert message into memory.
                    returndatacopy(0, 0, returnDataSize)
    
                    // Revert with the same message.
                    revert(0, returnDataSize)
                }
    
                switch returnDataSize
                case 32 {
                    // Copy the return data into memory.
                    returndatacopy(0, 0, returnDataSize)
    
                    // Set success to whether it returned true.
                    success := iszero(iszero(mload(0)))
                }
                case 0 {
                    // There was no return data.
                    success := 1
                }
                default {
                    // It returned some malformed input.
                    success := 0
                }
            }
        }
    }
    
    
    // File @openzeppelin/contracts/utils/cryptography/[email protected]
    
    
    // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/MerkleProof.sol)
    
    
    
    /**
     * @dev These functions deal with verification of Merkle Trees proofs.
     *
     * The proofs can be generated using the JavaScript library
     * https://github.com/miguelmota/merkletreejs[merkletreejs].
     * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
     *
     * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
     */
    library MerkleProof {
        /**
         * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
         * defined by `root`. For this, a `proof` must be provided, containing
         * sibling hashes on the branch from the leaf to the root of the tree. Each
         * pair of leaves and each pair of pre-images are assumed to be sorted.
         */
        function verify(
            bytes32[] memory proof,
            bytes32 root,
            bytes32 leaf
        ) internal pure returns (bool) {
            return processProof(proof, leaf) == root;
        }
    
        /**
         * @dev Returns the rebuilt hash obtained by traversing a Merklee tree up
         * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
         * hash matches the root of the tree. When processing the proof, the pairs
         * of leafs & pre-images are assumed to be sorted.
         *
         * _Available since v4.4._
         */
        function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
            bytes32 computedHash = leaf;
            for (uint256 i = 0; i < proof.length; i++) {
                bytes32 proofElement = proof[i];
                if (computedHash <= proofElement) {
                    // Hash(current computed hash + current element of the proof)
                    computedHash = _efficientHash(computedHash, proofElement);
                } else {
                    // Hash(current element of the proof + current computed hash)
                    computedHash = _efficientHash(proofElement, computedHash);
                }
            }
            return computedHash;
        }
    
        function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
            assembly {
                mstore(0x00, a)
                mstore(0x20, b)
                value := keccak256(0x00, 0x40)
            }
        }
    }
    
    
    // File contracts/core/RewardDistributor.sol
    
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.12;
    
    
    
    
    
    /// @title RewardDistributor
    /// @author ████
    
    /**
        @notice
        Adapted from Hidden-Hand's RewardDistributor
    */
    
    contract RewardDistributor is Ownable, ReentrancyGuard {
        using SafeTransferLib for ERC20;
    
        struct Distribution {
            address token;
            bytes32 merkleRoot;
            bytes32 proof;
        }
    
        struct Reward {
            address token;
            bytes32 merkleRoot;
            bytes32 proof;
            uint256 updateCount;
        }
    
        struct Claim {
            address token;
            address account;
            uint256 amount;
            bytes32[] merkleProof;
        }
    
        // Address of the Multisig (also as the primary source of rewards)
        address public immutable MULTISIG;
    
        // Maps each of the token address to its reward metadata
        mapping(address => Reward) public rewards;
        // Tracks the amount of claimed reward for the specified token address + account
        mapping(address => mapping(address => uint256)) public claimed;
    
        event RewardClaimed(
            address indexed token,
            address indexed account,
            uint256 amount,
            uint256 updateCount
        );
    
        event RewardMetadataUpdated(
            address indexed token,
            bytes32 merkleRoot,
            bytes32 proof,
            uint256 indexed updateCount
        );
    
        constructor(address multisig) {
            require(multisig != address(0), "Invalid address");
            MULTISIG = multisig;
        }
    
        /**
            @notice Enables and restricts native token ingress to Multisig
         */
        receive() external payable {
            if (msg.sender != MULTISIG) revert("Not MULTISIG");
        }
    
        /**
            @notice Claim rewards based on the specified metadata
            @param  claims  Claim[] List of claim metadata
         */
        function claim(Claim[] calldata claims) external nonReentrant {
            require(claims.length != 0, "Invalid claims");
    
            for (uint256 i; i < claims.length; ++i) {
                _claim(
                    claims[i].token,
                    claims[i].account,
                    claims[i].amount,
                    claims[i].merkleProof
                );
            }
        }
    
        /**
            @notice Update rewards metadata
            @param  distributions  Distribution[] List of reward distribution details
         */
        function updateRewardsMetadata(Distribution[] calldata distributions)
            external
            onlyOwner
        {
            require(distributions.length != 0, "Invalid distributions");
    
            for (uint256 i; i < distributions.length; ++i) {
                // Update the metadata and also increment the update counter
                Distribution calldata distribution = distributions[i];
                Reward storage reward = rewards[distribution.token];
                reward.token = distribution.token;
                reward.merkleRoot = distribution.merkleRoot;
                reward.proof = distribution.proof;
                ++reward.updateCount;
    
                emit RewardMetadataUpdated(
                    distribution.token,
                    distribution.merkleRoot,
                    distribution.proof,
                    reward.updateCount
                );
            }
        }
    
        /**
            @notice Claim a reward
            @param  token        address    Token address
            @param  account      address    Eligible user account
            @param  amount       uint256    Reward amount
            @param  merkleProof  bytes32[]  Merkle proof
         */
        function _claim(
            address token,
            address account,
            uint256 amount,
            bytes32[] calldata merkleProof
        ) private {
            Reward memory reward = rewards[token];
    
            require(reward.merkleRoot != 0, "Distribution not enabled");
    
            // Verify the merkle proof
            require(
                MerkleProof.verify(
                    merkleProof,
                    reward.merkleRoot,
                    keccak256(abi.encodePacked(account, amount))
                ),
                "Invalid proof"
            );
    
            // Verify the claimable amount
            require(claimed[token][account] < amount, "No claimable reward");
    
            // Calculate the claimable amount based off the total of reward (used in the merkle tree)
            // since the beginning for the user, minus the total claimed so far
            uint256 claimable = amount - claimed[token][account];
            // Update the claimed amount to the current total
            claimed[token][account] = amount;
    
            // Check whether the reward is in the form of native tokens or ERC20
            // by checking if the token address is set to the Multisig or not
            if (token != MULTISIG) {
                ERC20(token).safeTransfer(account, claimable);
            } else {
                (bool sent, ) = payable(account).call{value: claimable}("");
                require(sent, "Failed to transfer to account");
            }
    
            emit RewardClaimed(
                token,
                account,
                claimable,
                reward.updateCount
            );
        }
    }

    File 2 of 2: BTRFLYV2
    // Sources flattened with hardhat v2.9.2 https://hardhat.org
    
    // File @rari-capital/solmate/src/tokens/[email protected]
    
    
    pragma solidity >=0.8.0;
    
    /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
    /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
    /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
    /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
    abstract contract ERC20 {
        /*///////////////////////////////////////////////////////////////
                                      EVENTS
        //////////////////////////////////////////////////////////////*/
    
        event Transfer(address indexed from, address indexed to, uint256 amount);
    
        event Approval(address indexed owner, address indexed spender, uint256 amount);
    
        /*///////////////////////////////////////////////////////////////
                                 METADATA STORAGE
        //////////////////////////////////////////////////////////////*/
    
        string public name;
    
        string public symbol;
    
        uint8 public immutable decimals;
    
        /*///////////////////////////////////////////////////////////////
                                  ERC20 STORAGE
        //////////////////////////////////////////////////////////////*/
    
        uint256 public totalSupply;
    
        mapping(address => uint256) public balanceOf;
    
        mapping(address => mapping(address => uint256)) public allowance;
    
        /*///////////////////////////////////////////////////////////////
                                 EIP-2612 STORAGE
        //////////////////////////////////////////////////////////////*/
    
        bytes32 public constant PERMIT_TYPEHASH =
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    
        uint256 internal immutable INITIAL_CHAIN_ID;
    
        bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
    
        mapping(address => uint256) public nonces;
    
        /*///////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
    
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
    
            INITIAL_CHAIN_ID = block.chainid;
            INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
        }
    
        /*///////////////////////////////////////////////////////////////
                                  ERC20 LOGIC
        //////////////////////////////////////////////////////////////*/
    
        function approve(address spender, uint256 amount) public virtual returns (bool) {
            allowance[msg.sender][spender] = amount;
    
            emit Approval(msg.sender, spender, amount);
    
            return true;
        }
    
        function transfer(address to, uint256 amount) public virtual returns (bool) {
            balanceOf[msg.sender] -= amount;
    
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
    
            emit Transfer(msg.sender, to, amount);
    
            return true;
        }
    
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual returns (bool) {
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
    
            if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
    
            balanceOf[from] -= amount;
    
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
    
            emit Transfer(from, to, amount);
    
            return true;
        }
    
        /*///////////////////////////////////////////////////////////////
                                  EIP-2612 LOGIC
        //////////////////////////////////////////////////////////////*/
    
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual {
            require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
    
            // Unchecked because the only math done is incrementing
            // the owner's nonce which cannot realistically overflow.
            unchecked {
                bytes32 digest = keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                    )
                );
    
                address recoveredAddress = ecrecover(digest, v, r, s);
    
                require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
    
                allowance[recoveredAddress][spender] = value;
            }
    
            emit Approval(owner, spender, value);
        }
    
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
            return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
        }
    
        function computeDomainSeparator() internal view virtual returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                        keccak256(bytes(name)),
                        keccak256("1"),
                        block.chainid,
                        address(this)
                    )
                );
        }
    
        /*///////////////////////////////////////////////////////////////
                           INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
    
        function _mint(address to, uint256 amount) internal virtual {
            totalSupply += amount;
    
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
    
            emit Transfer(address(0), to, amount);
        }
    
        function _burn(address from, uint256 amount) internal virtual {
            balanceOf[from] -= amount;
    
            // Cannot underflow because a user's balance
            // will never be larger than the total supply.
            unchecked {
                totalSupply -= amount;
            }
    
            emit Transfer(from, address(0), amount);
        }
    }
    
    
    // File @openzeppelin/contracts/access/[email protected]
    
    
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    
    
    
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         *
         * _Available since v3.1._
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
    
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
    
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
    
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
    
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
    
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
    
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
    
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) external;
    }
    
    
    // File @openzeppelin/contracts/utils/[email protected]
    
    
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    
    
    
    /**
     * @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 @openzeppelin/contracts/utils/[email protected]
    
    
    // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
    
    
    
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
    
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
    
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
    
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
    
        /**
         * @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] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
    }
    
    
    // File @openzeppelin/contracts/utils/introspection/[email protected]
    
    
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    
    
    
    /**
     * @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 @openzeppelin/contracts/utils/introspection/[email protected]
    
    
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.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 @openzeppelin/contracts/access/[email protected]
    
    
    // OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)
    
    
    
    
    
    
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address => bool) members;
            bytes32 adminRole;
        }
    
        mapping(bytes32 => RoleData) private _roles;
    
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
    
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with a standardized message including the required role.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         *
         * _Available since v4.1._
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role, _msgSender());
            _;
        }
    
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
    
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
            return _roles[role].members[account];
        }
    
        /**
         * @dev Revert with a standard message if `account` is missing `role`.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert(
                    string(
                        abi.encodePacked(
                            "AccessControl: account ",
                            Strings.toHexString(uint160(account), 20),
                            " is missing role ",
                            Strings.toHexString(uint256(role), 32)
                        )
                    )
                );
            }
        }
    
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
            return _roles[role].adminRole;
        }
    
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
    
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
    
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) public virtual override {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
    
            _revokeRole(role, account);
        }
    
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         *
         * NOTE: This function is deprecated in favor of {_grantRole}.
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
    
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
    
        /**
         * @dev Grants `role` to `account`.
         *
         * Internal function without access restriction.
         */
        function _grantRole(bytes32 role, address account) internal virtual {
            if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
            }
        }
    
        /**
         * @dev Revokes `role` from `account`.
         *
         * Internal function without access restriction.
         */
        function _revokeRole(bytes32 role, address account) internal virtual {
            if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }
    
    
    // File contracts/core/BTRFLYV2.sol
    
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.12;
    
    
    /// @title BTRFLYV2
    /// @author Realkinando
    
    /**
        @notice 
        Minimum viable token for BTRFLYV2, follows same patterns as V1 token, but with improved readability
    */
    
    contract BTRFLYV2 is AccessControl, ERC20("BTRFLY", "BTRFLY", 18) {
        bytes32 public constant MINTER_ROLE = "MINTER_ROLE";
    
        constructor() {
            _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        }
    
        /**
            @notice Mint tokens
            @param  to      address  Address to receive tokens
            @param  amount  uint256  Amount to mint
         */
        function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
            _mint(to, amount);
        }
    
        /**
            @notice Burn tokens
            @param  amount  uint256  Amount to burn
         */
        function burn(uint256 amount) external {
            _burn(msg.sender, amount);
        }
    }