USDC flow

Screenshot 2025-03-03 at 14.51.24.png

Node Registry

All Node Operators that want to participate on the XMTP network are identified by a XMTP Node NFT. These NFT’s are exclusively granted by the XMTP Security Council multi-sig and may be revoked at any time.

The Security Council is responsible for performing KYC on all Node Operator NFT holders and ensuring that the group of NFT holders represents a diverse group of entities on the network unlikely to collude with one another.

Interface

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

// Import the standard ERC721 interface.
import { IERC721 } from "../../lib/oz/contracts/token/ERC721/IERC721.sol";
/**
 * @title  INodeRegistry
 * @notice This interface defines the ERC721-based registry for “nodes” in the system.
 * Each node is minted as an NFT with a unique ID (starting at 100 and increasing by 100 with each new node).
 * In addition to the standard ERC721 functionality, the contract supports node-specific features,
 * including node property updates.
 */
interface INodeRegistry is INodeRegistryErrors, INodeRegistryEvents, IERC721 {
    /**
     * @notice Struct representing a node in the registry.
     * @param  signingKeyPub      The public key used for node signing/verification.
     * @param  httpAddress        The HTTP endpoint address for the node.
     * @param  inCanonicalNetwork A flag indicating whether the node is part of the canonical network.
     * @param  minMonthlyFee      The minimum monthly fee collected by the node operator.
     */
    struct Node {
        bytes signingKeyPub;
        string httpAddress;
        bool inCanonicalNetwork;
        uint256 minMonthlyFeeMicroDollars;
    }

    /**
     * @notice Struct representing a node with its ID.
     * @param  nodeId The unique identifier for the node.
     * @param  node   The node struct.
     */
    struct NodeWithId {
        uint256 nodeId;
        Node node;
    }

    /* ============ Admin-Only Functions ============ */

    /**
     * @notice Adds a new node to the registry and mints its corresponding ERC721 token.
     * @dev    Only the contract owner may call this. Node IDs start at 100 and increase by 100 for each new node.
     * @param  to                        The address that will own the new node NFT.
     * @param  signingKeyPub             The public signing key for the node.
     * @param  httpAddress               The node’s HTTP address.
     * @param  minMonthlyFeeMicroDollars The minimum monthly fee that the node operator collects.
     * @return nodeId                    The unique identifier of the newly added node.
     */
    function addNode(
        address to,
        bytes calldata signingKeyPub,
        string calldata httpAddress,
        uint256 minMonthlyFeeMicroDollars
    ) external returns (uint256 nodeId);

    /**
     * @notice Adds a node to the canonical network.
     * @dev    Only the contract owner may call this.
     * @param  nodeId The unique identifier of the node.
     */
    function addToNetwork(uint256 nodeId) external;

    /**
     * @notice Removes a node from the canonical network.
     * @dev    Only the contract owner may call this.
     * @param  nodeId The unique identifier of the node.
     */
    function removeFromNetwork(uint256 nodeId) external;

    /**
     * @notice Sets the commission percentage that the node operator receives.
     * @dev    Only the contract owner may call this.
     * @param  newCommissionPercent The new commission percentage.
     */
    function setNodeOperatorCommissionPercent(uint256 newCommissionPercent) external;

    /**
     * @notice Sets the maximum number of active nodes.
     * @dev    Only the contract owner may call this.
     * @param  newMaxActiveNodes The new maximum number of active nodes.
     */
    function setMaxActiveNodes(uint8 newMaxActiveNodes) external;

    /**
     * @notice Set the base URI for the node NFTs.
     * @dev    Only the contract owner may call this.
     * @param  newBaseURI The new base URI. Has to end with a trailing slash.
     */
    function setBaseURI(string calldata newBaseURI) external;

    /* ============ Node Manager Functions ============ */

    /**
     * @notice Transfers node ownership from one address to another.
     * @dev    Only the contract owner may call this. Automatically deactivates the node.
     * @param  from   The current owner address.
     * @param  to     The new owner address.
     * @param  nodeId The ID of the node being transferred.
     */
    function transferFrom(address from, address to, uint256 nodeId) external;

    /**
     * @notice Set the HTTP address of an existing node.
     * @dev    Only the contract owner may call this.
     * @param  nodeId      The unique identifier of the node.
     * @param  httpAddress The new HTTP address.
     */
    function setHttpAddress(uint256 nodeId, string calldata httpAddress) external;

    /**
     * @notice Set the minimum monthly fee for a node.
     * @dev    Only the contract owner may call this.
     * @param  nodeId                    The unique identifier of the node.
     * @param  minMonthlyFeeMicroDollars The new minimum monthly fee.
     */
    function setMinMonthlyFee(uint256 nodeId, uint256 minMonthlyFeeMicroDollars) external;

