CARV Protocol Service

CARV Protocol Service

  • Utilizes a proxy contract.

  • Manages system parameters separately through the Settings contract.

  • Does not store funds; all funds are managed by the Vault contract. This contract only has operational authority for fixed roles.

  • Inherits from AccessControlUpgradeable and VRFConsumerBaseV2 (for generating random numbers using Chainlink).

  • Implements the main logic of the CARV protocol.

Core Function Explanations

Trusted Execution Environment (TEE)

Verifier Node

Attestation

Delegation

Rewards

Function Definitions & Explanations

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

interface IProtocolService {

    /**
     * @notice This enum represents result of an attestation reported by verifier
     *
     * `Valid`: This attestation can be parsed successfully and is not malicious
     * `Invalid`: This attestation cannot be parsed successfully
     * `Malicious`: This attestation can be parsed successfully but is malicious
     */
    enum AttestationResult {
        Valid,
        Invalid,
        Malicious
    }

    /**
     * @notice This struct represents information of a node
     *
     * `id`: The globally unique ID of the node. The node is automatically registered when
     *        it calls `nodeEnter` for the first time. Up to 65535 nodes can be registered.
     * `listIndex`: The position of the node in the current `activeVrfNodeList`
     * `active`: Is the node active?
     * `lastConfirmDate`: The date on which reward was last confirmed.
     * `missedVerifyCount`: The number of verifications the node currently misses
     * `selfTotalRewards`: The total rewards of the node itself
     * `selfClaimedRewards`: The rewards claimed by the node itself
     * `delegationRewards`: The rewards of the nodeโ€™s delegator
     * `lastEnterTime`: The time when the node last called `nodeEnter` to go online
     * `commissionRate`: rate of commission, which is given by NFT holders to the verifier node.(decimals: 4)
     * `commissionRateLastModifyAt`: timestamp of last modifying commission rate
     */
    struct NodeInfo {
        uint16 id;
        uint16 listIndex;
        bool active;
        uint32 lastConfirmDate;
        uint64 missedVerifyCount;
        int256 selfTotalRewards;
        uint256 selfClaimedRewards;
        uint256 delegationRewards;
        uint256 lastEnterTime;
        address claimer;
        uint32 commissionRate; // decimals: 4
        uint256 commissionRateLastModifyAt;
    }

    /**
     * @notice This struct represents reward information of a CarvNft token
     *
     * `initialRewards`: When this tokenID is delegated to a node, the initial amount of delegator's rewards needs to be recorded.
     * `totalRewards`: The amount of rewards this tokenID has been confirmed
     * `claimedRewards`: The amount of rewards this tokenID has been claimed
     */
    struct TokenRewardInfo {
        uint256 initialRewards;
        uint256 totalRewards;
        uint256 claimedRewards;
    }

    /**
     * @notice This struct represents staking information of a tee
     *
     * `valid`: Whether tee is currently valid, invalid tee cannot report attestation
     * `staked`: teeโ€™s current staked amount
     * `lastReportAt`: The time when tee last reported attestation, used for unstake check
     */
    struct TeeStakeInfo {
        bool valid;
        uint256 staked;
        uint256 lastReportAt;
    }

    /**
     * @notice This struct represents information of an attestation
     *
     * `reporter`: The address of the tee who reported this attestation
     * `valid`: The number of votes that this attestation is considered valid (voted by the verifier node)
     * `invalid`: The number of votes that this attestation is considered invalid (voted by the verifier node)
     * `vrfChosenID`: ID of the currently randomly selected verification nodes
     * `slashed`: Has this attestation been slashed?
     * `deadline`: Deadline for collecting verification
     */
    struct Attestation {
        address reporter;
        uint16 valid;
        uint16 invalid;
        uint16 malicious;
        uint32 vrfChosenID;
        bool slashed;
        uint256 deadline;
    }

    /**
     * @notice This struct represents information of an verification with signature
     *
     * `result`: result of this attestation
     * `index`: index of this node in activeVrfNodeList
     * `signer`: address of this node (sign the Data to call contract gasless)
     * `v`: v of signature signed by signer
     * `r`: r of signature signed by signer
     * `s`: s of signature signed by signer
     */
    struct VerificationInfo {
        AttestationResult result;
        uint16 index;
        address signer;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    // admin
    event UpdateVrfAddress(address vrf);
    event UpdateSettingsAddress(address settings);

