账户
0x55...460e
0x55...460E

0x55...460E

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.19+commit.7dd6d404
语言
Solidity
合同源代码
文件 1 的 2:AccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/// @title Access Control Contract
/// @notice This contract provides basic access control with an admin role, pause functionality, and pending admin transfer.
/// @dev The contract uses role-based permissioning for administrative actions and pausing functionality.
abstract contract AccessControl {
    /// @notice Address of the current admin
    address public admin;

    /// @notice Address of the pending admin, to be accepted as new admin
    address public pendingAdmin;

    /// @notice Indicates whether the contract is paused
    bool public paused;

    /// Custom errors for contract-specific revert reasons
    error HasPaused(bool _paused);
    error NullAddress();
    error NotAdmin();
    error NotPendingAdmin();

    /// Events for logging state changes
    event AdminChange(address newAdmin, address oldAdmin);
    event Pause(address account);
    event Unpause(address account);

    /// @notice Ensures only the admin can perform certain actions
    modifier onlyAuth() {
        if (msg.sender != admin) revert NotAdmin();
        _;
    }

    /// @notice Ensures certain actions can only be performed when the contract is not paused
    modifier whenNotPaused() {
        if (paused) revert HasPaused({_paused: true});
        _;
    }

    /// @notice Ensures certain actions can only be performed when the contract is paused
    modifier whenPaused() {
        if (!paused) revert HasPaused({_paused: false});
        _;
    }

    /// @notice Constructor to set the initial admin of the contract
    /// @param _admin The initial admin address
    constructor(address _admin) {
        admin = _admin;
    }

    /// @notice Sets a new pending admin
    /// @dev Can only be called by the current admin
    /// @param newAdmin The address of the proposed new admin
    function setPendingAdmin(address newAdmin) public onlyAuth {
        if (newAdmin == address(0)) revert NullAddress();
        pendingAdmin = newAdmin;
    }

    /// @notice Accepts the role of admin by the pending admin
    /// @dev Transfers the admin role from the current admin to the pending admin
    function acceptAdmin() public {
        if (pendingAdmin != msg.sender) revert NotPendingAdmin();
        address oldAdmin = admin;
        admin = pendingAdmin;
        pendingAdmin = address(0);
        emit AdminChange(admin, oldAdmin);
    }

    /// @notice Pauses the contract
    /// @dev Can only be called by the current admin when the contract is not paused
    function pause() external onlyAuth whenNotPaused {
        paused = true;
        emit Pause(msg.sender);
    }

    /// @notice Unpauses the contract
    /// @dev Can only be called by the current admin when the contract is paused
    function unpause() external onlyAuth whenPaused {
        paused = false;
        emit Unpause(msg.sender);
    }
}
合同源代码
文件 2 的 2:RegisterHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "./AccessControl.sol";

/// @title User Manager Interface
/// @notice Interface for user management operations.
interface IUserManager {
    function registerMember(address) external;

    function newMemberFee() external view returns (uint256);
}

/// @title ERC20 Token Interface
/// @notice Interface for basic ERC20 token operations.
interface IErc20 {
    function balanceOf(address) external view returns (uint256);

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

    function transfer(address, uint256) external returns (bool);
}

/// @title Referral Interface
/// @notice Interface for managing referral relationships.
interface IReferral {
    function setReferrer(address user, address referrer) external;
}