    /* ============ Getters Functions ============ */

    /// @notice The admin role identifier, which can also grant roles.
    // slither-disable-next-line naming-convention
    function ADMIN_ROLE() external pure returns (bytes32 adminRole);

    /// @notice The node manager role identifier.
    // slither-disable-next-line naming-convention
    function NODE_MANAGER_ROLE() external pure returns (bytes32 nodeManagerRole);

    /// @notice The maximum commission percentage that the node operator can receive (100% in basis points).
    // slither-disable-next-line naming-convention
    function MAX_BPS() external pure returns (uint256 maxBps);

    /// @notice The increment for node IDs, which allows for 100 shard node IDs per node in the future (modulus 100).
    // slither-disable-next-line naming-convention
    function NODE_INCREMENT() external pure returns (uint32 nodeIncrement);

    /**
     * @notice Gets all nodes regardless of their health status.
     * @return allNodes An array of all nodes in the registry.
     */
    function getAllNodes() external view returns (NodeWithId[] memory allNodes);

    /**
     * @notice Gets the total number of nodes in the registry.
     * @return nodeCount The total number of nodes.
     */
    function getAllNodesCount() external view returns (uint256 nodeCount);

    /**
     * @notice Retrieves the details of a given node.
     * @param  nodeId The unique identifier of the node.
     * @return node   The Node struct containing the node's details.
     */
    function getNode(uint256 nodeId) external view returns (Node memory node);

    /**
     * @notice Retrieves whether a node is part of the canonical network.
     * @param  nodeId The unique identifier of the node.
     * @return isCanonicalNode A boolean indicating whether the node is part of the canonical network.
     */
    function getIsCanonicalNode(uint256 nodeId) external view returns (bool isCanonicalNode);

    /**
     * @notice Retrieves the maximum number of active nodes in the canonical network.
     * @return maxNodes The maximum number of active nodes in the canonical network.
     */
    function getMaxActiveNodes() external view returns (uint8 maxNodes);

    /**
     * @notice The commission percentage that the node operator receives.
     * @dev    This is stored in basis points (1/100th of a percent).
     * Example: 1% = 100bps, 10% = 1000bps, 100% = 10000bps.
     * Commission is calculated as (nodeOperatorCommissionPercent * nodeOperatorFee) / MAX_BPS
     */
    function getNodeOperatorCommissionPercent() external view returns (uint256 commissionPercent);
}

Payer Registry

Requisites

Interface

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { IERC165 } from "../../lib/oz/contracts/utils/introspection/IERC165.sol";

/**
 * @title  IPayerRegistry
 * @notice Interface for managing payer USDC deposits, usage settlements,
 *         and a secure withdrawal process.
 */
interface IPayerRegistry is IERC165, IPayerRegistryEvents, IPayerRegistryErrors {
    /* ============ Structs ============ */

    /**
     * @dev   Struct to store payer information.
     * @param balance                The current USDC balance of the payer.
     * @param latestDepositTimestamp The timestamp of the most recent deposit.
     */
    struct Payer {
        int64 balance;
        uint64 latestDepositTimestamp;
    }

    /**
     * @dev   Struct to store withdrawal request information.
     * @param withdrawableTimestamp The timestamp when the withdrawal can be finalized.
     * @param amount                The amount requested for withdrawal.
     */
    struct Withdrawal {
        uint64 withdrawableTimestamp;
        uint64 amount;
    }

    /* ============ Payer Management ============ */

    /**
     * @notice Registers the caller as a new payer upon depositing the minimum required USDC.
     *         The caller must approve this contract to spend USDC beforehand.
     * @param  amount The amount of USDC to deposit (must be at least the minimum required).
     *
     * Emits `PayerRegistered`.
     */
    function register(uint64 amount) external;

    /**
     * @notice Allows the caller to deposit USDC into their own payer account.
     *         The caller must approve this contract to spend USDC beforehand.
     * @param  amount The amount of USDC to deposit.
     *
     * Emits `PayerBalanceUpdated`.
     */
    function deposit(uint64 amount) external;

    /**
     * @notice Allows anyone to donate USDC to an existing payer's account.
     *         The sender must approve this contract to spend USDC beforehand.
     * @param  payer  The address of the payer receiving the donation.
     * @param  amount The amount of USDC to donate.
     *
     * Emits `PayerBalanceUpdated`.
     */
    function deposit(address payer, uint64 amount) external;

    /* ============ Payer Balance Management ============ */

