DIHBurnEvent

0x31ffab7e0bdc5ed96bbc15e76291582ac82aef30

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
3,950 bytes
ABI entries
53
22 read · 7 write
License
none

Contract information

Address
0x31ffab7e0bdc5ed96bbc15e76291582ac82aef30
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0x13e17c51e1…462b4ce26e

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (22)

DEAD_ADDRESS()address
DEFAULT_ADMIN_ROLE()bytes32
MANAGER_ROLE()bytes32
burnTransactionCount()uint256
burnedBy(address)uint256
contractDIHBalance()uint256
dih()address
endTime()uint64
eventActive()bool
eventEnded()bool
getRoleAdmin(bytes32)bytes32
hasParticipated(address)bool
hasRole(bytes32, address)bool
minimumBurn()uint256
participantAt(uint256)address
participantCount()uint256
participantsLength()uint256
paused()bool
scheduleConfigured()bool
startTime()uint64
supportsInterface(bytes4)bool
totalBurned()uint256

Events (7)

DIHBurnedPausedRoleAdminChangedRoleGrantedRoleRevokedScheduleConfiguredUnpaused

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "dihToken",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "admin",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "minimumBurnAmount",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AccessControlBadConfirmation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "internalType": "bytes32",
        "name": "neededRole",
        "type": "bytes32"
      }
    ],
    "name": "AccessControlUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "received",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minimum",
        "type": "uint256"
      }
    ],
    "name": "BurnBelowMinimum",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EnforcedPause",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EventEnded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EventNotStarted",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ExpectedPause",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidMinimumBurn",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidSchedule",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ScheduleAlreadyConfigured",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ScheduleNotConfigured",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "balanceBefore",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "balanceAfter",
        "type": "uint256"
      }
    ],
    "name": "UnexpectedTokenAccounting",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAmount",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "burner",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "requestedAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "burnedAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "walletTotalBurned",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "eventTotalBurned",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "participantCount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "burnTransactionCount",
        "type": "uint256"
      }
    ],
    "name": "DIHBurned",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "Paused",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "previousAdminRole",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "newAdminRole",
        "type": "bytes32"
      }
    ],
    "name": "RoleAdminChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleGranted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleRevoked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint64",
        "name": "startTime",
        "type": "uint64"
      },
      {
        "indexed": true,
        "internalType": "uint64",
        "name": "endTime",
        "type": "uint64"
      }
    ],
    "name": "ScheduleConfigured",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "Unpaused",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DEAD_ADDRESS",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEFAULT_ADMIN_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MANAGER_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "requestedAmount",
        "type": "uint256"
      }
    ],
    "name": "burn",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "burnTransactionCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "burnedBy",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint64",
        "name": "newStartTime",
        "type": "uint64"
      },
      {
        "internalType": "uint64",
        "name": "newEndTime",
        "type": "uint64"
      }
    ],
    "name": "configureSchedule",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "contractDIHBalance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "dih",
    "outputs": [
      {
        "internalType": "contract IERC20",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "endTime",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "eventActive",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "eventEnded",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      }
    ],
    "name": "getRoleAdmin",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "grantRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "hasParticipated",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "hasRole",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "minimumBurn",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      }
    ],
    "name": "participantAt",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "participantCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "participantsLength",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pause",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "paused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "callerConfirmation",
        "type": "address"
      }
    ],
    "name": "renounceRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "revokeRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "scheduleConfigured",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "startTime",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }
    ],
    "name": "supportsInterface",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalBurned",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "unpause",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Source code

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

import {AccessControl} from
    "@openzeppelin/contracts/access/AccessControl.sol";

import {Pausable} from
    "@openzeppelin/contracts/utils/Pausable.sol";

import {ReentrancyGuard} from
    "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

import {IERC20} from
    "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {SafeERC20} from
    "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
 * @title DIHBurnEvent
 * @notice Limited-time DOGINHOOD community burn event.
 *
 * Users approve this contract, then call burn(amount).
 * Tokens move directly from the user to the dead address.
 *
 * The contract is used only for:
 * - enforcing the minimum burn;
 * - enforcing the event window;
 * - tracking participants;
 * - tracking total amounts burned;
 * - emitting public on-chain statistics.
 *
 * This contract does not select a winner and does not distribute rewards.
 */
