BaseBase
0x14...224d
0x14...224d

0x14...224d

US$0.00
此合同的源代码已经过验证!
合同元数据
编译器
0.8.21+commit.d9974bed
语言
Solidity
合同源代码
文件 1 的 9:AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.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:
 *
 * ```solidity
 * 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}:
 *
 * ```solidity
 * 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. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @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 returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @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 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.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual 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.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual 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 `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
合同源代码
文件 2 的 9:Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
合同源代码
文件 3 的 9:CrappyAchievements.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

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

contract CrappyAchievements is AccessControl, ICrappyAchievements {
    bytes32 constant public OWNER = keccak256("OWNER");
    bytes32 constant public NOTIFIER = keccak256("NOTIFIER");

    ICrappsToken private _crapps;
    mapping(address => mapping(RewardType => uint256)) private _userData;
    mapping(uint256 rewardIndex => mapping(address => bool)) private _redeemedRewards;
    Reward[] private _rewards;

    constructor(address initialCrapps) {
        _crapps = ICrappsToken(initialCrapps);
        _setRoleAdmin(NOTIFIER, OWNER);
        _grantRole(OWNER, _msgSender());
    }

    function transferOwnership(address newOwner) public override onlyRole(OWNER) {
        _transferOwnership(_msgSender(), newOwner);
    }

    function setCrapps(address newCrapps) public override onlyRole(OWNER) {
        if (address(_crapps) == newCrapps) {
            return;
        }

        address previousCrapps = address(_crapps);
        _crapps = ICrappsToken(newCrapps);
        emit CrappyAchievementsCrappsChanged(previousCrapps, newCrapps);
    }

    function addReward(Reward calldata rewardData) external override onlyRole(OWNER) {
        _rewards.push() = rewardData;
        emit CrappyAchievementsRewardAdded(rewardData, _rewards.length - 1);
    }

    function redeemReward(uint256 rewardIndex) public override {
        if (_rewards.length <= rewardIndex) {
            revert CrappyAchievementsRewardDoesNotExist();
        }

        if (_redeemedRewards[rewardIndex][_msgSender()]) {
            revert CrappyAchievementsRewardAlreadyRedeemed();
        }

        mapping(RewardType => uint256) storage thisUserData = _userData[_msgSender()];
        Reward storage rewardData = _rewards[rewardIndex];
        if (rewardData.requirement > thisUserData[rewardData.rewardType]) {
            revert CrappyAchievementsRewardRequirementNotSatisfied(rewardData.requirement);
        }

        _redeemedRewards[rewardIndex][_msgSender()] = true;
        _crapps.mint(_msgSender(), rewardData.rewardAmount);
        _notify(_msgSender(), RewardType.TotalCrappsRedeemed, rewardData.rewardAmount);
        emit CrappyAchievementsRewardRedeemed(rewardIndex, _msgSender());
    }

    function notify(address user, RewardType rewardType, uint256 value) public override onlyRole(NOTIFIER) {
        _notify(user, rewardType, value);
    }

    function batchNotify(address user, RewardType[] memory rewardTypes, uint256[] memory values) public override onlyRole(NOTIFIER) {
        require(rewardTypes.length == values.length, "Reward types and values must match length.");
        for (uint256 i = 0; i < rewardTypes.length; ++i) {
            _notify(user, rewardTypes[i], values[i]);
        }
    }

    function rewardCount() public view override returns (uint256) {
        return _rewards.length;
    }

    function rewards(uint256 start, uint256 count) public view override returns (Reward[] memory batch) {
        batch = new Reward[](count);
        for (uint256 i = start; i < start + count; ++i) {
            batch[i] = _rewards[i];
        }
    }

    function rewards() public view override returns (Reward[] memory) {
        return _rewards;
    }

    function reward(uint256 rewardIndex) public view override returns (Reward memory) {
        return _rewards[rewardIndex];
    }

    function betsPlaced(address user) public view override returns (uint256) {
        return _userData[user][RewardType.BetsPlaced];
    }

    function totalCrappsEarned(address user) public view override returns (uint256) {
        return _userData[user][RewardType.TotalCrappsEarned];
    }

    function totalCrappsRedeemed(address user) public view override returns (uint256) {
        return _userData[user][RewardType.TotalCrappsRedeemed];
    }

    function totalCrappsSpent(address user) public view override returns (uint256) {
        return _userData[user][RewardType.TotalCrappsSpent];
    }

    function totalCrappySkins(address user) public view override returns (uint256) {
        return _userData[user][RewardType.TotalCrappySkins];
    }

    function totalCrushWins(address user) public view override returns (uint256) {
        return _userData[user][RewardType.TotalCrushWins];
    }

    function totalCrushWinnings(address user) public view override returns (uint256) {
        return _userData[user][RewardType.TotalCrushWinnings];
    }

    function highestCrushWin(address user) public view override returns (uint256) {
        return _userData[user][RewardType.HighestCrushWin];
    }

    function highestCrushMultiplier(address user) public view override returns (uint256) {
        return _userData[user][RewardType.HighestCrushMultiplier];
    }

    function crapps() public view returns (ICrappsToken) {
        return _crapps;
    }

    function userData(address user, RewardType rewardType) public view override returns (uint256) {
        return _userData[user][rewardType];
    }

    function redeemedRewards(uint256 rewardIndex, address user) public view override returns (bool) {
        return _redeemedRewards[rewardIndex][user];
    }

    function _transferOwnership(address previousOwner, address newOwner) private {
        if (newOwner == address(0)) {
            revert CrappyAchievementsInvalidOwner();
        }

        _revokeRole(OWNER, previousOwner);
        _grantRole(OWNER, newOwner);
    }

    function _notify(address user, RewardType rewardType, uint256 value) private {
        uint256 previousValue = _userData[user][rewardType];

        uint256 newValue;
        if (rewardType == RewardType.HighestCrushWin || rewardType == RewardType.HighestCrushMultiplier) {
            if (previousValue >= value) {
                return;
            }

            newValue = value;
        } else {
            newValue = _userData[user][rewardType] + value;
            if (previousValue == newValue) {
                return;
            }
        }

        _userData[user][rewardType] = newValue;
        emit CrappyAchievementsNotify(user, rewardType, previousValue, newValue);
    }
}
合同源代码
文件 4 的 9:ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 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);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
合同源代码
文件 5 的 9:IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @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.
     */
    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. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    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 `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