    /**
     * @notice Initiates a withdrawal request for the caller.
     *         - Sets the payer into withdrawal mode (no further usage allowed).
     *         - Records a timestamp for the withdrawal lock period.
     * @param  amount The amount to withdraw (can be less than or equal to current balance).
     *
     * Emits `WithdrawalRequest`.
     * Emits `PayerBalanceUpdated`.
     */
    function requestWithdrawal(uint64 amount) external;

    /**
     * @notice Cancels a previously requested withdrawal, removing withdrawal mode.
     * @dev    Only callable by the payer who initiated the withdrawal.
     *
     * Emits `WithdrawalCancelled`.
     * Emits `PayerBalanceUpdated`.
     */
    function cancelWithdrawal() external;

    /**
     * @notice Finalizes a payer's withdrawal after the lock period has elapsed.
     *         - Accounts for any pending usage during the lock.
     *         - Returns the unspent balance to the payer.
     *
     * Emits `WithdrawalFinalized`.
     * Emits `PayerBalanceUpdated`.
     */
    function finalizeWithdrawal() external;

    /**
     * @notice Checks if a payer is currently in withdrawal mode and the timestamp
     *         when they initiated the withdrawal.
     * @param  payer                 The address to check.
     * @return withdrawal            The withdrawal status.
     */
    function getWithdrawalStatus(address payer) external view returns (Withdrawal memory withdrawal);

    /* ============ Usage Settlement ============ */

    /**
     * @notice Settles usage for a contiguous batch of (payer, amount) entries.
     * Assumes that the PayerReport contract has already verified the validity of the payers and amounts.
     *
     * @param  originatorNode The originator node of the usage.
     * @param  payers         A contiguous array of payer addresses.
     * @param  amounts        A contiguous array of usage amounts corresponding to each payer.
     *
     * Emits `UsageSettled`.
     * Emits `PayerBalanceUpdated` for each payer.
     */
    function settleUsage(
        uint256 originatorNode,
        address[] calldata payers,
        uint64[] calldata amounts
    ) external; /* onlyPayerReport */

    /**
     * @notice Transfers all pending fees to the designated distribution contract.
     * @dev    Uses a single storage write for updating accumulated fees.
     *
     * Emits `FeesTransferred`.
     */
    function transferFeesToDistribution() external;

    /* ============ Administrative Functions ============ */

    /**
     * @notice Sets the address of the fee distributor.
     * @param  feeDistributor The address of the new fee distributor.
     *
     * Emits `FeeDistributorUpdated`.
     */
    function setFeeDistributor(address feeDistributor) external;

    /**
     * @notice Sets the address of the payer report manager.
     * @param  payerReportManager The address of the new payer report manager.
     *
     * Emits `PayerReportManagerUpdated`.
     */
    function setPayerReportManager(address payerReportManager) external;

    /**
     * @notice Sets the address of the node registry for operator verification.
     * @param  nodeRegistry The address of the new node registry.
     *
     * Emits `NodeRegistryUpdated`.
     */
    function setNodeRegistry(address nodeRegistry) external;

    /**
     * @notice Sets the address of the USDC token contract.
     * @param  usdcToken The address of the new USDC token contract.
     *
     * Emits `UsdcTokenUpdated`.
     */
    function setUsdcToken(address usdcToken) external;

    /**
     * @notice Sets the minimum deposit amount required for registration.
     * @param  newMinimumDeposit The new minimum deposit amount.
     *
     * Emits `MinimumDepositUpdated`.
     */
    function setMinimumDeposit(uint64 newMinimumDeposit) external;

    /**
     * @notice Sets the minimum deposit amount required for registration.
     * @param  newMinimumRegistrationAmount The new minimum deposit amount.
     *
     * Emits `MinimumRegistrationAmountUpdated`.
     */
    function setMinimumRegistrationAmount(uint64 newMinimumRegistrationAmount) external;

    /**
     * @notice Sets the withdrawal lock period.
     * @param  newWithdrawalLockPeriod The new withdrawal lock period.
     *
     * Emits `WithdrawalLockPeriodUpdated`.
     */
    function setWithdrawalLockPeriod(uint32 newWithdrawalLockPeriod) external;

    /**
     * @notice Sets the transfer fees period.
     * @param  newTransferFeesPeriod The new transfer fees period.
     *
     * Emits `TransferFeesPeriodUpdated`.
     */
    function setTransferFeesPeriod(uint32 newTransferFeesPeriod) external;

    /**
     * @notice Pauses the contract functions in case of emergency.
     *
     * Emits `Paused()`.
     */
    function pause() external;

    /**
     * @notice Unpauses the contract.
     *
     * Emits `Unpaused()`.
     */
    function unpause() external;
}

Payer Report Manager

Requisites