contract DIHBurnEvent is
    AccessControl,
    Pausable,
    ReentrancyGuard
{
    using SafeERC20 for IERC20;

    bytes32 public constant MANAGER_ROLE =
        keccak256("MANAGER_ROLE");

    address public constant DEAD_ADDRESS =
        0x000000000000000000000000000000000000dEaD;

    IERC20 public immutable dih;
    uint256 public immutable minimumBurn;

    uint64 public startTime;
    uint64 public endTime;

    bool public scheduleConfigured;

    uint256 public totalBurned;
    uint256 public participantCount;
    uint256 public burnTransactionCount;

    mapping(address => uint256) public burnedBy;
    mapping(address => bool) public hasParticipated;

    address[] private participants;

    error ZeroAddress();
    error ZeroAmount();
    error InvalidMinimumBurn();
    error InvalidSchedule();
    error ScheduleAlreadyConfigured();
    error ScheduleNotConfigured();
    error EventNotStarted();
    error EventEnded();
    error BurnBelowMinimum(
        uint256 received,
        uint256 minimum
    );
    error UnexpectedTokenAccounting(
        uint256 balanceBefore,
        uint256 balanceAfter
    );

    event ScheduleConfigured(
        uint64 indexed startTime,
        uint64 indexed endTime
    );

    event DIHBurned(
        address indexed burner,
        uint256 requestedAmount,
        uint256 burnedAmount,
        uint256 walletTotalBurned,
        uint256 eventTotalBurned,
        uint256 participantCount,
        uint256 burnTransactionCount
    );

    constructor(
        address dihToken,
        address admin,
        uint256 minimumBurnAmount
    ) {
        if (
            dihToken == address(0) ||
            admin == address(0)
        ) {
            revert ZeroAddress();
        }

        if (minimumBurnAmount == 0) {
            revert InvalidMinimumBurn();
        }

        dih = IERC20(dihToken);
        minimumBurn = minimumBurnAmount;

        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MANAGER_ROLE, admin);

        _pause();
    }

    /**
     * @notice Configures the event window exactly once.
     * @dev Must be called while the contract is paused.
     */
    function configureSchedule(
        uint64 newStartTime,
        uint64 newEndTime
    )
        external
        onlyRole(MANAGER_ROLE)
        whenPaused
    {
        if (scheduleConfigured) {
            revert ScheduleAlreadyConfigured();
        }

        if (
            newStartTime <= block.timestamp ||
            newEndTime <= newStartTime
        ) {
            revert InvalidSchedule();
        }

        startTime = newStartTime;
        endTime = newEndTime;
        scheduleConfigured = true;

        emit ScheduleConfigured(
            newStartTime,
            newEndTime
        );
    }

    /**
     * @notice Permanently burns DIH by sending it directly
     *         from the caller to the dead address.
     */
    function burn(
        uint256 requestedAmount
    )
        external
        nonReentrant
        whenNotPaused
    {
        if (!scheduleConfigured) {
            revert ScheduleNotConfigured();
        }

        if (block.timestamp < startTime) {
            revert EventNotStarted();
        }

        if (block.timestamp >= endTime) {
            revert EventEnded();
        }

        if (requestedAmount == 0) {
            revert ZeroAmount();
        }

        uint256 deadBalanceBefore =
            dih.balanceOf(DEAD_ADDRESS);

        dih.safeTransferFrom(
            msg.sender,
            DEAD_ADDRESS,
            requestedAmount
        );

        uint256 deadBalanceAfter =
            dih.balanceOf(DEAD_ADDRESS);

        if (deadBalanceAfter < deadBalanceBefore) {
            revert UnexpectedTokenAccounting(
                deadBalanceBefore,
                deadBalanceAfter
            );
        }

        uint256 burnedAmount =
            deadBalanceAfter - deadBalanceBefore;

        if (burnedAmount < minimumBurn) {
            revert BurnBelowMinimum(
                burnedAmount,
                minimumBurn
            );
        }

        if (!hasParticipated[msg.sender]) {
            hasParticipated[msg.sender] = true;

            participants.push(msg.sender);

            unchecked {
                participantCount += 1;
            }
        }

        burnedBy[msg.sender] += burnedAmount;
        totalBurned += burnedAmount;

        unchecked {
            burnTransactionCount += 1;
        }

        emit DIHBurned(
            msg.sender,
            requestedAmount,
            burnedAmount,
            burnedBy[msg.sender],
            totalBurned,
            participantCount,
            burnTransactionCount
        );
    }

    function pause()
        external
        onlyRole(MANAGER_ROLE)
    {
        _pause();
    }

    function unpause()
        external
        onlyRole(MANAGER_ROLE)
    {
        if (!scheduleConfigured) {
            revert ScheduleNotConfigured();
        }

        _unpause();
    }

    function participantAt(
        uint256 index
    )
        external
        view
        returns (address)
    {
        return participants[index];
    }

    function participantsLength()
        external
        view
        returns (uint256)
    {
        return participants.length;
    }

    function eventActive()
        public
        view
        returns (bool)
    {
        return (
            scheduleConfigured &&
            !paused() &&
            block.timestamp >= startTime &&
            block.timestamp < endTime
        );
    }

    function eventEnded()
        external
        view
        returns (bool)
    {
        return (
            scheduleConfigured &&
            block.timestamp >= endTime
        );
    }

    function contractDIHBalance()
        external
        view
        returns (uint256)
    {
        return dih.balanceOf(address(this));
    }
}
Chain explorer2386msChain node75ms