合同源代码
文件 6 的 9:ICrappsToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

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

interface ICrappsToken is IERC20, IAccessControl {
    error CrappsTokenInvalidOwner();

    error CannotTransferCrapps();

    function transferOwnership(address newOwner) external;

    function mint(address to, uint256 amount) external;

    function burn(address from, uint256 amount) external;
}
合同源代码
文件 7 的 9:ICrappyAchievements.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

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

interface ICrappyAchievements is IAccessControl {
    event CrappyAchievementsNotify(address indexed user, RewardType rewardType, uint256 previousValue, uint256 newValue);
    event CrappyAchievementsRewardAdded(Reward rewardData, uint256 rewardIndex);
    event CrappyAchievementsRewardRedeemed(uint256 indexed rewardIndex, address indexed user);
    event CrappyAchievementsCrappsChanged(address indexed previousCrapps, address indexed newCrapps);

    error CrappyAchievementsInvalidOwner();
    error CrappyAchievementsRewardDoesNotExist();
    error CrappyAchievementsRewardAlreadyRedeemed();
    error CrappyAchievementsRewardRequirementNotSatisfied(uint256 requirement);

    enum RewardType {
        BetsPlaced, // 0
        TotalCrappsEarned, // 1
        TotalCrappsRedeemed, // 2
        TotalCrappsSpent, // 3
        TotalCrappySkins, // 4
        TotalCrushWins, // 5
        TotalCrushWinnings, // 6
        HighestCrushWin, // 7
        HighestCrushMultiplier // 8
    }

    struct Reward {
        RewardType rewardType;
        uint256 requirement;
        uint256 rewardAmount;
    }

    function transferOwnership(address newOwner) external;

    function setCrapps(address newCrapps) external;

    function addReward(Reward calldata rewardData) external;

    function redeemReward(uint256 rewardIndex) external;

    function notify(address user, RewardType rewardType, uint256 value) external;

    function batchNotify(address user, RewardType[] memory rewardTypes, uint256[] memory values) external;

    function rewardCount() external view returns (uint256);