    // tee
    event TeeStake(address tee, uint256 amount);
    event TeeUnstake(address tee, uint256 amount);
    event TeeSlash(address tee, bytes32 attestationID, uint256 amount);
    event ClaimMaliciousTeeRewards(address verifer, uint256 amount);
    event TeeReportAttestations(address tee, bytes32[] attestationIDs, string[] attestationInfos, uint256 requestID);
    event ConfirmVrfNodes(uint256 requestId, uint16[] vrfChosen, uint256 deadline);

    // node
    event NodeRegister(address node, uint16 id);
    event NodeActivate(address node);
    event NodeClear(address node);
    event NodeSlash(address node, bytes32 attestationID, uint256 rewards);
    event NodeModifyCommissionRate(address node, uint32 commissionRate);
    event NodeSetClaimer(address node, address claimer);
    event NodeClaim(address node, address claimer, uint256 rewards);
    event NodeReportVerification(address node, bytes32 attestationID, AttestationResult result);
    event NodeReportVerificationBatch(bytes32 attestationID, VerificationInfo[] infos);

    // delegation
    event Delegate(uint256 tokenID, address to);
    event Redelegate(uint256 tokenID, address to);
    event Undelegate(uint256 tokenID, address to);
    event ClaimRewards(uint256 tokenID, uint256 rewards);

    /*----------------------------------------------------------------------------------------------------------------*/

    /**
     * @notice update address of Settings contract.
     *
     * @dev Only admin role.
     * @dev Emits `UpdateSettingsAddress`.
     *
     * @param settings_: address of Settings contract.
     */
    function updateSettingsAddress(address settings_) external;
    /**
     * @notice update address of CarvVrf contract.
     *
     * @dev Only admin role.
     * @dev Emits `UpdateVrfAddress`.
     *
     * @param carvVrf_: address of CarvVrf contract.
     */
    function updateVrfAddress(address carvVrf_) external;

    /**
     * @notice Tee needs to stake CARVs before reporting attestation,
     * @notice and CARVs will be converted to veCARVs stored in Vault.
     * @notice which will be used to slash when tee proven to be evil.
     *
     * @dev Only tee role (granted by admin role).
     * @dev Emits `TeeStake`.
     *
     * @param amount: amount of CARV tee plans to stake.
     */
    function teeStake(uint256 amount) external;

    /**
     * @notice Tee withdraws staked veCARVs, rules are as follows:
     * @notice 1. enough time has passed since the last attestation was submitted.
     * @notice 2. veCARVs that have been slashed cannot be unstaked.
     * @notice 3. unable to continue reporting attestation after unstaking.
     *
     * @dev Only tee role (granted by admin role).
     * @dev Emits `TeeUnstake`.
     */
    function teeUnstake() external;

    /**
     * @notice When the attestation reported by a tee is proven to be malicious,
     * @notice anyone can initiate a teeSlash, which will slash the veCARVs staked by the tee.
     * @notice Each malicious attestation can only be slashed once.
     * @notice When the veCARVs staked by a tee are slashed below a threshold,
     * @notice the tee will be forcibly restricted from continuing to report attestation.
     *
     * @dev Emits `TeeSlash`.
     *
     * @param attestationID: id of attestation which is proven malicious.
     */
    function teeSlash(bytes32 attestationID) external;

    /**
     * @notice When an attestation reported by a tee is slashed,
     * @notice verifiers that have reported verification for this attestation can claim the reward.
     * @notice If a verifier have not reported verification of this attestation, it cannot claim the reward.
     *
     * @dev Emits `ClaimMaliciousTeeRewards`.
     *
     * @param attestationID: id of attestation which is slashed.
     */
    function claimMaliciousTeeRewards(bytes32 attestationID) external;

    /**
     * @notice Tee reports attestations. The same attestation can only be reported once.
     *
     * @dev Only staked tee role.
     * @dev Emits `TeeReportAttestations`.
     * @dev A request to apply for VRF will be sent to chainlink.
     * @dev After receiving the callback from chainlink, emits `ConfirmVrfNodes`.
     *
     * @param attestationInfos: attestations to be reported.
     */
    function teeReportAttestations(string[] memory attestationInfos) external;