/// @title UNION Member Registration Helper Contract
/// @notice This contract facilitates user registration and referral in the UNION protocol.
/// @dev Extends from the AccessControl contract for role-based permissions.
contract RegisterHelper is AccessControl {
    address public immutable union;
    address public immutable userManager;
    address public immutable referral;
    address payable public regFeeRecipient;

    uint public rebate;
    uint public regFee;

    /// Custom errors for specific revert cases.
    error NotEnoughBalance();
    error NotEnoughFee(uint256 fee);

    /// Events for logging state changes and operations.
    event RegFeeRecipientChange(address newRecipient, address oldRecipient);
    event RebateChange(uint newRebate, uint oldRebate);
    event RegFeeChange(uint newRegFee, uint oldRegFee);
    event Register(address newUser, address regFeeRecipient, address referrer, uint fee, uint regFee, uint rebate);

    /// @notice Constructor to initialize the contract with necessary addresses.
    /// @param _admin Address of the initial admin.
    /// @param _union Address of the UNION token.
    /// @param _userManager Address of the user manager contract.
    /// @param _referral Address of the referral contract.
    constructor(
        address _admin,
        address _union,
        address _userManager,
        address _referral,
        address payable _regFeeRecipient,
        uint _rebate,
        uint _regFee
    ) AccessControl(_admin) {
        if (_regFeeRecipient == payable(address(0))) revert NullAddress();
        union = _union;
        userManager = _userManager;
        referral = _referral;
        IErc20(union).approve(userManager, type(uint256).max);
        regFeeRecipient = _regFeeRecipient;
        rebate = _rebate;
        regFee = _regFee;
        emit RegFeeRecipientChange(regFeeRecipient, address(0));
        emit RebateChange(rebate, 0);
        emit RegFeeChange(regFee, 0);
    }

    /// @notice Fallback receive function to accept ETH.
    receive() external payable {}

    /// @notice Sets the recipient for registration fees.
    /// @param newRecipient The address of the new fee recipient.
    function setRegFeeRecipient(address payable newRecipient) external onlyAuth {
        if (newRecipient == address(0)) revert NullAddress();
        address payable oldRecipient = regFeeRecipient;
        regFeeRecipient = newRecipient;
        emit RegFeeRecipientChange(newRecipient, oldRecipient);
    }

    /// @notice Sets the rebate amount.
    /// @param newRebate The new rebate amount.
    function setRebate(uint newRebate) external onlyAuth {
        uint oldRebate = rebate;
        rebate = newRebate;
        emit RebateChange(newRebate, oldRebate);
    }

    /// @notice Sets the registration fee amount.
    /// @param newRegFee The new registration fee amount.
    function setRegFee(uint newRegFee) external onlyAuth {
        uint oldRegFee = regFee;
        regFee = newRegFee;
        emit RegFeeChange(newRegFee, oldRegFee);
    }

    /// @notice Registers a new user, sets their referrer, and handles fees.
    /// @param newUser The address of the user to be registered.
    /// @param referrer The address of the referrer.
    function register(address newUser, address payable referrer) external payable whenNotPaused {
        IReferral(referral).setReferrer(newUser, referrer);
        if (IErc20(union).balanceOf(address(this)) < IUserManager(userManager).newMemberFee())
            revert NotEnoughBalance();

        if (msg.value < regFee + rebate) revert NotEnoughFee(msg.value);
        
        uint diff = msg.value - regFee - rebate;
        if(referrer != address(0)) {
            if(regFee > 0)sendCall(regFeeRecipient, regFee);
            if(rebate > 0)sendCall(referrer, rebate); 
        } else {
            if(regFee > 0 || rebate > 0)sendCall(regFeeRecipient,regFee + rebate);
        }
        if(diff > 0)sendCall(payable(msg.sender), diff);

        IUserManager(userManager).registerMember(newUser);
        emit Register(newUser, regFeeRecipient, referrer, msg.value, regFee, rebate);
    }

    function claimToken(address recipient) external onlyAuth {
        if (recipient== address(0)) revert NullAddress();
        IErc20 token = IErc20(union);
        uint256 balance = token.balanceOf(address(this));
        token.transfer(recipient, balance);
    } 

    function sendCall(address payable to, uint amount) public payable {
        (bool sent, ) = to.call{value: amount}("");
        require(sent, "Failed to send Ether");
    }
}
设置
{
  "compilationTarget": {
    "src/RegisterHelper.sol": "RegisterHelper"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [
    ":ds-test/=lib/forge-std/lib/ds-test/src/",
    ":forge-std/=lib/forge-std/src/"
  ]
}
ABI
[{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_union","type":"address"},{"internalType":"address","name":"_userManager","type":"address"},{"internalType":"address","name":"_referral","type":"address"},{"internalType":"address payable","name":"_regFeeRecipient","type":"address"},{"internalType":"uint256","name":"_rebate","type":"uint256"},{"internalType":"uint256","name":"_regFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"HasPaused","type":"error"},{"inputs":[],"name":"NotAdmin","type":"error"},{"inputs":[],"name":"NotEnoughBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"NotEnoughFee","type":"error"},{"inputs":[],"name":"NotPendingAdmin","type":"error"},{"inputs":[],"name":"NullAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"}],"name":"AdminChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newRebate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldRebate","type":"uint256"}],"name":"RebateChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newRegFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldRegFee","type":"uint256"}],"name":"RegFeeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"oldRecipient","type":"address"}],"name":"RegFeeRecipientChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newUser","type":"address"},{"indexed":false,"internalType":"address","name":"regFeeRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"regFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rebate","type":"uint256"}],"name":"Register","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpause","type":"event"},{"inputs":[],"name":"acceptAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"claimToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referral","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"regFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"regFeeRecipient","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newUser","type":"address"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"register","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sendCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setPendingAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newRebate","type":"uint256"}],"name":"setRebate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newRegFee","type":"uint256"}],"name":"setRegFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newRecipient","type":"address"}],"name":"setRegFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"union","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"userManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]