    function rewards(uint256 start, uint256 count) external view returns (Reward[] memory batch);

    function rewards() external view returns (Reward[] memory);

    function reward(uint256 rewardIndex) external view returns (Reward memory);

    function betsPlaced(address user) external view returns (uint256);

    function totalCrappsEarned(address user) external view returns (uint256);

    function totalCrappsRedeemed(address user) external view returns (uint256);

    function totalCrappsSpent(address user) external view returns (uint256);

    function totalCrappySkins(address user) external view returns (uint256);

    function totalCrushWins(address user) external view returns (uint256);

    function totalCrushWinnings(address user) external view returns (uint256);

    function highestCrushWin(address user) external view returns (uint256);

    function highestCrushMultiplier(address user) external view returns (uint256);

    function crapps() external view returns (ICrappsToken);

    function userData(address user, RewardType rewardType) external view returns (uint256);

    function redeemedRewards(uint256 rewardIndex, address user) external view returns (bool);
}
合同源代码
文件 8 的 9:IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * 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[ERC 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);
}
合同源代码
文件 9 的 9:IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
设置
{
  "compilationTarget": {
    "contracts/CrappyAchievements.sol": "CrappyAchievements"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"initialCrapps","type":"address"}],"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":[],"name":"CrappyAchievementsInvalidOwner","type":"error"},{"inputs":[],"name":"CrappyAchievementsRewardAlreadyRedeemed","type":"error"},{"inputs":[],"name":"CrappyAchievementsRewardDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint256","name":"requirement","type":"uint256"}],"name":"CrappyAchievementsRewardRequirementNotSatisfied","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousCrapps","type":"address"},{"indexed":true,"internalType":"address","name":"newCrapps","type":"address"}],"name":"CrappyAchievementsCrappsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"enum ICrappyAchievements.RewardType","name":"rewardType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"previousValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"CrappyAchievementsNotify","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"enum ICrappyAchievements.RewardType","name":"rewardType","type":"uint8"},{"internalType":"uint256","name":"requirement","type":"uint256"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"indexed":false,"internalType":"struct ICrappyAchievements.Reward","name":"rewardData","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"rewardIndex","type":"uint256"}],"name":"CrappyAchievementsRewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"rewardIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"CrappyAchievementsRewardRedeemed","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"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NOTIFIER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"enum ICrappyAchievements.RewardType","name":"rewardType","type":"uint8"},{"internalType":"uint256","name":"requirement","type":"uint256"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"internalType":"struct ICrappyAchievements.Reward","name":"rewardData","type":"tuple"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"enum ICrappyAchievements.RewardType[]","name":"rewardTypes","type":"uint8[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"batchNotify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"betsPlaced","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"crapps","outputs":[{"internalType":"contract ICrappsToken","name":"","type":"address"}],"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":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"highestCrushMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"highestCrushWin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"enum ICrappyAchievements.RewardType","name":"rewardType","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"notify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rewardIndex","type":"uint256"}],"name":"redeemReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rewardIndex","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"redeemedRewards","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":[{"internalType":"uint256","name":"rewardIndex","type":"uint256"}],"name":"reward","outputs":[{"components":[{"internalType":"enum ICrappyAchievements.RewardType","name":"rewardType","type":"uint8"},{"internalType":"uint256","name":"requirement","type":"uint256"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"internalType":"struct ICrappyAchievements.Reward","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"rewards","outputs":[{"components":[{"internalType":"enum ICrappyAchievements.RewardType","name":"rewardType","type":"uint8"},{"internalType":"uint256","name":"requirement","type":"uint256"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"internalType":"struct ICrappyAchievements.Reward[]","name":"batch","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewards","outputs":[{"components":[{"internalType":"enum ICrappyAchievements.RewardType","name":"rewardType","type":"uint8"},{"internalType":"uint256","name":"requirement","type":"uint256"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"internalType":"struct ICrappyAchievements.Reward[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newCrapps","type":"address"}],"name":"setCrapps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"totalCrappsEarned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"totalCrappsRedeemed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"totalCrappsSpent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"totalCrappySkins","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"totalCrushWinnings","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"totalCrushWins","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"enum ICrappyAchievements.RewardType","name":"rewardType","type":"uint8"}],"name":"userData","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]