    /**
     * @notice In order to save costs more efficiently when selecting nodes in VRF,
     * @notice we designed a data structure `activeVrfNodeList` for this purpose.
     * @notice `activeVrfNodeList` stores up to 2000 active nodes with delegation weight,
     * @notice and each time VRF will select from these nodes.
     *
     * @notice Nodes need to activate themselves by calling nodeEnter of the smart contract
     * @notice To successfully activate a node, caller need to meet the following conditions:
     * @notice 1. The caller needs to hold CarvNft, or be delegated by other holders
     * @notice 2. If the current `activeVrfNodeList` is full, you need to choose a node
     * @notice    in the list with less delegation weight than yourself to replace it.
     *
     * @dev Emits `NodeActivate`.
     * @dev If any node is kicked and replaced by your node. Emits `NodeClear`.
     * @dev If any node calls nodeEnter for the first time. Emits `NodeRegister`.
     *
     * @param replacedNode: address of the node that needs to be replaced by your node
     *                  only works when the `activeVrfNodeList` is full
     */
    function nodeEnter(address replacedNode) external;

    /**
     * @notice Exits by node itself, and verification cannot be reported after exiting.
     * @notice When a node exits, it will be punished based on missing counts during the online period.
     *
     * @dev Emits `NodeClear`.
     */
    function nodeExit() external;

    /**
     * @notice Modify the commission rate of verifier node
     *
     * @dev Emits `NodeModifyCommissionRate`.
     *
     * @param commissionRate: rate of commission, which is given by NFT holders to the verifier node
     */
    function nodeModifyCommissionRate(uint32 commissionRate) external;

    /**
     * @notice Similar to `nodeEnter`,
     * @notice but the txn can be broadcast to the chain by other address after being authenticated by the node.
     * @notice Compared with the `nodeEnter`, it is gasless for the verifier node.
     * @notice following the eip-712.
     *
     * @dev Emits `NodeActivate`.
     * @dev If any node is kicked and replaced by your node. Emits `NodeClear`.
     * @dev If any node calls nodeEnter for the first time. Emits `NodeRegister`.
     *
     * @param replacedNode: address of the node that needs to be replaced by your node
     *                  only works when the `activeVrfNodeList` is full
     * @param expiredAt: transaction expiration time
     * @param signer: address of verifier node ready to enter
     */
    function nodeEnterWithSignature(
        address replacedNode, uint256 expiredAt, address signer, uint8 v, bytes32 r, bytes32 s
    ) external;

    /**
     * @notice Similar to `nodeExit`,
     * @notice but the txn can be broadcast to the chain by other address after being authenticated by the node.
     * @notice Compared with the `nodeExit`, it is gasless for the verifier node.
     * @notice following the eip-712.
     *
     * @dev Emits `NodeClear`.
     *
     * @param expiredAt: transaction expiration time
     * @param signer: address of verifier node ready to exit
     */
    function nodeExitWithSignature(
        uint256 expiredAt, address signer, uint8 v, bytes32 r, bytes32 s
    ) external;

    /**
     * @notice Similar to `nodeModifyCommissionRate`,
     * @notice but the txn can be broadcast to the chain by other address after being authenticated by the node.
     * @notice Compared with the `nodeModifyCommissionRate`, it is gasless for the verifier node.
     * @notice following the eip-712.
     *
     * @dev Emits `NodeModifyCommissionRate`.
     *
     * @param commissionRate: rate of commission, which is given by NFT holders to the verifier node
     * @param expiredAt: transaction expiration time
     * @param signer: address of verifier node
     */
    function nodeModifyCommissionRateWithSignature(
        uint32 commissionRate, uint256 expiredAt, address signer, uint8 v, bytes32 r, bytes32 s
    ) external;

    /**
     * @notice Specify the address that can claim rewards of this verifier node
     * @notice it is gasless for the verifier node.
     * @notice following the eip-712.
     *
     * @dev Emits `NodeSetClaimer`.
     *
     * @param claimer: address of whom can claim reward of this verifier
     * @param expiredAt: transaction expiration time
     * @param signer: address of verifier node
     */
    function nodeSetRewardClaimerWithSignature(
        address claimer, uint256 expiredAt, address signer, uint8 v, bytes32 r, bytes32 s
    ) external;

    /**
     * @notice Claim reward of node.
     * @notice The reward for reporting verifications is divided into two parts: node's reward and delegators' reward
     * @notice Only when the node is online for more than 6 hours/day will there be reward.
     *
     * @dev Emits `NodeClaim`.
     *
     * @param node: address of node to claim reward
     */
    function nodeClaim(address node) external;

    /**
     * @notice Anyone can initiate a slash for a miss reporting of a node.
     * @notice Each miss reporting of a node can only be slashed once.
     * @notice When a node is selected by VRF but fails to report a verification within the specified time,
     * @notice the node can be slashed.
     *
     * @dev Emits `NodeSlash`.
     *
     * @param node: address of node to be slashed
     * @param attestationID: id of attestation that node miss reporting
     * @param index: index of this node in vrfChosen list of this attestation
     */
    function nodeSlash(address node, bytes32 attestationID, uint16 index) external;

    /**
     * @notice If the node is online but hasn't reported verification that day,
     * @notice the smart contract needs to be notified through this function to update the status of the node today.
     *
     * @param node: address of the node to report daily active
     */
    function nodeReportDailyActive(address node) external;

    /**
     * @notice After an attestation is reported, a group of nodes will be randomly selected through chainlink's VRF.
     * @notice These nodes need to submit the verification within the specified time.
     * @notice When a node is selected by VRF, the proof is submitted by calling `nodeReportVerification`.
     * @notice The verification cannot be submitted repeatedly.
     *
     * @dev Only nodes chosen by VRF in this attestation.
     * @dev Emits `NodeReportVerification`.
     *
     * @param attestationID: id of attestation
     * @param index: index of this node in vrfChosen list of this attestation
     * @param result: Whether the attestation is valid after being checked by the node.
     */
    function nodeReportVerification(bytes32 attestationID, uint16 index, AttestationResult result) external;

    /**
     * @notice Batch reporting verification.
     * @notice One address can collect multiple verifications and delegate reporting them in batches,
     * @notice following the eip-712.
     *
     * @dev Emits `NodeReportVerificationBatch`.
     */
    function nodeReportVerificationBatch(bytes32 attestationID, VerificationInfo[] calldata infos) external;

    /**
     * @notice If the NFT holder doesn't want to run the node to report the verification himself,
     * @notice he can delegate the authority to others to run by calling `delegate`.
     * @notice After delegating, NFT holders will give a proportion of rewards (10%) to the delegatee.
     *
     * @dev Emits `Delegate`.
     *
     * @param tokenID: tokenID of CarvNft to be delegated.
     * @param to: address of whom you want to delegate to
     */
    function delegate(uint256 tokenID, address to) external;

    /**
     * @notice Specify a new delegatee to replace the current delegatee.
     * @notice During the `redelegate`, the node may be triggered to exit.
     *
     * @dev Emits `Redelegate`.
     * @dev When the node's delegation weight is reduced to 0, it will be forced offline. Emits `NodeClear`
     *
     * @param tokenID: tokenID of CarvNft to be redelegated.
     * @param to: address of whom you want to redelegate to
     */
    function redelegate(uint256 tokenID, address to) external;

    /**
     * @notice Cancel current delegation.
     * @notice During the `undelegate`, the node may be triggered to exit.
     *
     * @dev Emits `undelegate`.
     * @dev When the node's delegation weight is reduced to 0, it will be forced offline. Emits `NodeClear`
     *
     * @param tokenID: tokenID of CarvNft to be undelegated.
     */
    function undelegate(uint256 tokenID) external;

    /**
     * @notice Claim rewards corresponding to a certain tokenID owned by you.
     * @notice vault.rewardsWithdraw() will be called to transfer veCARV to the user.
     * @notice After the user claim the reward, this CarvNft cannot be redeemed.
     *
     * @dev Emits `ClaimRewards`.
     *
     * @param tokenID: The token ID that needs to be claimed rewards
     */
    function claimRewards(uint256 tokenID) external;

    /**
     * @notice Check whether this tokenID has already claimed rewards.
     *
     * @param tokenID: token ID
     * @return claimed: whether this tokenID has already claimed rewards
     */
    function checkClaimed(uint256 tokenID) external view returns (bool);

    /**
     * @notice Get the index of today.
     */
    function todayIndex() external view returns (uint32);

    /**
     * @notice Get the offset of today.
     */
    function todayOffset() external view returns (uint256);
}

Last updated