All pages
Powered by GitBook
1 of 20

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Technical Documentation for CrossCurve DAO Smart Contracts

CalldataHelperV1DelegationManagerV1DelegationConditionValidatorV1EmissionManagerV1EscrowManagerEscrowVoteManagerV1GaugeFactoryV1GaugeV1IncentiveRewardsDistributorLockHolderFactoryV1LockHolderV1ProposalManagerRebaseRewardsDistributorV1RewardsDistributorFactoryV1Treasury

Guide for Developers

CrossCurve Consensus Bridge

Overview

CrossCurve Consensus Bridge (CCB) is a cross-chain communication protocol that enables reliable data and asset transfers between networks without relying on centralized intermediaries.

The system is built on a consensus mechanism among independent cross-chain messaging services, ensuring the authenticity of cross-chain messages received on the destination network.

The goal of CCB is to provide developers with a secure way to connect their dApps, DeFi protocols, DAOs, and Infrastructure Services to a multi-network ecosystem using a standardized API and a minimal set of dependencies.

Architecture

Basic cross-chain schema

The fundamental model of cross-chain interaction has been used in various forms for quite some time.

In blockchain A, a contract calls a bridge contract function to send data to blockchain B. The bridge contract emits an event, which is picked up by its off-chain service. This service then creates a transaction containing the necessary data for the corresponding bridge contract on blockchain B. Upon receiving the transaction, the bridge contract verifies its validity and then invokes the target contract function, passing along the data received from blockchain A.

One of the core challenges lies in the fact that the bridge service operates outside the blockchain environment and therefore cannot be verified on-chain. Different bridge protocols address this issue in different ways.

CrossCurve Consensus Bridge, however, introduces a more comprehensive approach. It is not meant to replace existing protocols - instead, it integrates and leverages their mechanisms, creating a unified and verifiable cross-chain communication layer.

Consensus Bridge schema

  • The sender initiates a message in the source network by calling the CrossCurve Gatekeeper contract.

  • Depending on the destination chain, the Gatekeeper prepares and executes parallel message dispatches through multiple supported protocols. One of these protocols transmits the full message payload, while the others send verification proofs to confirm message authenticity.

  • Each protocol, following its standard cross-chain messaging process, independently delivers its data to the CrossCurve Receiver contract in the destination network.

Advantages

Security

CrossCurve Consensus Bridge provides a truly secure mechanism for cross-chain messaging.

Its minimal configuration requires three sovereign cross-chain protocols.

Even in this base setup, an attacker would need to gain simultaneous control over at least two out of the three protocols to compromise a message — making unauthorized interference practically infeasible.

Censorship Resistance

Decentralization is one of the most effective defenses against censorship.

However, in many cross-chain systems, services are often built on specific infrastructure stacks, which introduces potential points of control and censorship.

CrossCurve Consensus Bridge eliminates this issue by leveraging sovereign protocols that operate independently from one another.

For an adversary to block or alter message delivery through the bridge, they would need to control more than 50% of the protocols participating in the message channel (in the minimal setup, that means controlling two out of three).

Resilience

The consensus of independent protocols ensures not only security but also fault tolerance in message transmission. Even in its minimal configuration, if one of the three protocols becomes temporarily unavailable, the remaining two are sufficient to keep the CrossCurve Consensus Bridge fully operational.

In the rare event of a deadlock — when one or more bridges within the consensus experience prolonged downtime, potentially locking assets that relied on the consensus bridge — the situation remains recoverable. The can replace the affected protocols in the consensus bridge of the impacted chain by approving a dedicated governance proposal.

Governance Protection

The CrossCurve Consensus Bridge is engineered for maximum security through the use of consensus among independent bridge protocols for data transmission.

Yet even the most secure systems can face potential vulnerabilities. The most common vector of attack targets governance mechanisms — an adversary might attempt to alter the composition of participating bridges, making it easier to compromise a single one.

In CrossCurve Consensus Bridge, such a scenario is structurally impossible.

Bridge configuration across all chain pairs is managed exclusively by the CrossCurve DAO. Any modification requires explicit community approval. The voting process lasts 7 days, and even after a proposal passes, the new configuration takes effect only after 24 hours, providing an additional safeguard and ensuring transparent governance.

This underlines the critical role of the CrossCurve DAO in maintaining decentralization across cross-chain messaging.

We believe that greater decentralization and resilience can be achieved when ecosystem participants — those advocating for transparency, stability, and security — become active members of the CrossCurve DAO. Their participation strengthens governance integrity and makes malicious decisions significantly harder to pass.

Capabilities

Cross-Chain Messaging

This is the foundation for building advanced cross-chain applications.

Through the Consensus Bridge, dApps gain a reliable communication layer between their instances deployed across different blockchains - or with APIs of other applications operating in separate networks.

Cross-Chain Token Transfers

Cross-chain token transfer applications using any combination of lock/mint or burn/mint models can rely on the Consensus Bridge as a trustless verification layer.

It provides accurate and verifiable information about token locking, minting, unlocking, or burning events across different blockchains.

Foundation for SuperDVN

The Consensus Bridge integrates seamlessly into the LayerZero cross-chain verification system.

This is achieved through a wrapper module called SuperDVN, which externally behaves like a standard DVN but internally performs multi-protocol consensus verification for enhanced security and reliability.

Supported Protocols

Currently, the CrossCurve Consensus Bridge supports the following messaging protocols:

  • CrossCurve Oracle Network

Integration and Connection

The integration with the Consensus Bridge is planned to be permissionless. However, the protocol is currently operating in an experimental mode. If you’d like to connect, please contact us

To send a message to a recipient on another chain, you need to:

  1. Prepare the calldata for the target contract function with the required parameters.

  2. Call the sendData function on the CrossCurve Gatekeeper contract:

Contracts

To configure the bridge, use the following contract addresses.

For proper operation, the contracts must be deployed on both the source and destination blockchains.

Network
CrossCurve Receiver Address
CrossCurve Gatekeeper Address

The Receiver collects the received message instances and emits events as they arrive.
  • The external service CrossCurve Pusher monitors these events. Once the original message and a sufficient number of verification confirmations are detected, it submits an execution transaction to the CrossCurve Receiver contract.

  • Upon receiving the transaction from CrossCurve Pusher, the Receiver verifies the existence of the original message and all required confirmations from other protocols.

  • If validation is successful, the verified message is then delivered to its designated recipient.

  • Ethereum Sepolia Testnet

    0xE98Fd4eF563dCDfC535755f1FBCC0942a8e63517

    0xAB2f5D5A675F9004FEfcA7DC462e3C919a03E892

    Arbitrum Sepolia Testnet

    0x962EDA2C1b103539cC13eF28951274d21291BC68

    0xde6724F8E4Ce4698c0096c58554e44F8aCE28600

    BNB Smart Chain (BSC) Testnet

    0xC07642337453820d51Bc60D7f39A53B202E691b8

    0xFA36e2B52b8a21347eB6c46401b210D547e7a4cA

    Sonic Testnet

    0x1992D29b2251F85A3f46B9da95b3A9DdD31CaED1

    CrossCurve DAO
    Layer Zero
    Axelar
    Router Protocol
    [email protected]

    sendData(
            bytes calldata data,
            bytes32 to,
            uint64 chainIdTo,
            bytes[] memory currentOptions
        ) external nonReentrant returns(uint256 fee)
    0x3619F7DA7e8Cc2A6E264d351d2D9a2CD36894063

    DelegationConditionValidatorV1

    Overview

    DelegationConditionValidatorV1 This is an upgradeable contract that allows you to set additional conditions for delegating locks. It is integrated with EscrowManager, which allows you to know the current status of the lock when delegating it.

    Key Roles and Features:

    1. Access Control: Restricts setAssuranceLockParameters and setMinLockVeEywa calls to the owner of contract(owner()).

    2. Upgradeable via UUPS: Uses UUPSUpgradeable and OwnableUpgradeable patterns, restricting contract upgrades to the owner.


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides upgrade functionality under the UUPS proxy pattern, restricted to the contract owner.

    • OwnableUpgradeable (OpenZeppelin): Manages ownership, allowing only the owner to modify critical parameters and authorize upgrades.

    • IDelegationConditionValidatorV1: Defines core methods (e.g., validateDelegations) and events for this contract.

    Additional External References:

    • IDelegationManagerV1: Management of delegated locks

    • IEscrowManagerExtended: Holds locked token data and checks voting power and freeze logic.


    State Variables

    • s_escrowManager (IEscrowManagerExtended) IEscrowManager interface for the EscrowManager contract.

    • s_delegationManager (IDelegationManagerV1) IDelegationManagerV1 for the DelegationManager contract.


    Constructor

    • Description: Disables contract initializers to prevent re-initialization in a UUPS proxy context.


    External Functions (Defined by IDelegationConditionValidatorV1)

    initialize(...)

    Description: Configures ownership, references, and initial state:

    • References the escrow manager and delegation manager.

    Parameters:

    • owner_: The address of the contract owner.

    • escrowManager_: The IEscrowManager interface for the EscrowManager contract.

    • delegationManager_: The IDelegationManagerV1 for the DelegationManager contract.


    validateDelegations(address delegator_, address delegatee_, uint256[] calldata tokenIds_)

    Description: The function performs checks and determines whether delegation of tokenIds_ from delegator_ to delegatee_ is possible.

    Parameters:

    • delegator_: The address of the contract owner.

    • delegatee_: The IEscrowManager interface for the EscrowManager contract.

    • tokenIds_: The IDelegationManagerV1 for the DelegationManager contract.

    Checks:

    • sender must be a DelegationManager contract. Otherwise, UnauthorizedCaller() is thrown


    Errors

    • UnauthorizedCaller() Thrown when the caller is not authorized to perform the action.


    Summary

    DelegationConditionValidatorV1 Being updatable in the long run can provide the opportunity for varied and flexible customization for delegated locks.

    LockHolderV1

    Overview

    LockHolderV1 is an upgradeable contract integrated with DelegationManager, EscrowManager, EscrowVoteManager, IncentiveRewardsAggregatorV1. For each delegator-delegate pair, a different LockHolder contract is deployed, with delegated locks in the balance. Through the LockHolder contract the proxied call of functions of EscrowManager, EscrowVoteManager counters, as well as collection and distribution of incentive rewards takes place.

    Key Roles and Features:

    LockHolderFactoryV1

    Overview

    LockHolderFactoryV1 This is an upgradeable contract that deploys new LockHolder contracts on the blockchain. It is integrated with the DelegationManager contract.

    Key Roles and Features:

    1. Access Control: Restricts setAssuranceLockParameters and

    Access Control: Restricts setAssuranceLockParameters and setMinLockVeEywa calls to the owner of contract(owner()).

  • Upgradeable via UUPS: Uses UUPSUpgradeable and OwnableUpgradeable patterns, restricting contract upgrades to the owner.


  • Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides upgrade functionality under the UUPS proxy pattern, restricted to the contract owner.

    • OwnableUpgradeable (OpenZeppelin): Manages ownership, allowing only the owner to modify critical parameters and authorize upgrades.

    • ILockHolderV1: Defines core methods (e.g., boost, deboost, extend) and events for this contract.

    Additional External References:

    • SafeERC20, IERC20 (OpenZeppelin): Handles secure ERC20 operations for distributing and approving token transfers.

    • IIncentiveRewardsAggregatorV1: Aggregates lists of reward tokens.

    • IEscrowVoteManagerV1: Receives gauge emission amounts and coordinates gauge reward distribution.

    • IEscrowManager: Holds locked token data and checks voting power and freeze logic.


    Constants

    • PRECISION: The divisior for percentage math.


    State Variables

    • s_delegationManager (address) address of the delegation manager contract.

    • s_escrowVoteManager (IEscrowVoteManagerV1) IEscrowVoteManagerV1 interface for the escrow vote manager contract.

    • s_incentiveRewardsAggregator (IIncentiveRewardsAggregatorV1) IIncentiveRewardsAggregatorV1 Interface for the incentive rewards aggregator contract.


    Constructor

    • Description: Disables contract initializers to prevent re-initialization in a UUPS proxy context.


    External Functions (Defined by ILockHolderV1)

    initialize(...)

    Description: Configures ownership, references, and initial state:

    • References the EYWA NFT, escrow manager, escrow vote manager and delegation manager.

    Parameters:

    • owner_: The address of the contract owner.

    • delegationManager_: The address of the delegation manager contract.

    • escrowManager_: The address of the escrow manager contract.

    • escrowVoteManager_: The address of the escrow vote manager contract.

    • incentiveRewardsAggregator_: The address of the incentive rewards aggregator contract.


    claimIncentives(address delegator_, address delegatee_, uint256 percentIncentive_)

    Description: Function for claim and distributing incentives. The function queries arrays with the addresses of the reward tokens and the size of the reward for each of them on the IncentiveRewardsAggregatorV1 contract Then for each IncentiveRewardsDistributor it claim incentives, calculates on the basis of percentIncentive_ what parts of it should be received by delegator_ and delegatee_ and distributes incentives between them.

    Parameters:

    • delegator_: The delegator's address.

    • delegatee_: The delegate's address.

    • percentIncentive_: The percentage of the incentive reward received by the delegate.

    Checks:

    • The function must be called by the DelegationManager contract. Otherwise, UnauthorizedCaller() is thrown.


    Errors

    • UnauthorizedCaller() Thrown when the caller is not authorized to perform the action.


    Summary

    LockHolderV1 contract is an important and necessary part of the lock delegation and self-delegation architecture. It allows for diverse and flexible interaction and integration with all contracts in the system.

    setMinLockVeEywa
    calls to the
    owner
    of contract(
    owner()
    ).
  • Upgradeable via UUPS: Uses UUPSUpgradeable and OwnableUpgradeable patterns, restricting contract upgrades to the owner.


  • Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides upgrade functionality under the UUPS proxy pattern, restricted to the contract owner.

    • OwnableUpgradeable (OpenZeppelin): Manages ownership, allowing only the owner to modify critical parameters and authorize upgrades.

    • ILockHolderFactoryV1: Defines core methods (e.g., createLockHolder) and events for this contract.

    Additional External References:

    • ERC1967Proxy (OpenZeppelin): A proxy implementation that stores the logic contract address in storage per EIP-1967.

    • LockHolderV1: LockHolder contract.


    State Variables

    • s_escrowManager (address) Address of the EscrowManager contract.

    • s_escrowVoteManager (address) Address of the EscrowVoteManager contract.

    • s_delegationManager (address) Address of the DelegationManager contract.

    • s_incentiveRewardsAggregator (address) Address of the IncentiveRewardsAggregator contract.


    Constructor

    • Description: Disables contract initializers to prevent re-initialization in a UUPS proxy context.


    External Functions (Defined by ILockHolderFactoryV1)

    initialize(...)

    Description: Configures ownership, references, and initial state:

    • References the EYWA NFT, escrow manager, escrow vote manager and delegation manager.

    Parameters:

    • owner_: The address of the contract owner.

    • escrowManager_: The address of the escrow manager contract.

    • escrowVoteManager_: The address of the escrow vote manager contract.

    • delegationManager_: The address of the delegation manager contract.

    • incentiveRewardsAggregator_: The address of the incentive rewards aggregator contract.


    createLockHolder()

    Description: The function deploys and initializes an upgradable LockHolder contract. Returns the address of the LockHolder contract.

    Checks:

    • sender must be a DelegationManager contract. Otherwise, UnauthorizedCaller() is thrown.

    Events:

    • Emits LockHolderCreated(m_lockHolder, m_implementation).


    Events

    • LockHolderCreated(address indexed lockHolder, address indexed implementation)) Emitted when a new LockHolder is created.


    Errors

    • UnauthorizedCaller() Thrown when the caller is not the delegation manager.


    Summary

    LockHolderFactoryV1 contract is an important part of the lock delegation architecture. It deploys a new LockHolder contract for each delegator-delegate pair, which provides the ability to reliably track the movement of delegated locks, manage them, and receive and distribute rewards.

    constructor() {
        _disableInitializers();
    }
    function initialize(
        address owner_,
        IEscrowManagerExtended escrowManager_,
        IDelegationManagerV1 delegationManager_
    ) external initializer;
    function validateDelegations(
        address delegator_,
        address delegatee_,
        uint256[] calldata tokenIds_
    ) external view returns(bool);
    uint256 private constant PRECISION = 100_000;
    constructor() {
        _disableInitializers();
    }
    function initialize(
        address owner_,
        address delegationManager_,
        IEscrowManager escrowManager_,
        IEscrowVoteManagerV1 escrowVoteManager_,
        IIncentiveRewardsAggregatorV1 incentiveRewardsAggregator_
    ) external initializer;
    function claimIncentives(
        address delegator_, 
        address delegatee_, 
        uint256 percentIncentive_
    ) external;
    constructor() {
        _disableInitializers();
    }
    function initialize(
        address owner_, 
        address escrowManager_,
        address escrowVoteManager_,
        address delegationManager_,
        address incentiveRewardsAggregator_
    ) external initializer;
    function createLockHolder() external returns (address);

    CalldataHelperV1

    Overview

    CalldataHelperV1 is an upgradeable contract that assists with decoding and slicing transaction calldata. It extracts critical parameters such as method-specific calldata, a target address, and a chain identifier from a given input. This functionality is useful in scenarios where cross-chain calls or proxy calls need to parse custom-encoded calldata.

    By implementing ICalldataHelperV1, the CalldataHelperV1 contract ensures a standardized interface for:

    • Initializing ownership through an upgradeable pattern.

    • Decoding calldata to separate out function-specific parameters from overhead bytes (e.g., selectors).

    • Validating slicing operations to prevent out-of-bounds data reads.


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides functions for upgrading this contract in a UUPS proxy setup, ensuring only the owner can authorize upgrades.

    • OwnableUpgradeable (OpenZeppelin): Implements ownership-related logic, allowing only the contract owner to perform certain actions.

    • ICalldataHelperV1: Declares the initialize and decode functions, as well as the InvalidSliceParameters error.


    Constants and State Variables

    This contract does not introduce new constants besides those inherited or implied from the interface. It also does not maintain any additional state variables beyond upgradeability and ownership structures provided by OpenZeppelin libraries.


    Constructor

    • Description:

      • Disables initializers to ensure this upgradeable contract cannot be re-initialized after deployment, following best practices for UUPS proxy pattern.


    External Functions

    initialize(address owner_)

    • Description:

      • Initializes the contract in an upgradeable context.

      • Sets up ownership by transferring ownership to the specified owner_.

      • Can only be called once due to the


    decode(bytes calldata calldata_)

    • Description:

      • Decodes the provided calldata to extract three main elements:

        1. m_calldata: The method-specific or function-specific bytes of calldata.


    Internal and Private Functions

    _authorizeUpgrade(address)

    • Description:

      • Restricts contract upgrades so that only the owner may authorize them, protecting upgrade logic from unauthorized calls.


    _slice(bytes memory data_, uint256 start_, uint256 length_)

    • Description:

      • Extracts a slice from data_ starting at offset start_ for length_ bytes.

      • Reverts with InvalidSliceParameters if out-of-bounds.


    Errors

    InvalidSliceParameters()

    • Description: Indicates that the requested slice exceeds the bounds of the original array (start_ + length_ > data_.length).


    Summary

    CalldataHelperV1 provides a lightweight, upgradeable solution for parsing transaction calldata and extracting specific parameters like function-specific calldata, target addresses, and chain IDs. It integrates with standard libraries for safe upgradeability (UUPSUpgradeable) and ownership control (OwnableUpgradeable), ensuring secure and maintainable deployment. By strictly enforcing slice parameter checks and skipping the initial 4 bytes, CalldataHelperV1 simplifies the process of handling custom-encoded transaction data in cross-chain or proxied contexts.

    Treasury

    Overview

    Treasury is a minimal yet crucial smart contract that securely stores and allows controlled withdrawals of both native ETH and ERC20 tokens. Owned by a single address, it enforces strict access control on asset movements via onlyOwner and mitigates re-entrant calls through nonReentrant. This design ensures that only the contract owner can authorize withdrawals and that no re-entrancy exploits can siphon funds.

    Key Attributes:

    1. Custody of Funds: Holds ERC20 tokens and ETH in a secure manner.

    2. Restricted Withdrawals: Only the contract’s owner can withdraw funds, specifying the token address, amount, and recipient.

    3. Non-Reentrant Calls: Uses ReentrancyGuard to prevent complex re-entrancy exploits during withdrawals.

    By implementing the ITreasury interface, Treasury provides a standardized API (the withdraw function) and emits an event (TokenWithdrawn) whenever funds are moved out.


    Inherited Contracts and Interfaces

    • Ownable (OpenZeppelin) Allows the contract to have an owner who can perform restricted operations. In this case, only the owner can call withdraw.

    • ReentrancyGuard (OpenZeppelin) Protects critical functions from re-entrancy attacks. The withdraw function uses the nonReentrant modifier.

    • ITreasury (Custom Interface) Declares the withdraw

    Additional External References:

    • SafeERC20, IERC20 (OpenZeppelin): Used to safely transfer ERC20 tokens in the withdraw function.

    • Address (OpenZeppelin): Provides the sendValue method for securely transferring ETH.


    State Variables

    The Treasury contract does not introduce mutable storage variables beyond those inherited from its parent classes. However, it defines:

    • owner (Ownable.sol) A variable inherited from Ownable indicating the contract owner. Managed internally by the Ownable library’s _owner storage variable.

    No other custom state variables are defined. The contract relies on the OpenZeppelin libraries for ownership tracking and reentrancy control.


    Constructor

    • Description: The constructor takes a single parameter, owner_, which designates the address to be assigned as the contract owner via Ownable. This ownership implies exclusive access to withdraw.

    • Parameters:

      • owner_: The address that will be granted ownership of the contract upon deployment.


    Fallback Function

    • Description: A receive fallback function that allows the contract to accept native ETH transfers (e.g., via send or transfer with no calldata). This function does not store or otherwise process the ETH, aside from receiving it into the contract’s balance.


    External Functions

    withdraw(address token_, uint256 amount_, address recipient_)

    • Description: Allows the contract owner to withdraw either an ERC20 token or native ETH from the treasury to a specified recipient_. If token_ is the zero address (address(0)), it indicates a withdrawal of ETH. Otherwise, it withdraws the specified ERC20 token using a safe transfer.

    • Parameters:

      • token_


    Events

    TokenWithdrawn(address indexed token, uint256 indexed amount, address indexed recipient)

    • Emitted When: The withdraw function completes successfully, whether removing ETH or ERC20 tokens.

    • Parameters:

      • token: The address of the token being withdrawn. Zero address if ETH.


    Summary

    Treasury acts as a straightforward contract for securely holding and releasing ETH or ERC20 tokens under the CrossCurve ecosystem. With Ownable restricting withdraw calls to the contract owner and ReentrancyGuard ensuring robust security, the Treasury provides a trustworthy store for tokens:

    1. Secure Custody: Only an authorized owner can move assets out.

    2. Versatile Withdrawals: Supports both ETH and arbitrary ERC20 tokens using standardized SafeERC20 operations.

    3. Transparent Logging: The TokenWithdrawn event ensures on-chain traceability of funds outflow.

    This minimal approach ensures clarity, security, and traceable control over treasury assets within the CrossCurve framework.

    RewardsDistributorFactoryV1

    Overview

    RewardsDistributorFactoryV1 is an upgradeable contract (using UUPS) that deploys new instances of IncentiveRewardsDistributor. It restricts calls to createRewardsDistributor such that only the escrow vote manager can request a new distributor, thus ensuring controlled creation of additional reward distribution contracts.

    Key Roles and Features:

    initializer
    modifier from OpenZeppelin.
  • Parameters:

    • owner_: The address of the contract owner.

  • Effects:

    • Calls __UUPSUpgradeable_init() and __Ownable_init(owner_), configuring the contract for UUPS upgradeability and ownership management.

  • m_target: The address the calldata is meant to target.
  • m_chainId: The chain identifier for cross-chain or multi-chain scenarios.

  • Skips the first 4 bytes, typically used as a selector or prefix.

  • Parameters:

    • calldata_: The full calldata to decode, where the first 4 bytes are not part of the relevant data for extraction.

  • Return:

    • m_calldata: The extracted method calldata (bytes).

    • m_target: The extracted target address (address).

    • m_chainId: The extracted chain identifier (uint64).

  • Logic:

    • Calls the private _slice function to remove the first 4 bytes.

    • Uses abi.decode(...) with a tuple (bytes, address, uint64, address) to decode the relevant fields (although the last address is ignored in this particular decode pattern).

  • Used internally by decode to remove the first 4 bytes (or any other arbitrary slice).

  • Parameters:

    • data_: The original bytes array to slice.

    • start_: The offset in data_ to begin slicing.

    • length_: Number of bytes to copy into the new array.

  • Return:

    • result_: A newly allocated bytes array of size length_, containing the requested slice.

  • Errors:

    • InvalidSliceParameters(): Thrown if start_ + length_ exceeds the length of data_.

  • function signature and
    TokenWithdrawn
    event. The contract implements these specifications.
  • Effects:

    • Calls Ownable(owner_) constructor to set the provided owner_ as the owner.

    • No additional logic is performed here.

  • : The address of the ERC20 token to withdraw. If
    address(0)
    , the function sends ETH instead of tokens.
  • amount_: The amount of tokens (or ETH) to withdraw.

  • recipient_: The address that will receive the withdrawn tokens or ETH.

  • Checks & Effects:

    1. The function uses onlyOwner to ensure only the contract’s owner can invoke it.

    2. Uses nonReentrant from ReentrancyGuard to prevent nested re-entrant calls.

    3. If token_ == address(0), uses Address.sendValue to send amount_ of ETH to recipient_.

    4. Otherwise, calls safeTransfer on IERC20(token_) to transfer amount_ tokens to recipient_.

  • Events:

    • Emits TokenWithdrawn(token_, amount_, recipient_) upon a successful withdrawal, logging details for transparency.

  • amount: The quantity of tokens or ETH withdrawn.

  • recipient: The address that received the withdrawn funds.

  • constructor() {
        _disableInitializers();
    }
    function initialize(address owner_) external initializer
    function decode(bytes calldata calldata_) external pure returns (
        bytes memory m_calldata,
        address m_target,
        uint64 m_chainId
    )
    function _authorizeUpgrade(address) internal override onlyOwner
    function _slice(
        bytes memory data_, 
        uint256 start_, 
        uint256 length_
    ) private pure returns (bytes memory result_)
    constructor(address owner_) Ownable(owner_) {}
    receive() external payable {}
    function withdraw(
        address token_,
        uint256 amount_,
        address recipient_
    )
        external
        onlyOwner
        nonReentrant
    Factory Pattern: It centralizes the instantiation of IncentiveRewardsDistributor contracts, simplifying the process of creating new distributors for different escrow managers.
  • Access Control: Only the escrow vote manager can call createRewardsDistributor, preventing unauthorized distribution contract creation.

  • Upgradeable via UUPS: Ensures the factory contract can be upgraded over time, with ownership-based control on upgrades.

  • By implementing IRewardsDistributorFactoryV1, RewardsDistributorFactoryV1 provides a standard interface for deployment logic and event transparency (though no new custom events are declared here beyond the interface's contract structure).


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides the upgrade mechanism under the UUPS proxy standard, restricting upgrades to the contract owner.

    • OwnableUpgradeable (OpenZeppelin): Establishes ownership logic, allowing the owner to authorize upgrades and potentially modify other aspects of the factory (not used extensively here but available if needed).

    • IRewardsDistributorFactoryV1: Declares the initialize and createRewardsDistributor functions, along with an InvalidCaller error.

    Additional External References:

    • IncentiveRewardsDistributor: The contract being deployed by this factory. It manages incentive reward distributions for an escrow manager and is constructed with references to the escrow vote manager and escrow manager.


    State Variables

    • s_escrowVoteManager (address): The address of the escrow vote manager contract. Only this address may invoke createRewardsDistributor on the factory.


    Constructor

    • Description: Disables contract initializers to ensure initialize() can only be called once. A standard pattern for UUPS upgradeable contracts.


    External Functions

    1. initialize(address owner_, address escrowVoteManager_)

    • Description: Configures the factory contract by assigning ownership (owner_) and storing the escrow vote manager address. This function can be called only once due to the initializer modifier.

    • Parameters:

      • owner_: The address to be assigned as the owner of this factory contract.

      • escrowVoteManager_: The address of the escrow vote manager responsible for orchestrating new distributor creations.

    • Logic and Effects:

      1. Invokes __UUPSUpgradeable_init() and __Ownable_init(owner_) for upgrade and ownership setup.

      2. Sets s_escrowVoteManager = escrowVoteManager_.


    2. createRewardsDistributor(address escrowManager_)

    • Description: Creates and returns a new IncentiveRewardsDistributor contract instance, linking it to the calling escrow vote manager (msg.sender) and the provided escrowManager_. The call reverts if not invoked by s_escrowVoteManager.

    • Checks:

      • Must be called by s_escrowVoteManager. Otherwise, it reverts with InvalidCaller().

    • Logic:

      1. If msg.sender != s_escrowVoteManager, revert with InvalidCaller().

      2. Deploys a new IncentiveRewardsDistributor by calling its constructor:

    • Return:

      • address: The address of the newly created incentive rewards distributor contract.


    Internal Functions

    1. _authorizeUpgrade(address newImplementation)

    • Description: Restricts upgrades of this factory to the contract’s owner, following the UUPS standard. This ensures that only the owner can deploy a new logic implementation for this factory contract.


    Errors

    • InvalidCaller() Thrown if createRewardsDistributor is called by an address other than s_escrowVoteManager.


    Summary

    RewardsDistributorFactoryV1 provides a straightforward pattern for creating new IncentiveRewardsDistributor instances. By restricting calls to its createRewardsDistributor function to the escrow vote manager, it ensures that only authorized processes can spin up new distributor contracts. Coupled with UUPS upgradeability, the factory is future-proofed, allowing for modifications to how distributors are instantiated if the protocol’s requirements evolve over time.

    GaugeFactoryV1

    Overview

    GaugeFactoryV1 is an upgradeable factory contract responsible for deploying and initializing Gauge contracts within the CrossCurve ecosystem. These gauges are used to manage and distribute rewards for various liquidity pools or other vote-driven incentives. The factory integrates with an Escrow Vote Manager to ensure that only the authorized vote manager can create new gauges, thus maintaining a secure and controlled environment for gauge deployments.

    Key Roles and Features:

    1. Gauge Deployment: Deploys a new GaugeV1 instance as an upgradeable proxy (using ERC1967Proxy), specifying an implementation, an owner, and initial parameters for reward distribution campaigns.

    2. Access Control: Restricts createGauge calls to the escrow vote manager (s_escrowVoteManager).

    3. Upgradeable via UUPS: Uses UUPSUpgradeable and OwnableUpgradeable patterns, restricting contract upgrades to the owner.

    By implementing IGaugeFactoryV1, GaugeFactoryV1 provides a standardized interface and event structure for gauge creation, enabling other contracts in the CrossCurve ecosystem to request new gauges securely.


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides upgrade functionality using the UUPS proxy pattern. Only the owner can authorize upgrades.

    • OwnableUpgradeable (OpenZeppelin): Establishes ownership and restricts certain critical functions (like upgrades) to the contract owner.

    • IGaugeFactoryV1: Declares functions (initialize and createGauge) and the GaugeCreated event. Also defines the InvalidCaller

    Additional External References:

    • ERC1967Proxy (OpenZeppelin): A proxy implementation that stores the logic contract address in storage per EIP-1967.

    • IDistributionCreator.CampaignParameters: Used to initialize reward distribution parameters for newly created gauges.

    • GaugeV1: The gauge implementation contract being deployed as a proxy.


    State Variables

    • s_escrowVoteManager (address): The address of the escrow vote manager contract authorized to create new gauges. Only s_escrowVoteManager can call the createGauge function.


    Constructor

    • Description: Disables initializers to prevent re-initialization in a UUPS upgradeable setup. Ensures the initialize function can only be called once.


    External Functions

    initialize(...)

    • Description: Initializes the factory contract by setting the contract owner (owner_) and the escrow vote manager (escrowVoteManager_). This function can only be called once due to the initializer modifier.

    • Parameters:

      • owner_


    createGauge(...)

    • Description: Deploys and initializes a new GaugeV1 contract as an ERC1967Proxy, passing in the gauge implementation address, initialization arguments, and returning the newly created gauge address.

    • Parameters:

      • owner_: The address that will become the owner of the new gauge contract.


    Internal Functions

    _authorizeUpgrade(address)

    • Description: Restricts the contract’s upgrade function (in a UUPS proxy context) to the owner, ensuring unauthorized parties cannot upgrade the factory logic.


    Events

    GaugeCreated(address indexed gauge, address indexed implementation)

    • Emitted When: A new gauge contract is deployed via createGauge.

    • Parameters:

      • gauge: The address of the newly created gauge (proxy).


    Errors

    InvalidCaller()

    • Description: Thrown if createGauge is called by an address other than s_escrowVoteManager. Ensures gauge creation is limited to the authorized escrow vote manager.


    Summary

    GaugeFactoryV1 securely and upgradeably deploys GaugeV1 contracts under the control of the escrow vote manager. By enforcing that only the designated manager can call createGauge, it prevents unauthorized deployments while still allowing flexible, time-extended reward distribution campaigns. The combination of UUPS upgradeability, ownership checks, and a standardized creation event (GaugeCreated) supports a robust, maintainable environment for launching new gauges in the CrossCurve ecosystem.

    GaugeV1

    Overview

    GaugeV1 is an upgradeable contract used to manage reward distributions for a particular pool or strategy in the CrossCurve ecosystem. The contract primarily integrates with the Escrow Vote Manager (s_escrowVoteManager) to authorize reward notifications and uses a Distribution Creator (DISTRIBUTION_CREATOR) for executing reward distribution campaigns.

    Key features include:

    • Upgradeable (UUPS Pattern): The contract can be updated while preserving state.

    • Owner-Based Access Control: Critical functions (like upgrading the contract and updating campaign parameters) are restricted to the owner.

    • Reward Notification: The notifyRewardAmount function can only be invoked by the authorized escrow vote manager, ensuring a controlled flow of rewards.

    • Campaign Parameters: The gauge uses dynamic campaign parameters (s_campaignParameters) to manage reward distribution schedules and amounts, which can be updated by the owner if needed.

    By implementing IGaugeV1, GaugeV1 provides a standardized interface for initializing, updating campaign parameters, and receiving new reward amounts within the CrossCurve ecosystem.


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Enables upgradeability under the UUPS (Universal Upgradeable Proxy Standard) pattern, restricted to the contract owner.

    • OwnableUpgradeable (OpenZeppelin): Provides ownership logic, limiting certain state changes (e.g., upgrading, updating parameters) to the contract owner.

    • IGaugeV1: Interface defining core functions (e.g., initialize, updateCampaignParameters, notifyRewardAmount) and events (

    Additional External References:

    • SafeERC20, IERC20 (OpenZeppelin): Library and interface for safe ERC20 token transfers.

    • IEscrowVoteManagerV1: Tracks and authorizes reward notifications, ensuring that only the designated manager can call notifyRewardAmount.

    • IDistributionCreator: Contract on Arbitrum that actually creates or schedules distribution campaigns (Merkl distributions).


    Constants

    • EPOCH_DURATION: The duration of an epoch (1 week). Used to align reward distribution campaigns with discrete time intervals.

    • DISTRIBUTION_CREATOR: The address of the Merkl distribution contract on Arbitrum. This contract is responsible for orchestrating reward distributions once campaigns are created.


    State Variables

    • s_escrowVoteManager (address) Address of the escrow vote manager contract, which is authorized to call notifyRewardAmount.

    • s_eywa (address) The EYWA token address used for rewards distribution.

    • s_campaignParameters (IDistributionCreator.CampaignParameters) Holds the current campaign parameters for distributing rewards. These parameters include data like amount, duration, start timestamp, and distribution logic, which can be updated by the owner.


    Constructor

    • Description: Disables contract initializers to prevent multiple initializations. Enforces that initialize can be called only once in a UUPS upgradeable context.


    External Functions

    initialize(...)

    • Description:

      • Initializes the gauge contract by setting the owner, escrow vote manager, EYWA token address, and the initial campaign parameters for rewards.

      • Can only be called once, marked by the initializer modifier from OpenZeppelin upgradeable patterns.


    updateCampaignParameters(...)

    • Description:

      • Allows the contract owner to update the gauge’s campaign parameters.

      • The newly provided parameters (newCampaignParameters_) overwrite the existing ones in s_campaignParameters.


    notifyRewardAmount(uint256 amount_)

    • Description:

      • Notifies the gauge of a newly available amount_ of EYWA tokens to be distributed as rewards.

      • Can only be called by s_escrowVoteManager.


    Internal and Private Functions

    _authorizeUpgrade(address)

    • Description:

      • Ensures that only the contract owner can authorize upgrades.

      • Enforced by the UUPSUpgradeable pattern.


    Events

    1. RewardNotified(uint256 indexed amount)

      • Emitted when new rewards are notified to the gauge, indicating the total tokens added.

    2. CampaignParametersUpdated( address indexed gauge, IDistributionCreator.CampaignParameters oldCampaignParameters, IDistributionCreator.CampaignParameters newCampaignParameters )


    Errors

    • UnauthorizedCaller()

      • Thrown if a function restricted to s_escrowVoteManager or onlyOwner is called by an unauthorized address.


    Summary

    GaugeV1 is an upgradeable contract that manages reward distributions for a specific pool or strategy within the CrossCurve ecosystem. By connecting to an Escrow Vote Manager, it ensures only authorized parties can add new rewards (notifyRewardAmount). Through DistributionCreator on Arbitrum, it transforms these rewards into time-bound distribution campaigns. The contract owner can update campaign parameters if needed, while all major functionalities (initialization, upgrade, parameter updates) remain protected by ownership and authorized checks.

    address public s_escrowVoteManager;
    constructor() {
        _disableInitializers();
    }
    function initialize(address owner_, address escrowVoteManager_) external initializer
    function createRewardsDistributor(address escrowManager_) external returns (address)
    function _authorizeUpgrade(address) internal override onlyOwner
    Returns the newly created contract’s address.
    error.
    : The address designated as the owner of this factory.
  • escrowVoteManager_: The address of the authorized escrow vote manager contract.

  • Effects:

    • Calls __UUPSUpgradeable_init() to set up the UUPS upgrade mechanism.

    • Calls __Ownable_init(owner_), assigning ownership to owner_.

    • Sets s_escrowVoteManager to escrowVoteManager_.

  • eywa_: The address of the EYWA token the gauge will handle for reward distribution.

  • campaignParameters_: A struct of parameters (e.g., schedule, amounts) used by the gauge for reward distribution.

  • Checks:

    • The caller must be s_escrowVoteManager. Otherwise, InvalidCaller() is thrown.

  • Logic:

    1. Deploys a new GaugeV1 implementation contract.

    2. Instantiates an ERC1967Proxy pointing to that implementation.

    3. Encodes the constructor arguments (owner, escrow vote manager address, EYWA token address, campaign parameters) for the gauge’s initialize function call via the proxy.

    4. Emits the GaugeCreated event with the new gauge’s proxy address and the gauge implementation address.

  • Return:

    • address: The newly deployed gauge proxy contract.

  • Events:

    • GaugeCreated(m_gauge, m_implementation): Indicates a new gauge contract was created.

  • implementation: The address of the gauge implementation contract used for the new proxy.
    new IncentiveRewardsDistributor(msg.sender, escrowManager_)
    address public s_escrowVoteManager;
    constructor() {
        _disableInitializers();
    }
    function initialize(address owner_, address escrowVoteManager_) external initializer
    function createGauge(
        address owner_,
        address eywa_,
        IDistributionCreator.CampaignParameters calldata campaignParameters_
    ) 
        external 
        returns (address)
    function _authorizeUpgrade(address) internal override onlyOwner
    RewardNotified
    ,
    CampaignParametersUpdated
    ) for the gauge contract.

    The contract uses DISTRIBUTION_CREATOR with a known address (0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd) on Arbitrum.

    Parameters:
    • owner_: The address designated as the contract owner.

    • escrowVoteManager_: The address of the escrow vote manager, authorized to call notifyRewardAmount.

    • eywa_: The address of the EYWA token used for distributing rewards.

    • campaignParameters_: Initial set of campaign parameters (amount, duration, schedule, etc.) used in distribution campaigns.

  • Effects:

    • Calls __UUPSUpgradeable_init() and __Ownable_init(owner_) to set up UUPS upgrade and ownership.

    • Assigns s_escrowVoteManager and s_eywa references.

    • Stores s_campaignParameters from input.

  • Parameters:

    • newCampaignParameters_: The updated distribution parameters (e.g., new schedule or amounts) for this gauge.

  • Events:

    • Emits CampaignParametersUpdated(address(this), oldCampaignParameters, newCampaignParameters_) to log the changes.

  • The function transfers amount_ from the caller to this gauge, checks if the resulting balance is sufficient to meet the minimum distribution threshold, and if so, triggers the distribution campaign via DISTRIBUTION_CREATOR.createCampaign(...).
  • Parameters:

    • amount_: The amount of EYWA tokens to be added for distribution.

  • Checks & Logic:

    1. Verifies msg.sender == s_escrowVoteManager, otherwise reverts with UnauthorizedCaller().

    2. Transfers amount_ of EYWA from the caller to the contract.

    3. If the new balance (multiplied by 1 hour / EPOCH_DURATION) meets the rewardTokenMinAmounts(...) requirement of DISTRIBUTION_CREATOR, the contract approves DISTRIBUTION_CREATOR to spend its entire balance and initiates a campaign:

      • Sets m_campaignParameters.amount to the new gauge balance.

      • Sets startTimestamp = uint32(block.timestamp).

    4. Emits RewardNotified(amount_).

  • Events:

    • RewardNotified(amount_) logs the newly notified reward amount.

  • Logged when the owner updates the gauge’s distribution campaign parameters.

    uint256 public constant EPOCH_DURATION = 1 weeks;
    IDistributionCreator public constant DISTRIBUTION_CREATOR = IDistributionCreator(0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd);
    constructor() {
        _disableInitializers();
    }
    function initialize(
        address owner_,
        address escrowVoteManager_,
        address eywa_,
        IDistributionCreator.CampaignParameters calldata campaignParameters_
    ) 
        external
        initializer
    function updateCampaignParameters(
        IDistributionCreator.CampaignParameters calldata newCampaignParameters_
    ) 
        external 
        onlyOwner
    function notifyRewardAmount(uint256 amount_) external
    function _authorizeUpgrade(address) internal override onlyOwner
  • Sets duration = uint32(EPOCH_DURATION) (one week).

  • Calls createCampaign(...) on DISTRIBUTION_CREATOR.

  • EscrowVoteManagerV1

    Overview

    EscrowVoteManagerV1 is a governance-related contract that manages gauge creation, voting power distribution, reward claiming, and other functionalities within the CrossCurve ecosystem. It integrates with:

    • Escrow Manager (IEscrowManager): Tracks locked EYWA positions.

    • Emission Manager (IEmissionManagerV1): Supplies emission epochs and total reward amounts.

    • Rewards Distributor Factory (IRewardsDistributorFactoryV1): Deploys incentive reward distributor contracts for new gauges.

    • Gauge Factory (IGaugeFactoryV1): Deploys gauge contracts, which are used to distribute rewards based on votes.

    • Reward Distributors (IRewardsDistributor): Handle depositing and withdrawing of votes and distributing claimable incentives.

    The contract also includes logic for:

    1. Voting with multiple token IDs: A user can cast votes across multiple locks (NFTs) for multiple pools.

    2. Transfer Freeze after Deboost: Adds a cooldown period preventing NFT transfers after a lock has been deboosted in the Escrow Manager.

    3. Gauge Lifecycle Management: Owners can create, kill, or revive gauges.

    4. Reward Distribution: Accumulates gauge rewards and distributes them after each epoch.

    By implementing IEscrowVoteManagerV1, EscrowVoteManagerV1 provides a standardized API for gauge-based governance interactions in the CrossCurve ecosystem.


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides upgrade functionality under the UUPS proxy pattern, restricted to the contract owner.

    • OwnableUpgradeable (OpenZeppelin): Manages ownership, allowing only the owner to modify critical parameters and authorize upgrades.

    • IEscrowVoteManagerV1: Defines core methods (e.g., vote, createGauge, distributeRewardsForMultipleGauges) and events for this contract.

    Additional External References:

    • SafeERC20, IERC20 (OpenZeppelin): Handles secure ERC20 operations for distributing and approving token transfers.

    • Math (OpenZeppelin): Provides utility math functions, e.g., Math.max.

    • IEscrowManager: Holds locked token data and checks voting power and freeze logic.


    Constants

    • EPOCH_DURATION: Duration of each emission/gauge epoch (1 week).

    • PRECISION: Scaling factor (1e18) for reward calculations and distribution indexes.


    State Variables

    • s_transferFreezeAfterDeboost (uint256) Duration (in seconds) that locks are non-transferable after a deboost in the Escrow Manager.

    • s_emissionManager (address) Address of the emission manager contract, which calls this manager to notify new rewards.

    • s_eywa (address) Address of the EYWA token used for gauge rewards.


    Constructor

    • Description: Disables contract initializers to prevent re-initialization in a UUPS proxy context.


    External Functions (Defined by IEscrowVoteManagerV1)

    initialize(...)

    Description: Configures ownership, references, and initial state:

    • Sets s_transferFreezeAfterDeboost to 4 hours initially.

    • References the emission manager, escrow manager, gauge/rewards distributor factories, and whitelists initial tokens.


    updateTransferFreezeAfterDeboost(uint256 transferFreezeAfterDeboost_)

    Description: Updates the freeze period (in seconds) that applies after a deboost in Escrow Manager. This restricts NFT transfers for transferFreezeAfterDeboost_ seconds.

    Events:

    • Emits TransferFreezeAfterDeboostUpdated(oldDuration, newDuration).


    createGauge(address pool_, IDistributionCreator.CampaignParameters calldata campaignParameters_)

    Description: Creates a new gauge for a specified pool by:

    1. Deploying an incentive rewards distributor via s_rewardsDistributorFactory.

    2. Deploying a gauge contract via s_gaugeFactory.

    3. Tracking the new gauge and marking it as active.

    Checks:

    • Ensures the pool does not already have a gauge.

    Events:

    • Emits GaugeCreated(pool_, gauge, incentiveRewardsDistributor).


    killGauge(address gauge_)

    Description: Deactivates a gauge so it stops receiving future rewards. Does not remove votes or claimable rewards; simply marks it inactive.

    Checks:

    • s_isActiveGauge[gauge_] must be true.

    Events:

    • Emits GaugeKilled(gauge_).


    reviveGauge(address gauge_)

    Description: Reactivates a previously killed gauge, allowing it to receive rewards again.

    Checks:

    • s_isActiveGauge[gauge_] must be false.

    Events:

    • Emits GaugeRevived(gauge_).


    setWhitelistStatusForTokens(address[] calldata tokens_, bool[] calldata statuses_)

    Description: Updates whether tokens are whitelisted for reward distribution. Only callable by the contract owner. Checks input array lengths.

    Events:

    • Emits WhitelistStatusUpdatedForTokens(operator, tokens_, statuses_).


    vote(uint256[] calldata tokenIds_, address[][] calldata pools_, uint256[][] calldata weights_)

    Description: Allows a user to cast votes for multiple token IDs across multiple pools. For each token ID:

    1. Authorizes caller via IEscrowManager.checkAuthorized.

    2. Validates array lengths for pools and weights.

    3. Performs _vote(...) to reset old votes, allocate new votes, and update vote counts.


    reset(uint256 tokenId_)

    Description: Removes all existing votes for a given token ID, setting them to zero. The caller must be authorized. Updates the token’s last vote timestamp.


    poke(uint256 tokenId_)

    Description: Recalculates (re-casts) votes for a token ID based on updated voting power without changing the vote distribution pattern. The function still calls _vote(...) internally using the same pools and weights previously allocated.


    claimIncentives(address[] calldata incentiveRewardsDistributors_, address[][] calldata rewardTokens_)

    Description: Claims incentives for the caller from multiple rewards distributors. Each distributor has a list of reward tokens. Forwards the call to IRewardsDistributor.getReward.


    notifyRewardAmount(uint256 rewardAmount_)

    Description: Only callable by s_emissionManager. Increments _s_distributionIndex by an amount proportional to rewardAmount_ / totalVotesInPreviousEpoch. If no votes or unauthorized call, reverts.

    Events:

    • Emits RewardNotified(rewardAmount_).


    distributeRewardsForMultipleGauges(address[] calldata gauges_)

    Description: Triggers an epoch update in the emission manager, then calls _distributeRewards(...) for each gauge in gauges_.


    distributeRewardsForGaugesInRange(uint256 start_, uint256 end_)

    Description: Also triggers an emission manager epoch update, then iterates through s_pools[start_ ... end_] to distribute rewards to each gauge.


    getPoolsCount()

    Description: Returns the number of pools (and thus gauges) tracked in s_pools.


    nextEpochStart()

    Description: Returns the next epoch’s start time as s_currentEpochStart + EPOCH_DURATION from the emission manager.


    currentEpochStart()

    Description: Returns the current epoch’s start time from the emission manager.


    Internal and Private Functions

    _authorizeUpgrade(address)

    Description: Restricts contract upgrades to the owner.


    _vote(...)

    Description: Handles the actual voting steps for a single token ID:

    1. Calls _reset(...) to remove existing votes.

    2. Proportionally allocates votingPower_ across pools based on weights_.

    3. Updates s_votesByEpochAndPool, s_votesByTokenIdAndPool, and s_votedPoolsByTokenId

    Events:

    • VoteCast on each pool voted for.


    _reset(uint256 tokenId_)

    Description: Clears existing votes for tokenId_ by:

    • Withdrawing votes from all previously voted pools (if the token voted in the current epoch).

    • Adjusting s_votesByEpochAndPool and s_votesByTokenIdAndPool.

    • Updating the total votes for the epoch.


    _distributeRewards(address gauge_, uint256 currentEpochStart_)

    Description: Distributes claimable EYWA tokens to a specified gauge. If the gauge is inactive, does nothing. Otherwise:

    1. Calls _updateRewardIndexForGauge(...).

    2. Notifies the gauge of claimable rewards via notifyRewardAmount.

    Events:

    • RewardsDistributed after successful distribution.


    _updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_)

    Description: Calculates how many new tokens a gauge can claim using the global distribution index. If there were votes in the previous epoch (currentEpochStart_ - EPOCH_DURATION), the gauge's share of _s_distributionIndex is updated, and claimable rewards are added to s_claimableRewardsByGauge[gauge_].


    Events

    • TransferFreezeAfterDeboostUpdated(oldDuration, newDuration) Emitted when the freeze period is changed.

    • GaugeCreated(pool, gauge, incentiveRewardsDistributor) Emitted upon successful gauge creation.

    • GaugeKilled(gauge), GaugeRevived(gauge) Emitted when a gauge is deactivated or reactivated.


    Errors

    • GaugeDoesNotExist() Thrown if a user tries to vote for a gauge that is not known (no gauge for that pool).

    • GaugeAlreadyExists() Thrown when creating a gauge for a pool that already has one.

    • GaugeNotActive() Thrown if an action (like voting or distributing) is attempted on an inactive gauge.


    Summary

    EscrowVoteManagerV1 orchestrates multi-token, multi-pool voting within the CrossCurve ecosystem. By coordinating gauges, incentive reward distribution, epoch-based reward notifications from the emission manager, and freeze logic from the escrow manager, it ensures a secure, transparent mechanism for users to direct token emissions to liquidity pools and other incentive-based programs. With features like deboost freeze periods and gauge lifecycle control, the contract provides flexible governance and reward distribution for token holders.

    RebaseRewardsDistributorV1

    Overview

    RebaseRewardsDistributorV1 is an upgradeable contract that distributes rebase rewards to veEYWA token holders (locks) in the CrossCurve ecosystem. It relies on an Emission Manager (s_emissionManager) to trigger epoch updates (checkpoints) and fetches locked token data from an Escrow Manager (s_escrowManager). The contract tracks weekly reward distributions and lets veEYWA token holders claim these rebase rewards either directly (if their lock is expired) or by depositing rewards back into the lock (if it’s still active).

    Key features include:

    IRewardsDistributorFactoryV1, IGaugeFactoryV1: Deploy new reward distributors and gauge contracts, respectively.
  • IGaugeV1: Interface for a gauge contract that can receive reward notifications (notifyRewardAmount).

  • IEmissionManagerV1: Informs about epoch starts and ensures updated weekly distributions.

  • s_escrowManager (IEscrowManager) Reference to the contract that manages locked tokens (NFT-based locks).

  • s_rewardsDistributorFactory (IRewardsDistributorFactoryV1) Creates new rewards distributor contracts for gauges.

  • s_gaugeFactory (IGaugeFactoryV1) Creates new gauge contracts.

  • _s_distributionIndex (uint256) A global distribution index used to compute new reward distribution shares after the emission manager notifies reward amounts.

  • s_pools (address[]) An array of pool addresses that have gauges.

  • s_gaugeByPool (mapping(address => address)) Maps a pool address to its gauge contract address.

  • s_poolByGauge (mapping(address => address)) Reverse map: gauge address to the corresponding pool address.

  • s_incentiveRewardsDistributorByGauge (mapping(address => address)) Tracks which incentive rewards distributor belongs to which gauge.

  • s_votesByEpochAndPool (mapping(uint256 => mapping(address => uint256))) Records how many votes each pool has received in a given epoch (keyed by epoch start timestamp).

  • s_claimableRewardsByGauge (mapping(address => uint256)) Shows how many EYWA tokens each gauge can claim, waiting to be distributed.

  • s_lastDistributionTimestampByGauge (mapping(address => uint256)) Timestamp of the most recent reward distribution for each gauge.

  • s_isGauge (mapping(address => bool)) Indicates if an address is recognized as a gauge.

  • s_isWhitelistedToken (mapping(address => bool)) Tracks which tokens are whitelisted for reward distributions and voting.

  • s_isActiveGauge (mapping(address => bool)) If true, the gauge is active and can receive new rewards.

  • s_usedVotesByTokenId (mapping(uint256 => uint256)) How many votes a lock (NFT) has allocated.

  • s_lastVotedTimestampByTokenId (mapping(uint256 => uint256)) Timestamp of the last time a token ID voted, used to reset or poke votes.

  • s_votedPoolsByTokenId (mapping(uint256 => address[])) List of pools each token ID is currently voting for.

  • s_totalVotesByEpoch (mapping(uint256 => uint256)) Cumulative votes across all pools in a given epoch.

  • s_votesByTokenIdAndPool (mapping(uint256 => mapping(address => uint256))) Number of votes allocated by a specific token ID to a particular pool.

  • _s_supplyDistributionIndex (mapping(address => uint256)) Per-gauge distribution index to track how much of the global distribution index the gauge has accounted for.

  • .
  • Deposits votes into the associated gauge’s incentive rewards distributor.

  • Tracks total votes used by the token ID (s_usedVotesByTokenId).

  • Informs escrow manager the token has voted.

  • Marking the token as not voted in the escrow manager.
  • Emitting VotesAbstained.

  • WhitelistStatusUpdatedForTokens(operator, tokens, statuses) Shows updated whitelist status for multiple tokens.

  • RewardNotified(rewardAmount) Occurs when the emission manager notifies a new reward sum.

  • VoteCast(voter, pool, tokenId, votes) Occurs for each pool a token ID votes for.

  • VotesAbstained(voter, pool, tokenId, votes) Occurs when votes are reset/removed for a given pool.

  • RewardsDistributed(distributor, gauge, rewardAmount) Shows distribution of accumulated rewards to a gauge.

  • GaugeAlreadyActive() Thrown if reactivating a gauge that is already active.

  • UnauthorizedAccess() Thrown if a non-authorized caller tries to vote or manage votes.

  • InvalidArrayLengths() Thrown if arrays for tokenIds_, pools_, or weights_ do not match in length.

  • ZeroEntry() Thrown if some operation results in zero (e.g., zero votes allocated to a pool).

  • AlreadyVoted() Not used in current logic, but defined in the interface.

  • ProtectedFunctionSelectorUsed() Not used here (relates to proposal manager).

  • InvalidSliceParameters() Not used here, pertains to slicing logic in other modules.

  • uint256 public constant EPOCH_DURATION = 1 weeks;
    uint256 public constant PRECISION = 1e18;
    constructor() {
        _disableInitializers();
    }
    function initialize(
        address owner_,
        address emissionManager_,
        address eywa_,
        IEscrowManager escrowManager_,
        IRewardsDistributorFactoryV1 rewardsDistributorFactory_,
        IGaugeFactoryV1 gaugeFactory_,
        address[] calldata whitelistedTokens_
    ) external initializer
    function updateTransferFreezeAfterDeboost(uint256 transferFreezeAfterDeboost_) external onlyOwner
    function createGauge(
        address pool_,
        IDistributionCreator.CampaignParameters calldata campaignParameters_
    ) external onlyOwner
    function killGauge(address gauge_) external onlyOwner
    function reviveGauge(address gauge_) external onlyOwner
    function setWhitelistStatusForTokens(
        address[] calldata tokens_,
        bool[] calldata statuses_
    ) external onlyOwner
    function vote(
        uint256[] calldata tokenIds_,
        address[][] calldata pools_,
        uint256[][] calldata weights_
    ) external
    function reset(uint256 tokenId_) external
    function poke(uint256 tokenId_) external
    function claimIncentives(
        address[] calldata incentiveRewardsDistributors_, 
        address[][] calldata rewardTokens_
    ) external
    function notifyRewardAmount(uint256 rewardAmount_) external
    function distributeRewardsForMultipleGauges(address[] calldata gauges_) external
    function distributeRewardsForGaugesInRange(uint256 start_, uint256 end_) external
    function getPoolsCount() external view returns (uint256)
    function nextEpochStart() external view returns (uint256)
    function currentEpochStart() external view returns (uint256)
    function _authorizeUpgrade(address) internal override onlyOwner
    function _vote(
        uint256 tokenId_,
        uint256 votingPower_,
        address[] memory pools_,
        uint256[] memory weights_
    ) private
    function _reset(uint256 tokenId_) private
    function _distributeRewards(address gauge_, uint256 currentEpochStart_) private
    function _updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_) private
  • Epoch-Based Distribution: Rewards are recorded in discrete weekly intervals (epochs) and credited to locks according to each lock’s proportional voting power during the relevant weeks.

  • Checkpointing Logic: Every time new rebase rewards are added, the contract updates an internal “checkpoint,” distributing the newly added tokens proportionally over the elapsed time since the last checkpoint.

  • On-Chain Rebase: Locks continuously earn rebase rewards over time; once claimed, if the lock is still active (not past unlockTime), the rewards are automatically re-deposited. Otherwise, they are transferred to the lock owner.

  • Owner-Only Upgrades: Using a UUPS proxy pattern, only the contract owner can authorize upgrades to the distributor’s logic.

  • By implementing IRebaseRewardsDistributorV1, RebaseRewardsDistributorV1 ensures a standardized interface for initialization, checkpoint updates, and reward claims within the CrossCurve ecosystem.


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides functionality for upgradeable contracts following the UUPS (Universal Upgradeable Proxy Standard) pattern.

    • OwnableUpgradeable (OpenZeppelin): Gives ownership control, allowing only the owner to authorize upgrades and perform restricted actions.

    • IRebaseRewardsDistributorV1 (Custom Interface): Declares the core functions (e.g., initialize, checkpoint, claim) and events (Checkpoint, RewardsClaimed) that define how rebase rewards are managed and distributed.

    Additional External References:

    • SafeERC20, IERC20 (OpenZeppelin): For safe ERC20 transfers of the EYWA token.

    • IEscrowManager: Provides locked token data (voting power snapshots, unlockTime, ownership).

    • IEmissionManagerV1: Signals new epochs by calling checkpoint() and ensures emission periods are up-to-date.


    Constants

    • EPOCH_DURATION: Specifies that each distribution epoch is exactly one week (604,800 seconds).


    State Variables

    1. s_rewardsStartTime (uint256)

      • The timestamp (rounded down to the nearest week) at which rewards distribution officially begins.

    2. s_lastCheckpointTime (uint256)

      • Timestamp of the most recent checkpoint, marking when the contract last distributed tokens among the weekly intervals.

    3. s_previousTokenBalance (uint256)

      • The EYWA token balance in the contract before the most recent checkpoint. Used to calculate how many new tokens were added since the last checkpoint.

    4. s_eywa (IERC20)

      • The EYWA token contract used for rebase rewards.

    5. s_escrowManager (IEscrowManager)

      • The escrow manager contract that manages veEYWA locks and provides historical voting power data.

    6. s_emissionManager (IEmissionManagerV1)

      • The emission manager contract that calls checkpoint() each time new rebase rewards are determined.

    7. s_weeklyTokensDistributed (uint256[1000000000000000])

      • A large array mapping from a week’s starting timestamp (multiple of EPOCH_DURATION) to the total tokens distributed in that particular week.

    8. s_lastClaimedTimeByTokenId (mapping(uint256 => uint256))

      • For each veEYWA token ID, stores the last epoch’s timestamp at which it fully claimed rebase rewards.


    Constructor

    • Description: Disables initializers for this upgradeable contract, ensuring initialize is called only once in a UUPS proxy context.


    External Functions (IRebaseRewardsDistributorV1)

    1. initialize(...)

    • Description:

      • Sets up initial state for the rebase rewards distributor:

        • Assigns contract ownership to owner_.

        • Approves the escrowManager_ to pull unlimited EYWA tokens from this contract.

        • Records the start time of rewards (s_rewardsStartTime = current week boundary) and sets initial s_lastCheckpointTime similarly.

        • Links the references to s_eywa, s_escrowManager, and s_emissionManager.

    • Parameters:

      • owner_: The address designated as the contract owner.

      • eywa_: The EYWA token contract used for distributing rebase rewards.


    2. checkpoint()

    • Description:

      • Called only by s_emissionManager to record a new checkpoint, distributing newly added tokens across the weeks since the last checkpoint.

      • Reverts if msg.sender != s_emissionManager with UnauthorizedCaller().

    • Logic:

      1. Checks authorization.

      2. Calls _checkpoint() to distribute any new tokens proportionally among the weeks that have elapsed since s_lastCheckpointTime.


    3. claim(uint256 tokenId_)

    • Description:

      • Lets a user claim the rebase rewards for a single veEYWA lock identified by tokenId_.

      • Requires that s_emissionManager.s_currentEpochStart() be updated to or beyond the current week; otherwise reverts with EmissionPeriodNotUpdated().

    • Logic:

      1. Checks if the emission manager’s s_currentEpochStart() is updated to the current epoch.

      2. Calls _claim(tokenId_, lastCheckpointWeekBoundary).

      3. If the lock is expired (block.timestamp >= unlockTime), transfers the earned reward to the last lock owner. Otherwise, it deposits the reward back into the lock via


    4. claim(uint256[] calldata tokenIds_)

    • Description:

      • Claims rewards for multiple lock IDs in a single transaction.

      • Similar checks to claim(uint256), verifying the emission period is updated.

      • Iterates over each tokenId_ in tokenIds_ and calls the internal _claim(...), summing up the total claimed amount.


    5. earned(uint256 tokenId_)

    • Description:

      • Returns how many tokens the lock with tokenId_ has accumulated but not yet claimed, using the latest known checkpoint.

      • Internally calls _earned(...) with s_lastCheckpointTime / EPOCH_DURATION * EPOCH_DURATION as the last checkpoint boundary.

    • Return:

      • uint256: The unclaimed rebase reward balance for that particular lock ID.


    Internal and Private Functions

    _checkpoint()

    • Description:

      • The core logic that distributes newly added EYWA tokens among each weekly interval from the last checkpoint time until block.timestamp.

      • If a portion of a week is included, it distributes a proportional share.

    • Logic:

      1. Calculates m_totalReward = currentBalanceOfContract - s_previousTokenBalance.

      2. Spreads this m_totalReward proportionally across all full or partial weeks from s_lastCheckpointTime to block.timestamp.

      3. For each week boundary, updates s_weeklyTokensDistributed[weekTimestamp] +=


    _claim(...)

    • Description:

      • Internal function that calculates how many rebase rewards tokenId_ has earned since its last claim, updates the last claimed time, and emits RewardsClaimed.

      • Returns the amount of newly claimed rewards.

    • Logic:

      1. Calls _earned(tokenId_, lastCheckpointTime_) to compute (m_reward, m_initialWeekCursor, m_updatedWeekCursor).

      2. Sets s_lastClaimedTimeByTokenId[tokenId_] = m_updatedWeekCursor.

      3. If m_reward == 0


    _earned(...)

    • Description:

      • Calculates how many tokens the lock tokenId_ earned from m_initialWeekCursor to lastCheckpointTime_, iterating in weekly increments.

      • Uses historical voting power from the escrowManager.getPastVotesByTokenId(...) and total supply from escrowManager.getPastTotalSupply(...) to determine each week’s share of s_weeklyTokensDistributed.

    • Logic:

      1. Retrieves m_initialWeekCursor = s_lastClaimedTimeByTokenId[tokenId_]. If zero, attempts to set it from the lock’s first recorded epoch data in s_votingPowerPointByTokenIdAndEpoch.

      2. If m_initialWeekCursor >= lastCheckpointTime_, returns zero reward (nothing new to claim).


    Events

    1. Checkpoint(uint256 indexed timestamp, uint256 indexed totalReward)

      • Emitted whenever _checkpoint() distributes a new batch of tokens across weekly intervals.

      • timestamp: The timestamp of the checkpoint (usually block.timestamp).

      • totalReward: The number of newly added tokens distributed.

    2. RewardsClaimed(uint256 indexed tokenId, uint256 indexed initialWeekCursor, uint256 indexed updatedWeekCursor, uint256 reward)

      • Emitted whenever a token ID claims rebase rewards.

      • tokenId: The veEYWA NFT lock ID that claimed.


    Errors

    • UnauthorizedCaller()

      • Thrown if checkpoint() is called by an entity other than s_emissionManager.

    • EmissionPeriodNotUpdated()

      • Thrown if a user attempts to claim but the emission manager’s s_currentEpochStart() has not been advanced to the current week boundary.

    No additional custom errors are introduced beyond those declared in the IRebaseRewardsDistributorV1 interface.


    Summary

    RebaseRewardsDistributorV1 provides a specialized, upgradeable system for distributing weekly rebase rewards to locks in the CrossCurve ecosystem. It does so by:

    • Recording new tokens from the emission manager in discrete weekly intervals via _checkpoint().

    • Allowing token holders to claim their proportional share of rebase rewards at any time, either receiving them directly if their lock is expired or re-depositing them if their lock remains active.

    • Integrating with the escrow manager to read historical voting power for each epoch and proportionally allocate weekly distributed tokens.

    By bridging data from the emission manager and the escrow manager, RebaseRewardsDistributorV1 ensures consistent rebase reward accounting that reflects each lock’s relative stake and time-based accumulation of benefits. It complements the rest of the CrossCurve ecosystem by providing a secure, time-based rebase distribution mechanism with minimal overhead and clear upgrade paths.

    EscrowManager

    Overview

    EscrowManager is a smart contract that manages locked EYWA tokens, each represented by a unique ERC-721 NFT (sometimes referred to as a "veEYWA" token). Each lock NFT stores information about its locked token amount, unlock time, vesting wallets, and booster NFTs. The contract supports operations like depositing, boosting, extending, deboosting, and withdrawing locked tokens, while tracking voting power via time-based checkpoints and epochs. It integrates with an external vote manager contract for governance operations and a collection contract for booster NFTs.

    Key Features:

    1. Lock Creation & Management: Users can create locks by transferring EYWA tokens, vesting wallets, or tokens from an NFT collection. They can add more tokens, apply boosters, or extend the lock duration.

    2. Voting Power: Each lock corresponds to voting power that evolves over time. The contract records checkpoints of ownership and calculates voting weights using a dynamic rate-of-change approach.

    3. Booster Mechanism: Boosters from the collection can increase a lock’s "coverage," thus effectively increasing its lock amount for voting power calculations.

    4. Cooldown & Freeze Logic: After certain actions such as deboosting, token transfers may be temporarily frozen to prevent immediate re-deposit or locking manipulations.

    5. Delegation Checks: The contract can integrate with a delegation manager to move voting power between addresses.

    By implementing the IEscrowManager interface, the EscrowManager contract provides a consistent API for user and external contract interactions in the CrossCurve ecosystem.


    Inherited Contracts and Interfaces

    • ERC721Enumerable, ERC721 (OpenZeppelin): Provides standard ERC-721 NFT functionality with enumeration capabilities, enabling easy listing of all minted NFTs.

    • ERC721Holder (OpenZeppelin): Allows the contract to safely hold ERC-721 tokens (necessary when receiving booster NFTs).

    • ReentrancyGuard (OpenZeppelin): Mitigates re-entrant calls on state-changing functions.

    • SafeERC20, IERC20 (OpenZeppelin):

    External Interfaces:

    • IEscrowManager: The interface implemented by this contract, defining locking and booster logic.

    • IEscrowVoteManagerV1: The vote manager that tracks epochs, triggers freeze logic, and notifies about gauge rewards.

    • ICollection, IWalletFactory, IVestingWallet: Components in the ecosystem for NFT boosters, creating/managing vesting wallets, and bridging custom data.

    • IMetadataProviderV1:


    Constants

    • ATTACHMENTS_LIMIT = 100 Maximum number of attachments (vesting wallets or booster NFTs) allowed per lock.

    • EPOCH_DURATION = 1 weeks Governs epoch-based calculations for voting power and lock logic.

    • MAXIMUM_LOCK_DURATION = 156 weeks Maximum allowed lock duration in seconds (approximately 3 years).

    Immutable Addresses:

    • ESCROW_VOTE_MANAGER (address): The escrow vote manager contract address.

    • DELEGATION_MANAGER (address): The delegation manager contract address.

    • EYWA (IERC20): The EYWA token used in locking.


    State Variables

    • s_nextTokenId (uint256) The next NFT token ID to be minted.

    • s_epoch (uint256) Tracks how many epochs have occurred, updated whenever lock states change significantly.

    • s_totalLocked (uint256) Total EYWA tokens locked across all positions.

    Lock and Booster Details:

    • _s_lockInfoByTokenId (mapping(uint256 => LockInfo)): Internal mapping of lock data.

    • s_pureLockAmountByTokenId, s_vestingWalletsLockAmountByTokenId: Track base token amounts and vesting wallet contributions.

    • s_currentCoverageByTokenId: Booster coverage for each lock.

    Checkpointing and Voting:

    • s_checkpointsNumberByAccount (mapping(address => uint32)): Number of checkpoints for each account.

    • s_checkpoints (mapping(address => mapping(uint32 => Checkpoint))): Account checkpoints listing owned lock NFTs at different times.

    • s_ownershipChangeBlockNumberByTokenId (mapping(uint256 => uint256)): Block number of the last ownership change for each NFT (prevents double-voting in the same block).

    Deboost and Freeze Logic:

    • s_lastDeboostTimestampByTokenId (mapping(uint256 => uint256)): Records the last time a token was deboosted, used to freeze subsequent transfers.


    Constructor

    • Description: Deploys the EscrowManager, sets immutable addresses for the escrow vote manager, delegation manager, EYWA token, collection, wallet factory, and metadata provider, and initializes the NFT name and symbol.


    External Functions (Defined by IEscrowManager)

    createLock(uint256 lockAmount_, uint256 lockDuration_, address recipient_)

    Description: Creates a lock by transferring lockAmount_ of EYWA from the caller, locking it for lockDuration_ (rounded down to epoch boundaries). Mints an NFT for recipient_.


    createLock(uint256 lockDuration_, address recipient_, address[] calldata vestingWallets_)

    Description: Creates a lock by transferring tokens from multiple vesting wallets owned by the caller, then mints an NFT lock. Each vesting wallet's beneficiary must match the caller.


    createLock(uint256 lockDuration_, address recipient_, uint256[] calldata collectionTokenIds_)

    Description: Creates a lock using tokens associated with the given collection token IDs. Collects vesting wallets pinned to those NFTs, locks their balances, and automatically applies boosters via _modifyBoost.


    deposit(uint256 tokenId_, uint256 lockAmount_)

    Description: Adds extra EYWA to an existing lock. Requires caller authorization (_checkAuthorized).


    depositFor(uint256 tokenId_, uint256 lockAmount_)

    Description: Adds EYWA to an existing lock on behalf of the owner, without caller checks.


    boost(uint256 tokenId_, uint256[] calldata collectionTokenIds_)

    Description: Associates additional booster NFTs with a lock, increasing its coverage and potentially its voting power. Booster NFTs may also bring vesting wallets if pinned.


    deboost(uint256 tokenId_, uint256[] calldata collectionTokenIds_)

    Description: Removes booster NFTs from a lock. Verifies that the token hasn't voted in the current epoch. Records the block timestamp in s_lastDeboostTimestampByTokenId to freeze transfers for a defined period.


    withdraw(uint256 tokenId_)

    Description: Withdraws the tokens from an expired lock, returning booster NFTs, transferring vesting wallets, and burning the lock NFT. The caller must be authorized.

    Events:

    • Emits LockWithdrawn(recipient, tokenId, value) upon completion.


    extend(uint256 tokenId_, uint256 lockDuration_)

    Description: Extends the lock duration to a new unlockTime. Must be strictly greater than the current one. Updates voting power.


    setVoteStatus(uint256 tokenId_, bool status_)

    Description: Only callable by ESCROW_VOTE_MANAGER. Marks a token as having voted or not.


    registerTokenVote(uint256 tokenId_, bool status_)

    Description: Only callable by ESCROW_VOTE_MANAGER. Records that a token has voted (or not) in the current epoch.


    moveVotes(uint256 tokenId_, address from_, address to_)

    Description: Called by DELEGATION_MANAGER to transfer voting power if delegated. Ensures the token exists.


    getLockInfoByTokenId(uint256 tokenId_)

    Description: Returns the lock’s current amount, unlock timestamp, and associated vesting wallets.


    getRemainingFreezeTimeByTokenId(uint256 tokenId_)

    Description: Reports how many seconds remain until a token can be transferred again after deboost. Returns 0 if the freeze period has elapsed.


    getCheckpoint(address account_, uint32 index_)

    Description: Retrieves the checkpoint at index_ for account_, listing token ownership at that historical checkpoint.


    getPastVotes(address account_, uint256 timestamp_)

    Description: Calculates an account’s total voting power at a previous timestamp_, summing the power of all tokens they held at that time.


    getTotalSupply()

    Description: Returns the total voting power across all locks at the current time.


    getPastTotalSupply(uint256 timestamp_)

    Description: Returns the total voting power at a specific timestamp_. Uses the VotingPowerHelper for historical calculations.


    getPastVotesByTokenId(uint256 tokenId_, uint256 timestamp_)

    Description: Computes a single token’s voting power at a past timestamp_, factoring in rate-of-change data.


    getBoostersByTokenId(uint256 tokenId_)

    Description: Lists all booster NFT IDs currently enhancing the lock identified by tokenId_.


    checkAuthorized(address owner_, address spender_, uint256 tokenId_)

    Description: Checks if spender_ is permitted (owner, approved address, or operator) to manage the NFT for tokenId_.


    exists(uint256 tokenId_)

    Description: Returns true if an NFT for tokenId_ has been minted (i.e., the lock exists).


    Internal and Private Functions

    _createLock(...)

    Signature:

    Description: Core logic for creating a new lock. Mints an NFT, stores lock info, and transfers tokens or vesting wallets if needed.


    _deposit(...)

    Signature:

    Description: Adds more EYWA to a lock, updating voting power calculations.


    _modifyBoost(...)

    Signature:

    Description: Adds or removes booster NFTs to/from a lock. Adjusts coverage and recalculates voting power accordingly.


    _updateLock(...)

    Signature:

    Description: Recomputes and stores new voting power points whenever a lock’s amount or unlock time changes. Adjusts historical checkpoints and the global epoch.


    _moveVotes(...)

    Signature:

    Description: Reassigns voting power from from_ to to_ if the NFT’s ownership changes. Creates or updates checkpoints for both addresses.


    _clearStorage(...)

    Signature:

    Description: Clears all references and data for a withdrawn or burned lock, freeing storage.


    _findCheckpoint(...)

    Signature:

    Description: Helper to find or create the index for a checkpoint at the current timestamp for a given account.


    _calculateModifiedLockAmount(...)

    Signature:

    Description: Calculates the final lock amount after factoring in booster NFT coverage and rarity-based multipliers. If coverage exceeds lock amount, the algorithm partial-applies boosters in descending rarity.


    Events

    LockWithdrawn(address indexed recipient, uint256 indexed tokenId, uint256 indexed value)

    Emitted When: A user withdraws tokens from an expired lock. The NFT is burned, and booster NFTs/vesting wallets are returned.


    Errors

    • InvalidBeneficiary()

    • InvalidVestingWallet()

    • InvalidLockDuration()

    These errors ensure that contract rules regarding lock durations, amounts, boosters, freeze periods, and ownership checks are strictly enforced.


    Summary

    EscrowManager provides a robust system for locking EYWA tokens in exchange for NFT-based positions (veEYWA). Each lock can combine direct token transfers, vesting wallets, and booster NFTs, forming dynamic voting power that evolves over time. Freeze periods after deboosts and careful checkpointing logic protect against immediate manipulations of lock amounts and ownership. Through a clear interface defined in IEscrowManager, external ecosystem components (e.g., delegation managers, vote managers, NFT collections) can seamlessly integrate with this locking mechanism to support governance, incentive distribution, and user engagement within the CrossCurve platform.

    IncentiveRewardsDistributor

    Overview

    IncentiveRewardsDistributor is a contract that builds upon an abstract RewardsDistributor to manage the distribution of incentive rewards within the CrossCurve ecosystem. The RewardsDistributor base contract provides fundamental logic for tracking deposits, withdrawals, reward notifications, and reward claims over discrete epochs. The IncentiveRewardsDistributor adds:

    1. Whitelisting of Tokens: Before accepting a new token as a reward token, it checks with the escrow vote manager to ensure it is whitelisted.

    2. Escrow Vote Manager Authorization: Restricts calls to certain functions (like getReward) to only the authorized vote manager.

    3. Inheritance and Extended Logic: Integrates seamlessly with the epoch-based deposit/withdraw system in RewardsDistributor to finalize reward amounts and calculations.

    By implementing IIncentiveRewardsDistributor, this contract conforms to a standard reward distributor interface used throughout the CrossCurve ecosystem.


    Inherited Contracts and Interfaces

    • RewardsDistributor (Abstract Contract):

      • Manages core reward distribution functionality, such as:

        • Tracking how many tokens each owner has deposited during a specific epoch.

        • Storing how many rewards are allocated to a token for each epoch.

    Additional External References:

    • IEscrowVoteManagerV1:

      • Provides s_isWhitelistedToken to validate new reward tokens.

      • Acts as an authorization gate for methods that require msg.sender to be the vote manager.


    Contract Architecture

    Inherited from RewardsDistributor

    The base RewardsDistributor constructor sets immutable references for ESCROW_VOTE_MANAGER and ESCROW_MANAGER. It also defines the following main categories of functionality:

    1. Epoch-Based Deposit and Withdrawal:

      • deposit(uint256 amount_, uint256 tokenId_)

      • withdraw(uint256 amount_, uint256 tokenId_)

    We now detail each of these base functions.


    Functions in the Base Contract: RewardsDistributor

    1. Constructor

    • Description: Sets ESCROW_VOTE_MANAGER and ESCROW_MANAGER as immutable addresses, used for authorization and lock ownership lookups.

    • Parameters:

      • escrowVoteManager_: The address of the escrow vote manager contract.


    2. deposit(uint256 amount_, uint256 tokenId_)

    • Description:

      • Called by the escrow vote manager to record that amount_ of tokens have been deposited for the lock represented by tokenId_ in the current epoch.

      • Increments the total supply and the lock owner’s balance for the current epoch.


    3. withdraw(uint256 amount_, uint256 tokenId_)

    • Description:

      • Called by the escrow vote manager to record that amount_ of tokens have been removed from the lock represented by tokenId_ in the current epoch.

      • Decrements the total supply and the lock owner’s balance for the current epoch.


    4. getRewardTokensCount()

    • Description: Returns the length of the s_rewardTokens array, i.e., how many reward tokens the distributor currently recognizes.

    • Return:

      • uint256: number of recognized reward tokens.


    5. earned(address owner_, address rewardToken_)

    • Description:

      • Computes how many tokens of rewardToken_ the owner_ has earned across epochs since their last claim.

      • Iterates through epochs in weekly increments up to a maximum of 52 (1-year look-back) or until reaching the current epoch.


    6. earnedByEpoch(address owner_, address rewardToken_, uint256 epoch_)

    • Description:

      • Returns how many rewardToken_ tokens were earned by owner_ specifically in a single epoch.

      • Calculated as rewardPerToken(rewardToken_, epoch_) * s_balanceByOwnerAndEpoch[owner_][epoch_] / 1e18.


    7. rewardPerToken(address rewardToken_, uint256 epoch_)

    • Description:

      • Calculates the distribution ratio for rewardToken_ in a given epoch_.

      • If s_totalSupplyByEpoch[epoch_] is zero, it simply returns s_rewardAmountByTokenAndEpoch[rewardToken_][epoch_].


    8. getReward(address owner_, address[] calldata rewardTokens_) (abstract)

    • Description:

      • Abstract in the base RewardsDistributor. The child contract (IncentiveRewardsDistributor) provides the actual implementation.

      • Child’s implementation calls _getReward(...) internally after verifying authorization.


    9. notifyRewardAmount(address rewardToken_, uint256 rewardAmount_) (abstract)

    • Description:

      • Abstract method for adding new reward tokens. Child contract overrides this to add whitelisting checks or additional logic.

      • The base logic is available in _notifyRewardAmount(...) (see below).


    10. _getReward(address owner_, address[] calldata rewardTokens_) (internal)

    • Description:

      • The internal function that loops over rewardTokens_, calculates earned(...), updates s_lastRewardClaimByOwnerAndToken, and transfers the reward to owner_.

      • Finally, emits


    11. _notifyRewardAmount(address sender_, address rewardToken_, uint256 rewardAmount_) (internal)

    • Description:

      • Transfers rewardAmount_ of rewardToken_ from sender_ to this distributor.

      • Adds the difference in the contract’s balance to s_rewardAmountByTokenAndEpoch[rewardToken_][currentEpochStart]


    Functions in IncentiveRewardsDistributor

    Constructor

    • Description:

      • Calls the RewardsDistributor constructor to set ESCROW_VOTE_MANAGER and ESCROW_MANAGER.

      • No additional logic besides inheritance.


    getReward(address owner_, address[] calldata rewardTokens_)

    • Description:

      • Implementation of getReward from both RewardsDistributor (abstract) and IIncentiveRewardsDistributor.

      • Allows the escrow vote manager to claim rewards for owner_.


    notifyRewardAmount(address rewardToken_, uint256 rewardAmount_)

    • Description:

      • Implementation of notifyRewardAmount from both RewardsDistributor (abstract) and IIncentiveRewardsDistributor.

      • First ensures rewardToken_ is whitelisted via


    Events and Errors

    Inherited Events

    1. TokensDeposited(from, tokenId, amount)

    2. TokensWithdrawn(from, tokenId, amount)

    3. RewardNotified(from, token, epoch, amount)

    Inherited Errors

    • InvalidRewardToken()

    • UnauthorizedAccess()

    • ZeroAmountProvided()

    No new custom events or errors are introduced in IncentiveRewardsDistributor beyond what’s inherited.


    Summary

    IncentiveRewardsDistributor leverages the robust, epoch-based reward accounting system from RewardsDistributor to facilitate secure incentive distribution in the CrossCurve ecosystem, adding token whitelisting checks and limiting reward claims (getReward) to calls from the escrow vote manager. This design ensures:

    • Authorized Control: Only recognized and whitelisted tokens can be notified as rewards; only the escrow vote manager can trigger claims.

    • Accurate Reward Calculations: Inherits deposit/withdraw logic to track each user’s epoch-based balance.

    • Easy Integration: Conforms to the IIncentiveRewardsDistributor interface, providing consistent methods for reward notifications and claims across the system.

    Together with the RewardsDistributor base contract, IncentiveRewardsDistributor offers a flexible, robust means of awarding incentive tokens in a multi-lock, epoch-based environment.

    uint256 public constant EPOCH_DURATION = 1 weeks;
    constructor() {
        _disableInitializers();
    }
    function initialize(
        address owner_,
        IERC20 eywa_,
        IEscrowManager escrowManager_,
        IEmissionManagerV1 emissionManager_
    ) 
        external
        initializer
    function checkpoint() external
    function claim(uint256 tokenId_) external
    function claim(uint256[] calldata tokenIds_) external
    function earned(uint256 tokenId_) external view returns (uint256)
    function _checkpoint() private
    function _claim(uint256 tokenId_, uint256 lastCheckpointTime_) private returns (uint256)
    function _earned(
        uint256 tokenId_,
        uint256 lastCheckpointTime_
    )
        private
        view
        returns (
            uint256 m_reward,
            uint256 m_initialWeekCursor,
            uint256 m_updatedWeekCursor
        )
    escrowManager_
    : The contract that manages locked positions (NFT-based locks).
  • emissionManager_: The contract that triggers checkpoint updates.

  • escrowManager_.depositFor(...)
    .
  • Deducts the claimed reward from s_previousTokenBalance.

  • the portion of tokens corresponding to that interval.
  • Updates s_previousTokenBalance to the current contract balance.

  • Sets s_lastCheckpointTime = block.timestamp.

  • Emits Checkpoint(block.timestamp, m_totalReward).

  • , returns immediately. Otherwise emits
    RewardsClaimed(tokenId_, m_initialWeekCursor, m_updatedWeekCursor, m_reward)
    .
  • Returns m_reward to the caller for final deposit/transfer logic.

  • Iterates at most 52 times (1 year lookback in weekly steps):

    • For each weekly boundary from m_updatedWeekCursor to lastCheckpointTime_, calculates:

      • m_balance = escrowManager.getPastVotesByTokenId(tokenId_, nextWeekTimestamp)

      • m_supply = escrowManager.getPastTotalSupply(nextWeekTimestamp) (set to 1 if 0).

      • Distributes m_reward += (m_balance * s_weeklyTokensDistributed[weekTimestamp]) / m_supply.

    • Increments m_updatedWeekCursor by EPOCH_DURATION.

  • Returns (m_reward, m_initialWeekCursor, m_updatedWeekCursor).

  • initialWeekCursor: The earliest week boundary for which new rewards are being claimed.
  • updatedWeekCursor: The new last claimed week boundary.

  • reward: The total tokens claimed in this operation.

  • Provides secure wrappers for ERC20 operations, ensuring safe token transfers.
    Supplies metadata URIs for NFTs.
  • VotingPowerHelper (Library): Used to compute historical voting power and supply based on stored checkpoints and rate-of-change data.

  • MAXIMUM_NUMBER_OF_DELEGATES = 1024 Maximum delegates that can be tracked for a single account’s checkpoints.

  • SIGNED_MAXIMUM_LOCK_DURATION = 156 weeks Signed integer equivalent for arithmetic purposes.

  • Rarity Multipliers and Capacities: Predefined numerators and capacity values for Common, Uncommon, Rare, Legendary, and Infinity NFT rarities.

  • PRECISION = 100,000 Scaling factor for multiplier-based calculations (e.g., coverage, booster effects).

  • COLLECTION (ICollection): The collection contract supporting booster NFTs.

  • WALLET_FACTORY (IWalletFactory): Factory contract for validating vesting wallets.

  • METADATA_PROVIDER (IMetadataProviderV1): Supplies off-chain metadata for NFTs.

  • s_boostersByTokenId, s_boostersByTokenIdAndRarity: Maintain sets of booster NFT IDs by token ID and rarity.

  • s_rateOfChangeByUnlockTime (mapping(uint256 => int128)): Tracks changes in voting power at specific unlock times.

  • s_hasVotedByTokenId (mapping(uint256 => bool)): Whether a token has voted in the current epoch.

  • s_hasVotedByEpochAndTokenId (mapping(uint256 => mapping(uint256 => bool))): Tracks vote status for each token in each epoch.

  • s_votingPowerPointByEpoch (mapping(uint256 => VotingPowerPoint)): Stores aggregated voting power changes per epoch.

  • s_votingPowerPointByTokenIdAndEpoch (mapping(uint256 => VotingPowerPoint[1000000000])): Voting power data for each token ID across epochs.

  • InvalidLockAmount()
  • InvalidCollectionTokenId(uint256 tokenId)

  • InvalidArrayLength()

  • InvalidRarity()

  • NonExistentToken()

  • ExtendedUnlockTimeMustBeGreater()

  • AttachmentsLimitExceeded()

  • LockStillActive()

  • LockCurrentlyVoting()

  • MaximumNumberOfDelegatesExceeded()

  • BoostModificationAfterVoting()

  • UnauthorizedCaller()

  • TransferFrozenDueToDeboost()

  • Calculating earned rewards for each owner at specific epochs and enabling claim logic.

  • Declares and implements the IRewardsDistributor interface.

  • IIncentiveRewardsDistributor:

    • Extends IRewardsDistributor with additional constraints and methods (getReward & notifyRewardAmount restricted to certain checks).

  • IEscrowManager:
    • Gives ownership and deposit/withdraw data for NFT-based locks.

    • Indirectly leveraged via RewardsDistributor functions for deposit/withdraw logic.

  • ReentrancyGuard (OpenZeppelin):

    • Inherited in RewardsDistributor to prevent re-entrant calls.

  • SafeERC20, IERC20 (OpenZeppelin):

    • Used for secure ERC20 transfers.

  • Reward Notification and Calculation:
    • notifyRewardAmount(address rewardToken_, uint256 rewardAmount_) (abstract here, implemented in the child contract)

    • earned(address owner_, address rewardToken_) and earnedByEpoch(address owner_, address rewardToken_, uint256 epoch_)

    • rewardPerToken(address rewardToken_, uint256 epoch_)

  • Claiming Rewards:

    • getReward(address owner_, address[] calldata rewardTokens_) (abstract here, partially implemented in the child)

    • _getReward(address owner_, address[] calldata rewardTokens_) (internal)

  • Data Structures for Epoch Accounting and Balances:

    • Mappings storing how much each user has deposited per epoch, total supply per epoch, and how much reward is allocated per epoch.

  • escrowManager_: The address of the escrow manager contract.

    Checks:

    • msg.sender == ESCROW_VOTE_MANAGER; otherwise UnauthorizedAccess().

  • Logic:

    1. Queries the current epoch start via IEscrowVoteManagerV1(ESCROW_VOTE_MANAGER).currentEpochStart().

    2. Increments s_totalSupplyByEpoch[m_currentEpochStart] by amount_.

    3. Retrieves lock owner via IEscrowManager(ESCROW_MANAGER).ownerOf(tokenId_).

    4. Increments s_balanceByOwnerAndEpoch[m_owner][m_currentEpochStart] by amount_.

    5. Emits TokensDeposited(m_owner, tokenId_, amount_).

  • Checks:

    • msg.sender == ESCROW_VOTE_MANAGER; otherwise UnauthorizedAccess().

  • Logic:

    1. Gets the current epoch start, as with deposit.

    2. Decrements s_totalSupplyByEpoch[m_currentEpochStart] by amount_.

    3. Finds lock owner and decreases s_balanceByOwnerAndEpoch[m_owner][m_currentEpochStart] by amount_.

    4. Emits TokensWithdrawn(m_owner, tokenId_, amount_).

  • Returns:

    • (uint256 m_reward, uint256 m_lastRewardClaim): the total reward due and the final epoch boundary used for calculation.

  • Logic:

    1. Checks if rewardToken_ is recognized in s_isRewardToken[rewardToken_]. If not, returns (0,0).

    2. Locates the last claimed epoch timestamp for (owner_, rewardToken_).

    3. Repeatedly calls earnedByEpoch(...) for each epoch from m_lastRewardClaim up to but not including currentEpochStart.

    4. Summation is the total pending rewards.

  • Otherwise, divides that reward amount by the epoch’s total supply (scaled by 1e18).

  • Return:

    • uint256: The per-token reward ratio used to multiply by an owner’s deposit amount to get their share.

  • RewardsClaimed(owner_, token, amount)
    .
    .
  • If s_initialIncentiveTimestamp == 0, sets it to the current epoch start.

  • Emits RewardNotified(sender_, rewardToken_, epoch, difference).

  • Calls _getReward(...) internally after checking msg.sender == ESCROW_VOTE_MANAGER.

    s_isWhitelistedToken
    in
    IEscrowVoteManagerV1
    .
    • If the token is not recognized (!s_isRewardToken[rewardToken_]), checks the vote manager to see if it’s whitelisted; if so, it’s now added to s_rewardTokens.

    • If not whitelisted, reverts with NotWhitelisted().

  • Calls _notifyRewardAmount(msg.sender, rewardToken_, rewardAmount_) to handle actual fund transfer and epoch reward updates.

  • RewardsClaimed(from, token, amount)
    NotWhitelisted()
    constructor(
        address escrowVoteManager_,
        address delegationManager_,
        IERC20 eywa_,
        ICollection collection_,
        IWalletFactory walletFactory_,
        IMetadataProviderV1 metadataProvider_
    ) 
        ERC721("EYWA DAO NFT", "veEYWA")
    {
        ...
    }
    function createLock(
        uint256 lockAmount_,
        uint256 lockDuration_,
        address recipient_
    ) 
        external 
        nonReentrant
    function createLock(
        uint256 lockDuration_,
        address recipient_,
        address[] calldata vestingWallets_
    ) 
        external 
        nonReentrant
    function createLock(
        uint256 lockDuration_,
        address recipient_,
        uint256[] calldata collectionTokenIds_
    )
        external 
        nonReentrant
    function deposit(uint256 tokenId_, uint256 lockAmount_) external nonReentrant
    function depositFor(uint256 tokenId_, uint256 lockAmount_) external nonReentrant
    function boost(uint256 tokenId_, uint256[] calldata collectionTokenIds_) external nonReentrant
    function deboost(uint256 tokenId_, uint256[] calldata collectionTokenIds_) external nonReentrant
    function withdraw(uint256 tokenId_) external nonReentrant
    function extend(uint256 tokenId_, uint256 lockDuration_) external nonReentrant
    function setVoteStatus(uint256 tokenId_, bool status_) external
    function registerTokenVote(uint256 tokenId_, bool status_) external
    function moveVotes(uint256 tokenId_, address from_, address to_) external
    function getLockInfoByTokenId(uint256 tokenId_) 
        external 
        view 
        returns (
            int128 lockAmount_,
            uint256 unlockTime_,
            address[] memory vestingWallets_
        )
    function getRemainingFreezeTimeByTokenId(uint256 tokenId_) external view returns (uint256)
    function getCheckpoint(address account_, uint32 index_) external view returns (Checkpoint memory)
    function getPastVotes(address account_, uint256 timestamp_) external view returns (uint256 votes_)
    function getTotalSupply() external view returns (uint256)
    function getPastTotalSupply(uint256 timestamp_) external view returns (uint256)
    function getPastVotesByTokenId(uint256 tokenId_, uint256 timestamp_) external view returns (uint256)
    function getBoostersByTokenId(uint256 tokenId_) external view returns (uint256[] memory boosters_)
    function checkAuthorized(address owner_, address spender_, uint256 tokenId_) external view returns (bool)
    function exists(uint256 tokenId_) external view returns (bool)
    function _createLock(
        uint256 lockAmount_,
        uint256 lockDuration_,
        address recipient_,
        address[] memory vestingWallets_,
        LockModificationType lockModificationType_
    ) private
    function _deposit(uint256 tokenId_, uint256 lockAmount_) private
    function _modifyBoost(
        uint256 tokenId_, 
        uint256[] memory collectionTokenIds_, 
        LockModificationType lockModificationType_
    ) private
    function _updateLock(
        uint256 tokenId_,
        LockInfo memory previousLockInfo_,
        LockInfo memory currentLockInfo_
    ) private
    function _moveVotes(uint256 tokenId_, address from_, address to_) private
    function _clearStorage(uint256 tokenId_) private
    function _findCheckpoint(address account_) private view returns (uint32)
    function _calculateModifiedLockAmount(
        uint256 tokenId_,
        uint256 coverage_,
        uint256 lockAmount_
    ) private view returns (uint256)
    IncentiveRewardsDistributor (Concrete)
     └── RewardsDistributor (Abstract)
          └── IRewardsDistributor (Interface)
    constructor(address escrowVoteManager_, address escrowManager_) {
        ESCROW_VOTE_MANAGER = escrowVoteManager_;
        ESCROW_MANAGER = escrowManager_;
    }
    function deposit(uint256 amount_, uint256 tokenId_) external
    function withdraw(uint256 amount_, uint256 tokenId_) external
    function getRewardTokensCount() external view returns (uint256)
    function earned(address owner_, address rewardToken_) public view returns (uint256, uint256)
    function earnedByEpoch(address owner_, address rewardToken_, uint256 epoch_) public view returns (uint256)
    function rewardPerToken(address rewardToken_, uint256 epoch_) public view returns (uint256)
    function getReward(address owner_, address[] calldata rewardTokens_) external virtual;
    function notifyRewardAmount(address rewardToken_, uint256 rewardAmount_) external virtual;
    function _getReward(address owner_, address[] calldata rewardTokens_) internal
    function _notifyRewardAmount(address sender_, address rewardToken_, uint256 rewardAmount_) internal
    constructor(address escrowVoteManager_, address escrowManager_) 
        RewardsDistributor(escrowVoteManager_, escrowManager_)
    {}
    function getReward(
        address owner_, 
        address[] calldata rewardTokens_
    )
        external
        override (RewardsDistributor, IIncentiveRewardsDistributor)
        nonReentrant
    function notifyRewardAmount(
        address rewardToken_, 
        uint256 rewardAmount_
    )
        external
        override (RewardsDistributor, IIncentiveRewardsDistributor)
        nonReentrant

    ProposalManager

    Overview

    ProposalManager is a governance contract built on OpenZeppelin’s Governor framework, incorporating additional logic for:

    • Protected Function Selectors: Certain function calls (on specific chain IDs and target addresses) are restricted to the contract owner. This prevents unauthorized calls to critical on-chain functionality within governance proposals.

    • Cross-Chain Awareness: Maintains a set of valid chain IDs for bridging proposals, allowing secure multi-chain governance actions when using the bridge.

    • Custom Quorum Rules: Allows the owner to adjust the quorum fraction within set bounds (25%–80%).

    The contract also integrates with a Calldata Helper (CALLDATA_HELPER) to decode function selectors from the bridging calls. By extending Governor, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl, and Ownable, ProposalManager offers a robust on-chain DAO mechanism with time-locked execution, vote counting, adjustable quorum, and protective measures for crucial function selectors.


    Inherited Contracts and Interfaces

    1. Governor (OpenZeppelin): Base contract for on-chain governance, offering functionalities such as proposal creation, voting, proposal states, etc.

    2. GovernorCountingSimple (OpenZeppelin): Implements a simple vote counting mechanism: For, Against, and Abstain.

    3. GovernorVotes (OpenZeppelin): Integrates an IVotes token (in this case, an escrow manager providing voting power) for calculating a user’s votes at proposal snapshot blocks.

    Additional External References:

    • ICalldataHelperV1 (CALLDATA_HELPER): A contract that decodes selectors from proposal call data, used for identifying protected function selectors in bridging calls.

    • EnumerableSet (OpenZeppelin): Used to store a set of valid chain IDs, ensuring efficient addition, removal, and existence checks.


    Constants

    • MINIMUM_QUORUM_NUMERATOR (25): Minimum permissible quorum fraction (25%).

    • MAXIMUM_QUORUM_NUMERATOR (80): Maximum permissible quorum fraction (80%).


    State Variables

    • CALLDATA_HELPER (ICalldataHelperV1 immutable) The address of a helper contract used to decode function selectors for bridging calls. Set in the constructor and cannot be changed.

    • s_bridge (address) The address of the bridge contract used for cross-chain proposals or calls. Initially set in the constructor, can be updated by setBridge.


    Constructor

    • Parameters:

      • escrowManager_: An IVotes-compliant contract (e.g., an escrow manager) used for vote calculations.

      • timelock_: The TimelockController contract address used to queue and execute proposals after a time delay.


    External Functions

    1. setBridge(address bridge_)

    • Description: Updates the s_bridge address used for bridging calls. Only callable by the contract owner.

    • Parameters:

      • bridge_: The new bridge contract address.


    2. addChainId(uint256 chainId_)

    • Description: Adds a new chain ID to _chainIds. If the chain ID already exists, it reverts.

    • Checks:

      • _chainIds.add(chainId_) must return true, otherwise reverts with ChainIdAlreadyExists(chainId_).


    3. removeChainId(uint256 chainId_)

    • Description: Removes an existing chain ID from _chainIds. If the chain ID does not exist, it reverts.

    • Checks:

      • _chainIds.remove(chainId_) must return true, otherwise reverts with ChainIdDoesNotExist(chainId_).


    4. getChainIdsLength()

    • Description: Returns the number of chain IDs stored in _chainIds.

    • Return:

      • uint256: The length of _chainIds.


    5. getChainIdAtIndex(uint256 index_)

    • Description: Retrieves a chain ID from _chainIds by index_.

    • Return:

      • uint256: The chain ID at the specified index.


    6. addProtectedSelector(uint256 chainId_, address target_, bytes4 selector_)

    • Description: Marks a function selector (selector_) as protected on a particular chainId_ and target_. If the chain ID doesn’t exist or the selector is already protected, it reverts.

    • Events & Checks:

      • Must have _chainIds.contains(chainId_)


    7. removeProtectedSelector(uint256 chainId_, address target_, bytes4 selector_)

    • Description: Removes a protected function selector from s_protectedSelectorsByChainIdAndTarget. If the chain ID isn’t recognized or the selector isn’t actually protected, it reverts.

    • Logic:

      1. Ensures chainId_ is valid in _chainIds.


    8. updateQuorumNumerator(uint256 quorumNumerator_)

    • Description: An override from GovernorVotesQuorumFraction that checks if the new quorum fraction is within [MINIMUM_QUORUM_NUMERATOR, MAXIMUM_QUORUM_NUMERATOR]. Otherwise reverts with GovernorInvalidQuorumFraction(...).


    9. updateTimelock(TimelockController)

    • Description: An override from GovernorTimelockControl. This function does nothing here (the timelock is set in the constructor).


    10. propose(...) (Overridden from Governor)

    • Description:

      • Creates a new proposal. Before creation, the contract calls _checkSelectors(...) to see if any protected calls are included in calldatas_. If protected calls are found but the proposer is not the owner, it reverts with ProtectedFunctionSelectorUsed().

      • After checking, it calls super.propose(...)


    Integration with OpenZeppelin Governor Modules

    clock() (IERC6372)

    • Description: Returns the current timestamp as a uint48. Used by the governance system for time-related logic.

    state(uint256 proposalId_)

    • Description: Returns the current state of a proposal (Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, or Executed), integrating timelock considerations.

    quorum(uint256 timepoint_)

    • Description: Calculates the number of votes required for quorum at a given timepoint_, i.e., (totalSupplyAt(timepoint_) * quorumNumerator(timepoint_)) / quorumDenominator().

    CLOCK_MODE()

    • Description: Returns "mode=timestamp", indicating that block timestamps (instead of block numbers) are used for governance timing.

    proposalThreshold(), votingDelay(), votingPeriod(), proposalNeedsQueuing(...)

    These standard overrides define:

    • proposalThreshold(): 2_500e18 — minimum voting power needed to create a proposal.

    • votingDelay(): 2 days — time between proposal creation and the start of voting.


    Internal Functions

    _checkSelectors(...)

    • Description:

      • Iterates over targets_ and associated calldatas_, extracting the function selector for each. Then checks if it is among the protected selectors for the given chain ID (block.chainid) or if the target is the s_bridge address.


    _queueOperations(...), _executeOperations(...), _cancel(...), _executor()

    These are standard overrides from GovernorTimelockControl that handle queueing, executing, and canceling proposals in the timelock. The _executeOperations override also calls _checkSelectors(...) again before executing the proposal.


    Events

    1. BridgeUpdated(address indexed oldBridge, address indexed newBridge)

      • Emitted when the bridge address changes via setBridge.

    No additional custom events are defined besides those in the IProposalManager interface and standard Governor events.


    Errors

    From IProposalManager, we have:

    • ChainIdAlreadyExists(uint256 chainId)

    • ChainIdDoesNotExist(uint256 chainId)

    • SelectorAlreadyExists(bytes4 selector)


    Summary

    ProposalManager extends and customizes OpenZeppelin’s Governor to cater to CrossCurve DAO’s multi-chain governance needs. Through protective measures (_checkSelectors), it guards certain function calls on specific chain IDs and target addresses, ensuring only the contract owner can propose them. Additionally, it manages a dynamic range of acceptable quorum fractions and integrates with a TimelockController for secure, time-delayed proposal execution. By combining these features with ICalldataHelperV1 for decoding bridging calls, ProposalManager offers a flexible yet secure governance solution for cross-chain proposals in the CrossCurve ecosystem.

    GovernorVotesQuorumFraction (OpenZeppelin): Defines quorum as a fraction of the total supply of the governance token. The fraction is adjustable by the contract’s owner within defined limits.
  • GovernorTimelockControl (OpenZeppelin): Integrates a TimelockController for executing proposals after a governance-defined delay.

  • Ownable (OpenZeppelin): Provides basic ownership functionality, allowing the owner to perform restricted actions (e.g., adjusting quorum fraction, managing chain IDs/selectors).

  • IProposalManager (Custom): Declares additional functions, errors, and events specifically for managing cross-chain or bridging-based proposals, including protected selectors.

  • _chainIds (EnumerableSet.UintSet)
    A private set of valid chain IDs recognized by the contract. The owner can add or remove chain IDs via
    addChainId
    /
    removeChainId
    .
  • s_protectedSelectorsByChainIdAndTarget (mapping(uint256 => mapping(address => bytes4[]))) Stores arrays of protected function selectors for each (chainId, target) pair.

  • s_isProtectedSelector (mapping(uint256 => mapping(address => mapping(bytes4 => bool)))) A quick boolean lookup indicating if a particular function selector is protected on a given chainId and target address.

  • calldataHelper_: The helper contract for decoding function selectors in bridging calls.

  • owner_: Address designated as the owner of this contract (can set quorum fraction, manage chain IDs, etc.).

  • bridge_: The initial address of the cross-chain bridge.

  • Logic and Effects:

    1. Initializes the underlying Governor modules:

      • Governor("EYWA DAO") sets the governor name.

      • GovernorVotes(escrowManager_) specifies the votes token source.

      • GovernorVotesQuorumFraction(50) sets an initial quorum fraction of 50%.

      • GovernorTimelockControl(timelock_) ties this governor to the specified timelock.

      • Ownable(owner_) sets the contract owner.

    2. Stores CALLDATA_HELPER and s_bridge.

  • Events:

    • BridgeUpdated(oldBridge, newBridge) logs the address change.

    , otherwise
    InvalidChainId(...)
    .
  • If s_isProtectedSelector[chainId_][target_][selector_] is true, reverts with SelectorAlreadyExists(...).

  • Adds the selector to s_protectedSelectorsByChainIdAndTarget[chainId_][target_] and sets s_isProtectedSelector[chainId_][target_][selector_] = true.

  • Checks if s_isProtectedSelector[chainId_][target_][selector_] == true; otherwise reverts with SelectorDoesNotExist(...).

  • Searches in the array s_protectedSelectorsByChainIdAndTarget[chainId_][target_], removes the entry by swapping the last element, then popping the array.

  • Deletes s_isProtectedSelector[chainId_][target_][selector_].

  • to proceed with the standard Governor proposal workflow.
  • Parameters:

    • targets_, values_, calldatas_: Arrays specifying which contracts and functions to call, with how much ETH, plus the function data.

    • description_: A string describing the proposal.

  • Return:

    • uint256: The ID of the newly created proposal.

  • votingPeriod()
    :
    5 days
    — how long votes are accepted.
  • proposalNeedsQueuing(...): returns true, indicating proposals must be queued in the timelock after passing, before execution.

  • If any call uses a protected selector and proposalCreator_ is not the contract owner, reverts with ProtectedFunctionSelectorUsed().
  • Parameters:

    • proposalCreator_: The address that created the proposal.

    • targets_: The array of target contract addresses.

    • calldatas_: The array of encoded function calls corresponding to each target.

  • Logic:

    1. If targets_[i] equals s_bridge, decodes further with CALLDATA_HELPER.decode(...) to find (m_calldata, m_target, m_chainId). Then extracts the first 4 bytes (selector) from m_calldata.

    2. Checks s_protectedSelectorsByChainIdAndTarget[m_chainId][m_target].

    3. If matched, marks m_isProtected = true.

    4. Otherwise, if targets_[i] != s_bridge, reads the first 4 bytes from calldatas_[i] as the selector and checks s_protectedSelectorsByChainIdAndTarget[block.chainid][targets_[i]].

    5. If m_isProtected == true and proposalCreator_ != owner(), revert with ProtectedFunctionSelectorUsed().

  • SelectorDoesNotExist(bytes4 selector)
  • InvalidChainId(uint256 chainId)

  • ProtectedFunctionSelectorUsed()

  • GovernorInvalidQuorumFraction(uint256 newQuorumNumerator, uint256 quorumDenominator) (part of GovernorVotesQuorumFraction logic)

  • uint256 public constant MINIMUM_QUORUM_NUMERATOR = 25;
    uint256 public constant MAXIMUM_QUORUM_NUMERATOR = 80;
    constructor(
        IVotes escrowManager_,
        TimelockController timelock_,
        ICalldataHelperV1 calldataHelper_,
        address owner_,
        address bridge_
    )
        Governor("EYWA DAO")
        GovernorVotes(escrowManager_)
        GovernorVotesQuorumFraction(50)
        GovernorTimelockControl(timelock_)
        Ownable(owner_)
    {
        CALLDATA_HELPER = calldataHelper_;
        s_bridge = bridge_;
    }
    function setBridge(address bridge_) external onlyOwner
    function addChainId(uint256 chainId_) external onlyOwner
    function removeChainId(uint256 chainId_) external onlyOwner
    function getChainIdsLength() external view returns (uint256)
    function getChainIdAtIndex(uint256 index_) external view returns (uint256)
    function addProtectedSelector(uint256 chainId_, address target_, bytes4 selector_) external onlyOwner
    function removeProtectedSelector(uint256 chainId_, address target_, bytes4 selector_) external onlyOwner
    function updateQuorumNumerator(uint256 quorumNumerator_) external override onlyOwner
    function updateTimelock(TimelockController) external override
    function propose(
        address[] memory targets_,
        uint256[] memory values_,
        bytes[] memory calldatas_,
        string memory description_
    )
        public
        override (Governor, IGovernor)
        returns (uint256)
    function clock() public view override (Governor, GovernorVotes, IERC6372) returns (uint48)
    function state(uint256 proposalId_) public view override(Governor, GovernorTimelockControl, IGovernor) returns (ProposalState)
    function quorum(uint256 timepoint_) public view override (Governor, GovernorVotesQuorumFraction, IGovernor) returns (uint256)
    function CLOCK_MODE() public pure override (Governor, GovernorVotes, IERC6372) returns (string memory)
    function _checkSelectors(
        address proposalCreator_,
        address[] memory targets_,
        bytes[] memory calldatas_
    )
        private
        view

    CrossCurve SuperDVN

    Overview

    CrossCurve SuperDVN is a verification module (DVN, Decentralized Verification Network) compatible with the LayerZero DVN standard. It leverages the CrossCurve Consensus Bridge as its internal mechanism for cross-chain message verification and transmission.

    SuperDVN serves as a sovereign data verification layer for cross-chain applications, providing a decentralized, secure, and resilient environment for communication between blockchains.

    Architecture

    LayerZero uses DVNs to achieve decentralized verification of cross-chain message transfers. However, ensuring robust security requires two or more independent DVNs.

    For developers, SuperDVN appears as a single DVN, but internally it contains a consensus layer composed of multiple messaging protocols, combining their verification capabilities for enhanced reliability and trustlessness.

    Message Flow:

    • The OApp application sends a message on the source network to the LayerZero .

    • The LayerZero protocol processes the message in its standard way and forwards it for verification to the CrossCurve DVN contract.

    • The CrossCurve DVN prepares and sends the verification data to the CrossCurve Gatekeeper.

    Integration with LayerZero

    SuperDVN is fully compliant with the LayerZero , which provides:

    Ability to connect as a primary DVN for cross-chain messaging between networks using LayerZero.

    Support for custom DVN, allowing SuperDVN to be used for specific applications.

    Compatibility with the LayerZero ecosystem without the need to modify smart contracts.

    Supported Protocols

    SuperDVN leverages CrossCurve Consensus Bridge to enhance verification security.

    Currently, the Consensus Bridge supports the following messaging protocols:

    • CrossCurve Oracle Network

    Connection and Integration

    To integrate SuperDVN into an OApp, you need to configure the ULN as specified in the LayerZero .

    Here is an example configuration for using a single SuperDVN:

    SuperDVN.address — the address of the SuperDVN contract on the OApp network that sends the message.

    Contracts

    To configure the OApp, use the following contract addresses:

    Network
    CrossCurve SuperDVN Address
    The Gatekeeper dispatches this data through multiple messaging protocols.
  • Each protocol, following its standard cross-chain messaging procedure, independently delivers the data to the CrossCurve Receiver contract.

  • The Receiver collects the incoming message instances and emits events upon receipt.

  • The external service CrossCurve Pusher monitors these events. Once the original message and a sufficient number of verification confirmations are detected, it submits an execution transaction to the CrossCurve Receiver contract.

  • Upon receiving the transaction from CrossCurve Pusher, the Receiver checks that the original message and all required protocol confirmations are present.

  • If verification succeeds, the data is passed back to the CrossCurve DVN, which forwards the verification data to the ReceiveLib contract to complete the verification process and allow LayerZero to proceed with message delivery.

  • Ethereum Sepolia Testnet

    0xA8e6c5932fc3F0BBd4532e911BC1e14db78F35e9

    Arbitrum Sepolia Testnet

    0x2245F56774fa53966643bCeC94F916cBd16AA854

    Endpoint contract
    DVN Standard
    LayerZero
    Axelar
    Router Protocol
    documentation
    const ulnConfig = {
        confirmations: 1,
        requiredDVNCount: 1,
        optionalDVNCount: 0,
        optionalDVNThreshold: 0,
        requiredDVNs: [SuperDVN.address],
        optionalDVNs: []
      };
    
    
      const encodedUlnConfig = ethers.utils.defaultAbiCoder.encode(
        ["tuple(uint64 confirmations, uint8 requiredDVNCount, uint8 optionalDVNCount, uint8 optionalDVNThreshold, address[] requiredDVNs, address[] optionalDVNs)"],
        [ulnConfig]
      );

    DelegationManagerV1

    Overview

    DelegationManagerV1 This is an upgradeable contract that allows you to delegate your locks to another account. This contract is integrated with EmissionManager, EscrowManager, EscrowVoteManager, LockHolderFactory, RebaseRewardsDistributor, DelegationConditionValidator. It allows the lock owner to delegate or automate actions such as voting and collecting rebase rewards and incentive rewards.

    Key Roles and Features:

    1. Access Control: Restricts setAssuranceLockParameters and setMinLockVeEywa calls to the owner of contract(owner()).

    2. Upgradeable via UUPS: Uses UUPSUpgradeable and OwnableUpgradeable patterns, restricting contract upgrades to the owner.


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Provides upgrade functionality under the UUPS proxy pattern, restricted to the contract owner.

    • OwnableUpgradeable (OpenZeppelin): Manages ownership, allowing only the owner to modify critical parameters and authorize upgrades.

    • IDelegationManagerV1: Defines core methods (e.g., setDelegationParameters, delegate, vote) and events for this contract.

    Additional External References:

    • SafeERC20, IERC20 (OpenZeppelin): Handles secure ERC20 operations for distributing and approving token transfers.

    • IRebaseRewardsDistributorV2: Distributes rebase rewards and triggers relevant accounting updates.

    • IDelegationConditionValidatorV1: Logic to test additional conditions for delegation

    • ILockHolderFactoryV1: Deployment of new LockHolder contracts


    Constants

    • EPOCH_DURATION: Duration of each emission/gauge epoch (1 week).

    • PRECISION: The divisior for percentage math.


    State Variables

    • s_eywa (IERC20) ERC20 token interface for the EYWA token.

    • s_collection (ICollection) ICollection Interface for the NFT collection contract.

    • s_emissionManager (IEmissionManagerV1) IEmissionManagerV1 interface for the EmissionManagerV1 contract.


    Constructor

    • Description: Disables contract initializers to prevent re-initialization in a UUPS proxy context.


    External Functions (Defined by IDelegationManagerV1)

    initialize(...)

    Description: Configures ownership, references, and initial state:

    • Sets s_minLockEywa to 500 000 * 1e18 initially.

    • Sets s_minLockVeEywa to 1 000 * 1e18 initially.

    • Sets s_minLockDuration to 52 weeks initially.

    Parameters:

    • owner_: The address of the contract owner.

    • eywa_: ERC20 token interface for the EYWA token.

    • collection_: ICollection Interface for the NFT collection contract.


    setAssuranceLockParameters(uint256 minLockEywa_, uint256 minLockDuration_)

    Description: Setting parameters for the delegate assurance lock

    Parameters:

    Checks:

    • Only the owner of the contract can call this function.


    setMinLockVeEywa(uint256 minLockVeEywa_)

    Description: Setting the minimum veEYWA value for delegated locks

    Parameters:

    • minLockVeEywa_: The minimum number of veEYWA that must be in the delegated lock.

    Checks:

    • Only the owner of the contract can call this function.


    setDelegationParameters(DelegationParameters memory delegationParameters_)

    Description: Setting delegation parameters for the delegate If this is the first time a delegate is setting parameters, its address is added to the delegate array (s_delegaties) delegationParameters_.eywaDeposit and the delegate's current deposit. Delegation parameters can only be changed once the rewards have been received and distributed and rent has been paid for all delegated locks. When one or more parameters are changed: delegationParameters_.maxDelegatedVeEYWA, delegationParameters_.delegationEnd, delegationParameters_.eywaDeposit - the rent per veEYWA unit per epoch is recalculated. Performs a transfer of the required number of EYWA tokens from the delegate's account to his deposit in the contract. This deposit will be used to pay for lock rentals to delegates. The required number is calculated as the difference between

    Parameters:

    • delegationParameters_: Parameters of the delegation.

    Checks:

    • The delegate must pass the correct parameters. Otherwise, WrongDelegationParameters() is thrown.

    • The delegate assuranceLock lock must comply with the requirements set forth. Otherwise, BadAssuranceLock() is thrown.

    • All rewards for all delegated locks must be received. Otherwise, RewardsUnclaimed() is thrown.


    setDelegationVoteParameters(address[] calldata pools_, uint256[] calldata weights_)

    Description: Setting the parameters auto-voting for a specific delegate.

    Parameters:

    • pools_: The array of voting pools.

    • weights_: The array with voice weights for each pool.

    Checks:

    • pools_ and weights_ arrays must not be null and must have the same size. Otherwise, InvalidArrayLengths() is thrown.


    setAutoVotePermission(bool permission_)

    Description: Set the permission for automatic voting using the autoVote function. With this permission, this autoVote function can be called by any account. To perform automatic voting, you must set the parameters with the setDelegationVoteParameters function

    Parameters:

    • permission_: The permission parameter.


    boost(uint256 tokenId_, uint256[] calldata collectionTokenIds_)

    Description: The function proxies the call of the boost function on the EscrowManager contract.

    Parameters:

    • tokenId_: The ID of the token representing the lock.

    • collectionTokenIds_: An array of token IDs from the collection that will be used to boost the lock.

    Checks:

    • The lock must be self-delegation and the caller must be a delegator. Otherwise, UnauthorizedCaller() is thrown.

    • Delegation time should not be finalized. Otherwise, DelegateTimeExpired() is thrown.


    deboost(uint256 tokenId_, uint256[] calldata collectionTokenIds_)

    Description: The function proxies the call of the deboost function on the EscrowManager contract. And then performs a transfer of the NFTs withdrawn from the lock to the delegator balance.

    Parameters:

    • tokenId_: The ID of the token representing the lock.

    • collectionTokenIds_: An array of token IDs from the collection that will be used to boost the lock.

    Checks:

    • The lock must be self-delegation and the caller must be a delegator. Otherwise, UnauthorizedCaller() is thrown.


    extend(uint256[] calldata tokenIds_, uint256[] calldata lockDurations_)

    Description: The function proxies the call of the extend function on the EscrowManager contract.

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.

    • collectionTokenIds_: An array of token IDs from the collection that will be used to boost the lock.

    Checks:

    • The tokenIds_ and lockDurations_ arrays must have the equal length. Otherwise, InvalidArrayLengths() is thrown.

    • For each lock from tokenIds delegatee_ must be a delegator. Otherwise, WrongDelegatee() is thrown.


    withdrawDeposit(uint256 amount_)

    Description: Function for deposit withdrawal. Performs transfer of amount_ of EYWA tokens from contract balance to msg.sender balance. Decreases by amount_ the value in s_depositByDelegatee on the msg.sender key. A delegate can only withdraw a deposit if the current delegation has been completed and all payments for all delegated locks have been made.

    Parameters:

    • amount_: The amount of EYWA tokens.

    Checks:

    • Checks that the delegate's deposit is not less than amount_. Otherwise, NotEnoughAmount() is thrown.

    • Fee must be paid for each delegated lock. Otherwise, DelegationUnpaid() is thrown.


    claim()

    Description: The function transfers EYWA, received as a commission for delegated locks, to the balance of the delegator


    delegate(address delegatee_, uint256[] calldata tokenIds_)

    Description: This function performs a number of necessary checks, after which the internal function _delegate() is called, in which delegation takes place.

    Parameters:

    • delegatee_: The delegate's address.

    • tokenIds_: An array of IDs of the token representing the lock.

    Checks:

    • If it is not a self-delegation, it is checked that delegatee_ has set the delegation parameters. Otherwise, DelegateeNotExist() is thrown.

    • If it is not self-delegation, it is verified that the condition of the assurance lock is as specified. Otherwise, BadAssuranceLock() is thrown.

    • If it is not a self-delegation, iit is checked that the maximum number of veEYWA specified in the delegation parameters will not be exceeded. Otherwise, MaxDelegatedVeEywaExceeded()

    Events:

    • Emits Delegate(delegator, delegatee, tokenIds).


    paymentAndExtendDelegations(uint256[] calldata tokenIds_)

    Description: The function transfers lock commissions from the delegatee's deposit to the delegator's deposit, for all epochs since the last time the commission was received, and renews locks

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.


    claimIncentives(uint256[] calldata tokenIds_)

    Description: The function proxies the call of the claimIncentives function on the LockHoldeV1 contract. After collecting and distributing the bounty, notes that the bounty for the specified locks is received in this epoch.

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.


    revokeDelegations(uint256[] calldata tokenIds_)

    Description: Function to revoke the delegation. Sets the start of the next epoch to the time the delegation ends. After the start of the next epoch, these delegated locks cannot be voted on.

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.

    Checks:

    • For each lock from tokenIds_, it is checked that the function is called by a delegator or delegate. Otherwise, UnauthorizedCaller() is thrown.

    • For each lock from tokenIds_, it is checked that its delegation time has not yet expired. Otherwise, DelegateTimeExpired() is thrown.


    recallDelegations(uint256[] calldata tokenIds_)

    Description: Delegation Completion Function. In the normal situation, this function is called in the next epoch, after the revokeDelegations function has been called. For self-delegated locks, this function can be called at any time, without first calling the revokeDelegations function. If the delegation time specified in the delegation parameters has expired, you can call this function, without first calling the revokeDelegations function. After partitioning a lock, reduces by its veEYWA value the amount of veEYWA delegated to the delegate.

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.

    Checks:

    • You can't recall assurance lock. Otherwise, DelegationWithdrawalForbidden() is thrown.

    • For each lock from tokenIds_, it is checked that the function is called by a delegator or delegate. Otherwise, UnauthorizedCaller() is thrown.

    • All rewards for all delegated locks must be received. Otherwise, RewardsUnclaimed() is thrown.

    Events:

    • Emits RecallDelegations(tokenIds_).


    function vote(uint256[] calldata tokenIds_, address[][] calldata pools_, uint256[][] calldata weights_)

    Description: The function proxies the call of the extend function on the EscrowVoteManager contract.

    For each lock from tokenIds_ the necessary checks are performed in the internal function _checkDelegation, which will be described in detail below

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.

    • pools_: An array of voting pools.

    • weights_: An array with voice weights for each pool.


    autoVote(address delegatee_, uint256[] calldata tokenIds_)

    Description: The function proxies the call of the extend function on the EscrowVoteManager contract.

    The delegate must set permission for auto-voting in the setAutoVotePermission function and set the voting parameters in the setDelegationVoteParameters function. Then any account can perform voting by delegated locks of this delegate using the set parameters.

    For each lock from tokenIds_ the necessary checks are performed in the internal function _checkDelegation, which will be described in detail below

    Parameters:

    • delegatee_: An array of IDs of the token representing the lock.

    • tokenIds_: An array of IDs of the token representing the lock.

    Checks:

    • The delegatee_ must set the authorization for auto-voting. Otherwise, NotSetAutoVote() is thrown.

    • The delegatee_ must set the parameters for auto-voting. Otherwise, NotSetAutoVoteParameters() is thrown.

    • For each lock from tokenIds


    reset(uint256[] calldata tokenIds_)

    Description: The function proxies the call of the reset function on the EscrowVoteManager contract.

    For each lock from tokenIds_ the necessary checks are performed in the internal function _checkDelegation, which will be described in detail below.

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.


    poke(uint256[] calldata tokenIds_)

    Description: The function proxies the call of the poke function on the EscrowVoteManager contract.

    For each lock from tokenIds_ the necessary checks are performed in the internal function _checkDelegation, which will be described in detail below.

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.


    isAvailableDelegate(address delegator_, address delegatee_, uint256[] calldata tokenIds_)

    Description: The function checks all current delegation parameters accepts determines whether delegation from delegator_ to delegatee_ of tokenIds_ locks is possible. Returns true/false - delegation is or is not possible for the given values

    Parameters:

    • delegator_: The delegator's address.

    • delegatee_: The delegate's address.

    • tokenIds_: An array of IDs of the token representing the lock.


    getDelegationsParameters()

    Description: The function returns an array of parameters of all delegates


    getDelegationsVoteParametersByDelegate(address delegatee_)

    Description: The function returns the auto-voting parameters for a specific delegatee_

    Parameters:

    • delegatee_: The delegate's address.


    getDelegationsInfoByTokenIds(uint256[] calldata tokenIds_)

    Description: The function returns an array with delegation information for an array of delegations

    Parameters:

    • tokenIds_: An array of IDs of the token representing the lock.


    getDelegationInfoAndParametersByTokenId(uint256 tokenId_)

    Description: The function returns delegation information delegationInfo_ and delegation parameters delegationParameters_ for a specific delegated lock by its tokenId_

    Parameters:

    • tokenId_: The ID of the token representing the lock.


    getLockIdsByDelegator(address delegator_)

    Description: The function returns an array of id's of all locks delegated by the delegator_

    Parameters:

    • delegator_: The delegator's address.


    getLockIdsByDelegatee(address delegatee_)

    Description: The function returns an array of id's of all locks delegated to the delegatee_

    Parameters:

    • delegatee_: The delegate's address.


    getAssuranceLockStatus(uint256 tokenId_, address delegatee_)

    Description: The function checks and returns true/false the status of tokenId_ as a Assurance lock for the delegatee_

    Parameters:

    • tokenId_: The ID of the token representing the lock.

    • delegatee_: The delegate's address.


    Internal and Private Functions

    _delegate(IEscrowManagerExtended escrowManager_, DelegationParameters memory delegationParameters_, uint256[] calldata tokenIds_, address delegatee_)

    Description: The delegation function performs the necessary checks and then performs a lock transfer to the LockHolder contract, and the entire veEYWA of the lock is transferred to the delegate_ account. If this sender delegates locks to this delegate_ for the first time, a new LockHolder contract is deployed for them. The necessary data structures are also created to store information about the delegated lock. If a sender address or null address is specified as delegate_, self-delegation occurs.

    Parameters:

    • escrowManager_: The IEscrowManagerExtended interface for the EscrowManager contract.

    • delegationParameters_: Parameters of the delegation.

    • tokenIds_: The array of IDs of the token representing the lock.

    Checks

    • If it is not a self-delegation, checked that the veEYWA value of all lots specified in tokenIds_ is not less than the s_minLockVeEywa value set in the contract minVeEYWA set in delegations parameters. Otherwise, WrongVeEywaAmount() is thrown.

    • If it is not self-delegation, checks that the time to unlock the lock is not lower than the minimum time set by the delegatee_. Otherwise, LittleTimeToUnlock() is thrown.


    _recallDelegation(address delegator_, address delegatee_, uint256 tokenId_)

    Description: Delegation recall function

    First, a check is made to see if the vote was made by this tokenId_, and if so, the reset function is called on the LockHolder contract to reset the vote. Otherwise it will be impossible to perform a lock transfer.

    The data structures are then overwritten and the data of the revocable delegation is deleted.

    At the end, a transfer of the voiting power and lock token to the delegator address is made

    Checks:

    • For tokenId_, it is checked that there exists a LockHolder contract corresponding to the delegate-delegate pair. Otherwise, LockHolderNotExist() is thrown.

    Parameters:

    • delegator_: The delegator's address.

    • delegatee_: The delegate's address.

    • tokenId_ The ID of the token representing the lock.


    _getVotes(IEscrowManagerExtended escrowManager_, uint256[] calldata tokenIds_)

    Description: Returns the sum of veEYWA from an array of locks tokenIds_

    Parameters:

    • tokenIds_: The array of IDs of the token representing the lock.


    _getLastEpochOfDelegation(uint256 timeExpiry_, uint256 delegationEnd_)

    Description: The function returns the completion time of the delegation

    Parameters:

    • timeExpiry_: The expiry timestamp to verify.

    • delegationEnd_: The epoch in which the delegation will be completed.


    _getPaidEpoch(uint256 timeExpiry_, uint256 delegationEnd_, uint256 currentEpochStart_)

    Description: The function returns the start time of the last paid epoch

    Parameters:

    • timeExpiry_: The expiry timestamp to verify.

    • delegationEnd_: The epoch in which the delegation will be completed.

    • currentEpochStart_: The start time of the current epoch.


    _checkDelegationPaid(bool isSelfDelegation_, uint256 lastPaidEpoch_, uint256 paidEpoch_)

    Description: The function checks whether the delegation has been paid in the current epoch. Returned with DelegationUnpaid if the delegation is not paid in the current epoch.

    Parameters:

    • isSelfDelegation_: Indicates whether the delegation is self-delegated.

    • lastPaidEpoch_: An epoch when the last fee payment was made.

    • paidEpoch_: An epoch that must be paid for.

    Checks

    • Unless it is self-delegation, the last epoch paid should not be less than the epoch to be paid. Otherwise, DelegationUnpaid() is thrown.


    _checkRewardsClaimed(uint256 tokenId_, uint256 currentEpochStart_, address lockHolder_)

    Description: The function checks to see if available awards have been received. Returned with RewardsUnclaimed if no awards have been received.

    Parameters:

    • tokenId_: Indicates whether the delegation is self-delegated.

    • currentEpochStart_: The current epoch start.

    • lockHolder_: The lock holder contract address.

    Checks

    • Unless it is self-delegation, the last epoch paid should not be less than the epoch to be paid. Otherwise, DelegationUnpaid() is thrown.


    _checkCaller(address caller_)

    Description: Internal function to check the caller.

    Parameters:

    • caller_: The expected caller's address.

    Checks:

    • msg.sender must be equal to caller_. Otherwise, UnauthorizedCaller() is thrown.


    _checkDelegatorOrDelegatee(address delegator_, address delegatee_)

    Description: Internal function to verify that the function caller is a delegate or delegator.

    Parameters:

    • delegator_: The delegator's address.

    • delegatee_: The delegate's address.

    Checks:

    • msg.sender must be equal to delegator_ or delegatee_. Otherwise, UnauthorizedCaller() is thrown.


    _checkTimeExpiry(uint256 timeExpiry_)

    Description: Internal function to check that the delegation time has not expired. If the delegation has been revoked and timeExpiry is not equal to 0, the end time of the delegation must be less than the current time

    Parameters:

    • timeExpiry_: The end time of delegation.

    Checks:

    • block.timestamp must be not equal 0 and greater than timeExpiry_. Otherwise, DelegateTimeExpired() is thrown


    _checkLockHolder(address delegator_, address delegatee_)

    Description: Internal Function to check the LockHolder contract address for a pair of delegator and delegate. Returns the LockHolder contract's address.

    Parameters:

    • delegator_: The delegator's address.

    • delegatee_: The delegate's address.

    Checks:

    • a LockHolder contract must be deployed for the delegator_ and delegatee_ pair. Otherwise, LockHolderNotExist() is thrown


    _checkAssuranceLock(uint256 assuranceLock_, address delegatee_)

    Description: Internal function to check the assurance lock. Verification that the lock is a self-made lock, or its assurance lock meets the specified requirements

    Parameters:

    • assuranceLock_: The token ID used as the assurance lock.

    • delegatee_: The delegatee assurance lock.

    Checks:

    • If the conditions are not met for assuarnce lock. Otherwise, BadAssuranceLock() is thrown


    _checkDelegation(DelegationInfo memory delegationInfo_, address caller_, uint256 currentEpochStart_)

    Description: Internal function for check the delegation status. Check caller, time expiry, delegation fee payment. A number of internal functions are called for the checks, which will be described below

    Parameters:

    • delegationInfo_: The delegation information associated with the token.

    • caller_: The expected caller's address.

    • currentEpochStart_: The current epoch start.

    Checks:

    • The start time of the current epoch must be less than the end time of the delegation. Otherwise, DelegationEnded() is thrown.


    _checkForSelfDelegation(uint256 tokenId_)

    Description: Internal function for checking self-delegation. Returns the LockHolder contract's address.

    Parameters:

    • tokenId_: The ID of the token representing the lock.

    Checks:

    • When self-delegating for tokenId_, the delegator must be equal to the delegate and both of these values must be equal to msg.sender. Otherwise, UnauthorizedCaller() is thrown


    _currentEpochStart()

    Description: The function returns the start timestamp of the current epoch


    _nextEpochStart()

    Description: The function returns the start timestamp of the next epoch


    _getTimeToUnlock(uint256 tokenId_)

    Description: The function returns the time until the lock is unlocked

    Parameters:

    • tokenId_: The ID of the token representing the lock.


    _checkDelegateeExist(address delegatee_)

    Description: Internal function to check if the delegate has set the delegation parameters

    Parameters:

    • delegatee_: The delegate's address.

    Checks:

    • delegatee_ must have delegation parameters set. Otherwise, DelegateeNotExist() is thrown.


    Events

    • Delegate(address indexed delegator, address indexed delegatee, uint256[] tokenIds) The event is emitted when the delegation is successful.

    • RecallDelegations(uint256[] tokenIds) The event is emitted when the recall delegation is successful.


    Errors

    • WrongDelegationParameters() Thrown if the delegate tries to set the wrong delegation parameters.

    • BadAssuranceLock() Thrown if the assurance lock is not compliant.

    • DelegateeNotExist() Thrown if the delegate has not set the delegation parameters.


    Summary

    DelegationManagerV1 contract allows the castle owner not to track voting and reward collection, and not to spend ether on transaction fees. The delegate, on the other hand, can earn by taking a percentage of the rewards, sharing them with the castle owner. Alternatively, the castle owner can delegate to himself, which would simply automate the execution of voting and collection of rebase rewards and incentive rewards.

    EmissionManagerV1

    EmissionManagerV1

    Overview

    EmissionManagerV1 is an upgradeable contract that orchestrates weekly EYWA token emissions to various distribution streams, such as rebase rewards, gauge rewards, bonds, grants, and incentives. It integrates with the Escrow Manager to evaluate locked token balances and applies distinct emission formulas depending on whether the total locked tokens exceed a specified threshold. The contract also enforces constraints on weekly emissions, such as cooldown periods, maximum percentage increases, and valid distribution percentages.

  • IEscrowManagerExtended: Holds locked token data and checks voting power and freeze logic.

  • IEscrowVoteManagerV1: Receives gauge emission amounts and coordinates gauge reward distribution.

  • IEmissionManagerV1: Informs about epoch starts and ensures updated weekly distributions.

  • ILockHolderV1: Logic for proxied calls to EscrowManager, EscrowVoteManager and IncentiveRewardsDistributor contract functions

  • ICollection: Components in the ecosystem for NFT.

  • s_escrowManager (IEscrowManagerExtended) IEscrowManager interface for the EscrowManager contract.

  • s_escrowVoteManager (IEscrowVoteManagerV1) IEscrowVoteManagerV1 interface for the EscrowVoteManager contract.

  • s_lockHolderFactory (ILockHolderFactoryV1) ILockHolderFactoryV1 interface for the LockHolderFactoryV1 contract.

  • s_rebaseRewardsDistributor (IRebaseRewardsDistributorV2) IRebaseRewardsDistributorV2 interface for the RebaseRewardsDistributorV2 contract.

  • s_delegationConditionValidator (IDelegationConditionValidatorV1) IDelegationConditionValidatorV1 interface for the DelegationConditionValidator contract.

  • s_minLockEywa (uint256) The min EYWA amount in delegatee's lock.

  • s_minLockVeEywa (uint256) The min veEYWA amount in delegatee's lock.

  • s_minLockDuration (uint256) The min duration for delegatee's lock in seconds.

  • s_delegaties (address[]) Array with addresses of all delegates.

  • s_depositedByDelegatee (mapping(address => uint256)) Mapping from delegatee to its EYWA deposit information.

  • s_feeByDelegator (mapping(address => uint256)) Mapping from delegator to its EYWA fee information.

  • s_autoVoteByDelegatee (mapping(address => bool)) Mapping from delegatee to its permission for automatic execution of the autoVote() function.

  • s_delegationParametersByDelegatee (mapping(address => DelegationParameters)) Mapping from delegatee to its delegation parameters.

  • s_delegationInfoByTokenId (mapping(address => DelegationInfo)) Mapping from token ID to its delegation information.

  • s_lastClaimedIncentiveTimeByLockHolder (mapping(address => uint256)) Mapping from LockHolder contract address to its last time claimed incentive.

  • s_lastClaimedCommissionTimeByLockHolder (mapping(address => uint256)) Mapping from LockHolder contract address to its last time claimed commission.

  • s_lockHolders (mapping(address => mapping(address => address))) Nested mapping from delegator and delegatee to their LockHolder contract address.

  • _s_locksByDelegator (mapping(address => uint256)) Mapping from delegator to its amount of delegated locks.

  • _s_locksByDelegatee (mapping(address => uint256)) Mapping from delegatee to its amount of delegated locks.

  • _s_delegationVoteParametersByDelegatee (mapping(address => DelegationVoteParameters)) Mapping from delegatee to its delegation vote parameters.

  • _s_delegationIndexesInfoByTokenId (mapping(address => DelegationIndexesInfo)) Mapping from token ID to its delegation indexes info.

  • _s_lockIdByDelegatorAndIndex (address => mapping(uint256 => uint256)) Nested mapping from delegator and index to its delegated lock ID.

  • _s_lockIdByDelegateeAndIndex (address => mapping(uint256 => uint256)) Nested mapping from delegatee and index to its delegated lock ID.

  • _s_feeByDelegateeAndEpoch (mapping(address => mapping(uint256 => uint256))) Nested mapping of delegatee address and an epoch start to a fee.

  • References the EYWA token, EYWA NFT, escrow manager, escrow vote manager, emission manager, lockHolder factory, rebase rewards distributor, delegation condition validator.
    emissionManager_: IEmissionManagerV1 interface for the EmissionManagerV1 contract.
  • escrowManager_: The IEscrowManagerExtended interface for the EscrowManager contract.

  • escrowVoteManager_: The IEscrowVoteManagerV1 interface for the EscrowVoteManager contract.

  • lockHolderFactory_: ILockHolderFactoryV1 interface for the LockHolderFactoryV1 contract.

  • rebaseRewardsDistributor_: IRebaseRewardsDistributorV2 interface for the RebaseRewardsDistributorV2 contract.

  • delegationConditionValidator_: IDelegationConditionValidatorV1 interface for the DelegationConditionValidator contract.

  • Must be paid rent for all delegated tokens. Otherwise, DelegationUnpaid() is thrown.

  • The value of delegationParameters_.maxDelegatedVeEYWA must not be less than the current total veEYWA value for all locks delegated to this delegate. Otherwise, MaxDelegatedVeEywaExceeded() is thrown.

  • For each lock from tokenIds_, it is checked that its delegation time has not yet expired. Otherwise, DelegateTimeExpired() is thrown.

  • The maximum veEYWA value for dlegation should not be exceeded after extend of the locks. Otherwise, MaxDelegatedVeEywaExceeded() is thrown.

  • For each lock from tokenIds_, it is checked that there exists a LockHolder contract corresponding to the delegate-delegate pair. Otherwise, LockHolderNotExist() is thrown.

  • is thrown.
  • If it is not a self-delegation, additional delegation conditions are checked in the validateDelegations function on the DelegationConditionValidator contract. Otherwise, UnvalidatedDelegation() is thrown.

  • Must be paid rent for all delegated tokens. Otherwise, DelegationUnpaid() is thrown.

  • The delegation's time must be finalized. Otherwise, DelegationWithdrawalForbidden() is thrown.

  • delegatee_
    must be a delegatee. Otherwise,
    WrongDelegatee()
    is thrown.
    delegatee_: The delegate's address.

    InvalidArrayLengths() Thrown if the array has an invalid length.

  • UnauthorizedCaller() Thrown when the caller is not authorized to perform the action.

  • DelegateTimeExpired() Thrown when attempting to perform an action with a delegation with expired time.

  • DelegationEnded() Thrown when the delegation is complete.

  • WrongLockDuration() Thrown when attempting to set a new lock auto-extend time lower than the minimum allowed by the delegation parameters.

  • LockHolderNotExist() Thrown if there is no LockHolder for the current delegate and delegate pair.

  • NotEnoughAmount() Thrown when there is an insufficient deposit or fee for the requested operation.

  • DelegationNotEnded() Thrown when the delegation is not ended.

  • MaxDelegatedVeEywaExceeded() Thrown when the maximum number of delegated veEYWA has been exceeded.

  • UnvalidatedDelegation() Thrown when validation failed on the DelegationConditionValidator contract.

  • WrongVeEywaAmount() Thrown when the voting power of the delegated lock is less than s_minLockVeEywa.

  • LittleTimeToUnlock() Thrown when attempting to delegate a lock that has less time to unlock than the minimum time set by the delegate.

  • NotSetAutoVote() Thrown when attempting to perform auto-voting if no permission is set.

  • NotSetAutoVoteParameters() Thrown when attempting to perform auto-voting if no parameters are set for it.

  • WrongDelegatee() Thrown when attempting to perform an auto-vote if the lock is not delegated to the specified delegate.

  • DelegationWithdrawalForbidden() Thrown when the delegation time has not yet expired, preventing recall.

  • RewardsUnclaimed() Thrown when the delegation time has not yet expired, preventing recall.

  • DelegationUnpaid() Thrown when no awards have been received.

  • Key Features:

    • Epoch-Based Emissions: Emissions occur in discrete one-week epochs, with a configurable weekly emission amount.

    • Distribution Percentages: Tokens are split among rebase rewards, bonds, grants, and incentives, each receiving a percentage of the weekly emission.

    • Threshold-Based Adjustments: If total locked tokens exceed a s_lockThreshold, alternate base rates and multipliers are applied to rebase calculations.

    • Inflation Rates: Two inflation rates (s_initialInflationRate and s_secondaryInflationRate) further influence the rebase portion of weekly emissions.

    • Owner-Only Updates: Certain parameters, such as weekly emission amounts and distribution logic, can only be updated by the contract owner, subject to cooldowns and other constraints.

    By implementing the IEmissionManagerV1 interface, EmissionManagerV1 provides a clear set of functionalities and events essential for managing the emission process in the CrossCurve ecosystem.


    Inherited Contracts and Interfaces

    • UUPSUpgradeable (OpenZeppelin): Enables the contract to be upgradeable via a UUPS proxy pattern, ensuring only the owner can perform upgrades.

    • OwnableUpgradeable (OpenZeppelin): Provides ownership functionality, restricting sensitive actions (like parameter updates) to the owner.

    • IEmissionManagerV1: Interface declaring all essential methods (e.g., initialize, updateEpoch) and events for managing emissions.

    Additional External References:

    • SafeERC20, IERC20 (OpenZeppelin): Used for secure ERC20 operations.

    • IEscrowVoteManagerV1: Receives gauge emission amounts and coordinates gauge reward distribution.

    • IEscrowManager: Provides total locked token data for rebase emission calculations.

    • ITreasury: Manages treasury funds from which weekly emissions are withdrawn.

    • IRebaseRewardsDistributorV1: Distributes rebase rewards and triggers relevant accounting updates.


    Constants

    • EPOCH_DURATION: Duration of each emission epoch (1 week).

    • TOTAL_SUPPLY: Total EYWA token supply (1 billion).

    • MAXIMUM_EMISSION_INCREASE_PERCENTAGE: Maximum allowed percentage by which the weekly emission can increase, scaled by PRECISION.

    • PRECISION: Scaling factor for percentage calculations (e.g., 100,000 means 100%).

    • EPOCH_COOLDOWN_PERIOD: Number of epochs required between emission state changes.


    State Variables

    Emission Parameters

    • s_currentWeeklyEmission (uint256): Current weekly emission amount in EYWA tokens.

    • s_totalDistributedEmission (uint256): Accumulated amount of EYWA tokens distributed across all epochs.

    Distribution Percentages

    • s_gaugeEmissionPercentage (uint256): Percent of weekly emission allocated to gauge rewards, scaled by PRECISION.

    • s_bondEmissionPercentage (uint256): Percent allocated to bond rewards, scaled by PRECISION.

    • s_grantEmissionPercentage (uint256): Percent allocated to grants, scaled by PRECISION.

    • s_incentiveEmissionPercentage (uint256): Percent allocated to incentives, scaled by PRECISION.

    Threshold and Rates

    • s_lockThreshold (uint256): The threshold of total locked tokens determining which emission formula to apply.

    • s_baseRateBelowThreshold (uint256): Base rate for rebase emission if locked tokens < s_lockThreshold.

    • s_baseRateAboveThreshold (uint256): Base rate if locked tokens ≥ s_lockThreshold.

    • s_rateMultiplierBelowThreshold (uint256): Rate multiplier if locked tokens < s_lockThreshold.

    • s_rateMultiplierAboveThreshold (uint256): Rate multiplier if locked tokens ≥ s_lockThreshold.

    Inflation Rates

    • s_initialInflationRate (uint256): Inflation rate applied when locked tokens are below threshold.

    • s_secondaryInflationRate (uint256): Inflation rate used when locked tokens are above threshold.

    Epoch Management

    • s_currentEpochStart (uint256): Timestamp marking the start of the current emission epoch.

    • s_lastChangeEpochStart (uint256): Timestamp marking the start of the epoch during the last emission state change (used for cooldown enforcement).

    • s_epochCounter (uint256): Number of completed epochs.

    Addresses

    • s_treasury (address): Address holding the EYWA tokens (treasury).

    • s_rebaseRewardsDistributor (address): Distributor for rebase rewards.

    • s_bondEmissionDistributor (address): Distributor for bond emissions.

    • s_grantEmissionDistributor (address): Distributor for grant emissions.

    • s_incentiveEmissionDistributor (address): Distributor for incentive emissions.

    • s_escrowVoteManager (IEscrowVoteManagerV1): Escrow vote manager contract for gauge emissions.

    • s_escrowManager (IEscrowManager): Escrow manager providing locked token data.

    • s_eywa (IERC20): EYWA token used for emission distributions.


    Constructor

    • Description: Disables contract initializers to prevent re-initialization in a UUPS proxy context.


    External Functions

    initialize(...)

    Description: Initializes the contract for the first time, setting the owner, treasury, emission distributors, and references to external managers/tokens. Configures initial weekly emission, distribution percentages, threshold-based rates, and inflation rates.

    Parameters:

    • owner_: Contract owner address.

    • treasury_: Address of treasury holding EYWA tokens.

    • rebaseRewardsDistributor_: Distributor contract for rebase rewards.

    • bondEmissionDistributor_: Distributor contract for bond rewards.

    • grantEmissionDistributor_: Distributor contract for grants.

    • incentiveEmissionDistributor_: Distributor contract for incentives.

    • escrowVoteManager_: The escrow vote manager contract instance.

    • escrowManager_: Escrow manager contract instance for locked token data.

    • eywa_: EYWA token contract.

    Effects:

    • Calls __UUPSUpgradeable_init() and __Ownable_init(owner_).

    • Sets initial values for emission rates, thresholds, and inflation parameters.

    • Marks the start of the current epoch.


    updateTreasury(address newTreasury_)

    Description: Updates the treasury address from which emissions are withdrawn.

    Events:

    • Emits TreasuryUpdated(newTreasury_).


    updateEmissionDistributors(...)

    Description: Updates the addresses for bond, grant, and incentive emission distributors. Only callable by the owner.

    Parameters:

    • newBondEmissionDistributor_: New bond emission distributor address.

    • newGrantEmissionDistributor_: New grant emission distributor address.

    • newIncentiveEmissionDistributor_: New incentive emission distributor address.

    Events:

    • Emits EmissionDistributorsUpdated.


    updateWeeklyEmissionState(...)

    Description: Adjusts the weekly emission amount and distribution percentages. Enforces a cooldown (EPOCH_COOLDOWN_PERIOD) since the last change and forbids increases above the allowed percentage limit.

    Checks & Constraints:

    • Must have at least 1 epoch (s_epochCounter > 0).

    • Enforces cooldown: block.timestamp must be ≥ s_lastChangeEpochStart + EPOCH_COOLDOWN_PERIOD * EPOCH_DURATION.

    • newWeeklyEmission_ must not exceed historical average times MAXIMUM_EMISSION_INCREASE_PERCENTAGE / PRECISION.

    • Sum of new distribution percentages = PRECISION (100%).

    Events:

    • Emits WeeklyEmissionStateUpdated.


    updateParameters(...)

    Description: Updates threshold-based emission parameters, including base rates, rate multipliers, and inflation rates used in rebase calculations.

    Events:

    • Emits ParametersUpdated with the updated values.


    updateEpoch()

    Description: If a new epoch has started (block.timestamp >= s_currentEpochStart + EPOCH_DURATION), the contract calculates distributions for the new week and transfers tokens to the various distributors.

    Logic:

    1. Withdraws s_currentWeeklyEmission from s_treasury.

    2. Calculates rebase emission portion with _calculateRebaseEmission.

    3. Sends rebase emission to the rebase rewards distributor and calls checkpoint().

    4. Distributes leftover among gauge, bond, grant, and incentive streams proportionally.

    5. Increments s_epochCounter and updates s_totalDistributedEmission.

    Events:

    • Emits EpochUpdated with detailed breakdown.


    averageWeeklyEmissionOverTime()

    Description: Returns the average weekly emission across all completed epochs: s_totalDistributedEmission / s_epochCounter.


    Internal and Private Functions

    _authorizeUpgrade(address)

    • Description: Restricts the UUPS upgrade function to the contract owner.


    _calculateRebaseEmission(uint256 currentWeeklyEmission_)

    Description: Derives the rebase portion of the weekly emission based on total locked tokens from s_escrowManager.

    • If m_totalLocked >= s_lockThreshold, uses one set of base rates, multipliers, and s_secondaryInflationRate.

    • Otherwise, uses another set and s_initialInflationRate.

    Return:

    • rebaseEmission_: The final rebase token amount.

    Formula Components:

    1. Computes an adjustment factor that depends on s_baseRateAboveThreshold/s_rateMultiplierAboveThreshold or s_baseRateBelowThreshold/s_rateMultiplierBelowThreshold.

    2. Adds an inflation portion scaled by either s_secondaryInflationRate or s_initialInflationRate.

    3. Divides by 1e18 at the end to restore integer arithmetic precision.


    Events

    • TreasuryUpdated(address newTreasury)

    • EmissionDistributorsUpdated(address newBondEmissionDistributor, address newGrantEmissionDistributor, address newIncentiveEmissionDistributor)

    • WeeklyEmissionStateUpdated(uint256 newWeeklyEmission, uint256 newGaugeEmissionPercentage, ..., uint256 newIncentiveEmissionPercentage)

    • ParametersUpdated(..., uint256 initialInflationRate, uint256 secondaryInflationRate)

    • EpochUpdated(uint256 epochStartTimestamp, uint256 totalEmission, uint256 rebaseEmission, ..., uint256 incentiveEmission)

    These events log critical changes such as treasury updates, changes to emission distribution, parameter updates, and epoch transitions.


    Errors

    • InvalidCallee(): Thrown if an unauthorized entity calls certain functions (not used in current logic).

    • ZeroEpochCounter(): Thrown if updateWeeklyEmissionState is called before any epoch has elapsed.

    • CooldownPeriodNotElapsed(): Thrown if a new weekly emission state update is attempted within the cooldown period.

    • ExcessiveEmissionIncrease(): Thrown if the new weekly emission exceeds the allowed maximum over historical average.

    • InvalidPercentagesSum(): Thrown if the sum of new distribution percentages does not equal PRECISION (100%).


    Summary

    EmissionManagerV1 is a flexible and upgradeable contract for distributing EYWA tokens weekly to different reward mechanisms—governed by configurable rates and distribution percentages. By leveraging a threshold-based logic, it dynamically adjusts rebase emissions based on the total locked tokens, ensuring that ecosystem participation and inflation rates are balanced. It integrates seamlessly with treasury, escrow, and reward distributor contracts to enforce cooldowns, respect maximum emission growth, and emit relevant events detailing emission updates.

    uint256 private constant EPOCH_DURATION = 1 weeks;
    uint256 private constant PRECISION = 100_000;
    constructor() {
        _disableInitializers();
    }
    function initialize(
        address owner_,
        IERC20 eywa_,
        ICollection collection_,
        IEmissionManagerV1 emissionManager_,
        IEscrowManagerExtended escrowManager_,
        IEscrowVoteManagerV1 escrowVoteManager_,
        ILockHolderFactoryV1 lockHolderFactory_,
        IRebaseRewardsDistributorV2 rebaseRewardsDistributor_,
        IDelegationConditionValidatorV1 delegationConditionValidator_
    ) external initializer;
    function setAssuranceLockParameters(
        uint256 minLockEywa_,
        uint256 minLockDuration_
    ) external onlyOwner;
    -   `minLockEywa_`: The minimum number of EYWA tokens that must be in the assurance lock.
    -   `minLockDuration_`: The the minimum time before unlocking that a assurance lock should have.
    function setMinLockVeEywa(uint256 minLockVeEywa_) external onlyOwner;
    function setDelegationParameters(InputDelegationParameters memory delegationParameters_) external;
    function setDelegationVoteParameters(
        address[] calldata pools_,
        uint256[] calldata weights_
    ) external;
    function setAutoVotePermission(bool permission_) external;
    function boost(
        uint256 tokenId_,
        uint256[] calldata collectionTokenIds_
    ) external;
    function deboost(
        uint256 tokenId_,
        uint256[] calldata collectionTokenIds_
    ) external;
    function extend(
        uint256[] calldata tokenIds_,
        uint256[] calldata lockDurations_
    ) external;
    function withdrawDeposit(uint256 amount_) external;
    function claim() external;
    function delegate(
        address delegatee_,
        uint256[] calldata tokenIds_
    ) external;
    function paymentAndExtendDelegations(uint256[] calldata tokenIds_) external;
    function claimIncentives(uint256[] calldata tokenIds_) external;
    function revokeDelegations(uint256[] calldata tokenIds_) external;
    function recallDelegations(uint256[] calldata tokenIds_) external;
    function vote(
        uint256[] calldata tokenIds_,
        address[][] calldata pools_,
        uint256[][] calldata weights_
    ) external;
    function autoVote(
        address delegatee_,
        uint256[] calldata tokenIds_
    ) external;
    function reset(uint256[] calldata tokenIds_) external;
    function poke(uint256[] calldata tokenIds_) external;
    function isAvailableDelegate(
        address delegator_,
        address delegatee_,
        uint256[] calldata tokenIds_
    ) external view returns(bool);
    function getDelegationsParameters() external view returns(OutputDelegationParameters[] memory);
    function getDelegationsVoteParametersByDelegate(
        address delegatee_
    ) external view returns(address[] memory pools_, uint256[] memory weights_);
    function getDelegationsInfoByTokenIds(
        uint256[] calldata tokenIds_
    ) external view returns(DelegationInfo[] memory);
    function getDelegationInfoAndParametersByTokenId(
        uint256 tokenId_
    ) 
        external
        view 
        returns(DelegationInfo memory delegationInfo_, DelegationParameters memory delegationParameters_);
    function getLockIdsByDelegator(address delegator_) external view returns(uint256[] memory);
    function getLockIdsByDelegatee(address delegatee_) external view returns(uint256[] memory);
    function getAssuranceLockStatus(
        uint256 tokenId_,
        address delegatee_
    ) public view returns(bool);
    function _delegate(
        IEscrowManagerExtended escrowManager_,
        DelegationParameters memory delegationParameters_,
        uint256[] calldata tokenIds_,
        address delegatee_
    ) private;
    function _recallDelegation(
        address delegator_,
        address delegatee_,
        uint256 tokenId_
    ) internal;
    function _getVotes(
        IEscrowManagerExtended escrowManager_,
        uint256[] calldata tokenIds_
    ) private view returns(uint256);
    function _getLastEpochOfDelegation(
        uint256 timeExpiry_,
        uint256 delegationEnd_
    ) internal view returns (uint256);
    function _getLastEpochOfDelegation(
        uint256 timeExpiry_,
        uint256 delegationEnd_,
        uint256 currentEpochStart_
    ) internal view returns (uint256);
    function _checkDelegationPaid(
        bool isSelfDelegation_,
        uint256 lastPaidEpoch_,
        uint256 paidEpoch_
    ) internal view returns (uint256);
    function _checkRewardsClaimed(
        uint256 tokenId_,
        uint256 currentEpochStart_,
        address lockHolder_
    ) internal view returns (uint256);
    _checkCaller(address caller_) internal view;
    function _checkDelegatorOrDelegatee(
        address delegator_,
        address delegatee_
    ) internal view;
    function _checkTimeExpiry(uint256 timeExpiry_) internal view
    function _checkLockHolder(
        address delegator_,
        address delegatee_
    ) internal view returns(address);
    function _checkAssuranceLock(
        uint256 assuranceLock_,
        address delegatee_
    ) internal view returns(address);
    function _checkDelegation(
        DelegationInfo memory delegationInfo_,
        address caller_,
        uint256 currentEpochStart_
    ) internal view returns(address);
    function _checkForSelfDelegation(uint256 tokenId_) internal view returns(address);
    function _currentEpochStart() internal view returns (uint256);
    function _nextEpochStart() internal view returns (uint256);
    function _getTimeToUnlock(uint256 tokenId_) internal view returns(uint256);
    function _checkDelegateeExist(address delegatee_) internal view;
    uint256 public constant EPOCH_DURATION = 1 weeks;
    uint256 public constant TOTAL_SUPPLY = _000_000_000e18;
    uint256 public constant MAXIMUM_EMISSION_INCREASE_PERCENTAGE = 25_000;
    uint256 public constant PRECISION = 00_000;
    uint256 public constant EPOCH_COOLDOWN_PERIOD = 12;
    constructor() {
        _disableInitializers();
    }
    function initialize(
        address owner_,
        address treasury_,
        address rebaseRewardsDistributor_,
        address bondEmissionDistributor_,
        address grantEmissionDistributor_,
        address incentiveEmissionDistributor_,
        IEscrowVoteManagerV1 escrowVoteManager_,
        IEscrowManager escrowManager_,
        IERC20 eywa_
    ) external initializer
    function updateTreasury(address newTreasury_) external onlyOwner
    function updateEmissionDistributors(
        address newBondEmissionDistributor_,
        address newGrantEmissionDistributor_,
        address newIncentiveEmissionDistributor_
    ) external onlyOwner
    function updateWeeklyEmissionState(
        uint256 newWeeklyEmission_,
        uint256 newGaugeEmissionPercentage_,
        uint256 newBondEmissionPercentage_,
        uint256 newGrantEmissionPercentage_,
        uint256 newIncentiveEmissionPercentage_
    ) external onlyOwner
    function updateParameters(
        uint256 lockThreshold_,
        uint256 baseRateBelowThreshold_,
        uint256 rateMultiplierBelowThreshold_,
        uint256 baseRateAboveThreshold_,
        uint256 rateMultiplierAboveThreshold_,
        uint256 initialInflationRate_,
        uint256 secondaryInflationRate_
    ) external onlyOwner
    function updateEpoch() external
    function averageWeeklyEmissionOverTime() external view returns (uint256)
    function _authorizeUpgrade(address) internal override onlyOwner
    function _calculateRebaseEmission(uint256 currentWeeklyEmission_) private view returns (uint256)

    Technical Documentation for CrossCurve API

    Make cross-chain swap

    Performing a cross-chain swap consists of 4 steps:

    1. Routing construction

    2. Estimating cross-chain operations

    3. Forming data for the transaction

    4. Sending the transaction

    1. Routing construction

    Request routing for the specified tokens and networks

    Copy

    2. Making a route estimate

    From the obtained array of routes, take the first route (which is the most profitable for swapping) and send it for estimation

    Copy

    3. Forming data for sending the transaction

    Copy

    4. Sending the transaction

    Copy

    Tracking cross-chain swaps

    After initiating the cross-chain swap (sending the transaction), it is necessary to ensure that it reaches the destination network and is executed successfully. Each cross-chain swap involves several transactions and the number of transactions may vary depending on the operation.

    For swapping two stablecoins from Network A to Network B, the route can be depicted as:

    Chain A -----> Hubchain -----> Chain B

    Each transaction is linked with another by a unique identifier, requestId, which represents the cross-chain transition identifier and can be used to track the source status and transaction destinations.

    Searching for requestId

    You can get requestId of a transaction using its hash with the Pusher API:

    Copy

    Getting details about the cross-chain transition by its requestId

    Copy

    In the results, we are interested in the values of destination.status and destination.transactionHash. If destination.transactionHash is empty, this means that the transaction has not yet been executed and the request should be repeated at intervals until the hash appears.

    Then there are 3 scenarios:

    1. Both details.inconsistency and destination.emergency are false This scenario is considered the most correct and will occur in most cases. It means the cross-chain operation has been completed and we have the hash of the destination transaction. Now, using this hash, similarly, you can track the status of the next cross-chain transition (if there is one) and continue doing so until all cross-chain transitions are completed.

    2. details.inconsistency is true This means that the destination transaction was successfully completed, but the token swap did not occur due to slippage. An intermediate result has been returned to your address in this network. The cross-chain operation is considered complete at this point.

    3. The value of destination.emergency is true In this case, the destination network operation could not be executed and your tokens are stuck in the contract. To retrieve them, you need to contact support to request a refund.

    Pusher API Reference

    API Specification

    POST https://api.crosscurve.fi/api-docs/#/

    Routing API

    Get possible cross-chain routes for token exchange

    POST https://api.crosscurve.fi/routing/scan

    Request Body

    Name
    Type
    Description

    Get an estimate for cross-chain exchange

    POST https://api.crosscurve.fi/estimate

    Request Body

    Name
    Type
    Description

    Formulate data for the transaction

    POST https://api.crosscurve.fi/tx/create

    Request Body

    Name
    Type
    Description

    Explorer API

    API link: https://api.crosscurve.fi

    Explorer: https://explorer.eywa.fi/

    Get information about the transaction by its hash or requestId

    GET https://api.crosscurve.fi/search?search={hashOrReqId}&limit={limit}

    Query Parameters

    Name
    Type
    Description

    Get details of the cross-chain transfer by requestId

    GET https://api.crosscurve.fi/transaction/{reqId}

    Path Parameters

    Name
    Type
    Description

    Glossary

    Decryption of router operation codes:

    A - add liquidity

    R - remove liquidity

    S - swap

    LM - lock mint (lock the original token and mint its synthetic token in another network)

    BU - burn unlock (burn the synthetic token and unlock the original in another network)

    BM - burn mint (burn the synthetic token and mint the synthetic token in another network)

    params.tokenIn*

    String

    address of the token that the user is selling on the chainIdIn network

    params.tokemOut*

    String

    address of the token that the user is buying on the chainIdOut network

    params.amountIn*

    String

    query.params.tokenIn*

    String

    query.params.tokenOut*

    String

    query.params.amountIn*

    String

    query.slippage*

    Number

    amountIn*

    String

    amountInWithoutSlippage*

    String

    amountOut*

    String

    amountOutWithoutSlippage*

    String

    route*

    Route

    Uw - unwrap to native token

    W - wrap native token

    M - emergency mint

    U - emergency unlock

    params.chainIdIn*

    Number

    chainId of the sending network

    params.chainIdOut*

    Number

    chainId of the receiving network

    params*

    Object

    slippage*

    Number

    query*

    Object

    query.params*

    Object

    query.params.chainIdIn*

    Number

    query.params.chainIdOut*

    Number

    from*

    String

    recipient*

    String

    routing*

    Routing

    estimate*

    Estimate

    search*

    String

    hash or requestId of the transaction

    limit*

    String

    reqId*

    String

    const requestRoutingParams = {
        params: {
             chainIdIn": 1, // Ethereum
             chainIdOut: 250, // Fantom
             tokenIn: "0xdac17f958d2ee523a2206206994597c13d831ec7", // USDT
             tokenOut: "0xe71286fc887189c562410af12ed521c8e58e5fa3", // s3crypto_e
             amountIn: "100000000", // 100 USDT
        },
        slippage: 1, // 1%
    }
    
    const response = await fetch('https://api.crosscurve.fi/routing/scan', {
        method: 'POST',
        body: JSON.stringify(requestRoutingParams),
        headers: {
            "Content-Type": "application/json",
        },
    })
    
    const routing = await response.json()
    const bestRoute = routing[0]
    
    const response = await fetch('https://api.crosscurve.fi/estimate', {
        method: 'POST',
        body: JSON.stringify(bestRoute),
        headers: {
            "Content-Type": "application/json",
        },
    })
    
    const estimate = await response.json()
    const txCreateParams = {
        from: '0x...', // sender
        recipient: '0x...', // recipient
        routing,
        estimate,
    }
    
    const response = await fetch('https://api.crosscurve.fi/tx/create', {
        method: 'POST',
        body: JSON.stringify(txCreateParams),
        headers: {
            "Content-Type": "application/json",
        },
    })
    
    const rawTx = await response.json()
    import { Contract, JsonRpcProvider } from 'ethers'
     
    const provider = new JsonRpcProvider('RPC_URL_HERE')
    const signer = new Wallet(process.env.PRIVATE_KEY, provider)
    
    const router = new Contract(rawTx.to, [rawTx.abi], signer)
    
    const args = [
      rawTx.args[0],
      rawTx.args[1],
      [
        rawTx.args[2].executionPrice,
        rawTx.args[2].deadline,
        rawTx.args[2].v,
        rawTx.args[2].r,
        rawTx.args[2].s,
      ],
    ]
    
    const value = BigInt(rawTx.value) + BigInt(estimate.executionPrice)
    
    const tx = await router.start(...args, { value })
    const receipt = await tx.wait()
    const searchParams = new URLSearchParams({
    search: '0x...', // transaction hash
    limit: 1,
    }).toString();
    const response = await fetch(`https://api.crosscurve.fi/?${searchParams}`, {
    method: 'GET',
    })
    const result = await response.json()
    const details = result.result[0]
    const requestId = details.requestId
    TypeScript
    const response = await fetch(`https://api.crosscurve.fi/search?search=%7BhashOrReqId%7D&limit=%7Blimit%7D`, {
    method: 'GET',
    })
    const details = await response.json()
    const destination = details.destination
    TypeScript
    [
      {
        "query": {
          "params": {
            "tokenIn": "0xdac17f958d2ee523a2206206994597c13d831ec7",
            "chainIdIn": 1,
            "tokenOut": "0xe71286fC887189C562410af12eD521C8e58e5fA3",
            "chainIdOut": 250,
            "amountIn": "100000000"
          },
          "slippage": 0.5
        },
        "route": [
          {
            "type": "addLiquidity",
            "chainId": 1,
            "params": {
              "tokenIn": {
                "logos": {
                  "16": "https://s2.coinmarketcap.com/static/img/coins/16x16/825.png",
                  "32": "https://s2.coinmarketcap.com/static/img/coins/32x32/825.png",
                  "64": "https://s2.coinmarketcap.com/static/img/coins/64x64/825.png",
                  "128": "https://s2.coinmarketcap.com/static/img/coins/128x128/825.png",
                  "200": "https://s2.coinmarketcap.com/static/img/coins/200x200/825.png"
                },
                "chainId": 1,
                "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
                "name": "Tether USD",
                "symbol": "USDT",
                "decimals": 6,
                "originalName": "Tether USD",
                "originalSymbol": "USDT",
                "tags": ["erc20", "stable"],
                "permittable": false,
                "permit": false
              },
              "chainIdIn": 1,
              "tokenOut": {
                "chainId": 1,
                "address": "0xc4ad29ba4b3c580e6d59105fff484999997675ff",
                "name": "Curve.fi USD-BTC-ETH",
                "symbol": "crv3crypto",
                "decimals": 18,
                "originalName": "Curve.fi USD-BTC-ETH",
                "originalSymbol": "crv3crypto",
                "tags": ["erc20", "curve_lp"],
                "permittable": false,
                "permit": false,
                "coins": [
                  "0xdAC17F958D2ee523a2206206994597C13D831ec7",
                  "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
                  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
                ]
              },
              "chainIdOut": 1,
              "amountIn": "100000000",
              "amountInWithoutSlippage": "100000000",
              "amountOut": "54972421143512185",
              "amountOutWithoutSlippage": "55248664465841392",
              "slippage": 0.5
            },
            "pool": {
              "address": "0xd51a44d3fae010294c616388b506acda1bfaae46",
              "coins": [
                "0xdAC17F958D2ee523a2206206994597C13D831ec7",
                "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
                "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
              ],
              "decimals": [6, 8, 18],
              "logos": {
                "16": "https://s2.coinmarketcap.com/static/img/exchanges/16x16/1063.png",
                "32": "https://s2.coinmarketcap.com/static/img/exchanges/32x32/1063.png",
                "64": "https://s2.coinmarketcap.com/static/img/exchanges/64x64/1063.png",
                "128": "https://s2.coinmarketcap.com/static/img/exchanges/128x128/1063.png",
                "200": "https://s2.coinmarketcap.com/static/img/exchanges/200x200/1063.png"
              },
              "lp": {
                "chainId": 1,
                "address": "0xc4ad29ba4b3c580e6d59105fff484999997675ff",
                "name": "Curve.fi USD-BTC-ETH",
                "symbol": "crv3crypto",
                "decimals": 18,
                "originalName": "Curve.fi USD-BTC-ETH",
                "originalSymbol": "crv3crypto",
                "tags": ["erc20", "curve_lp"],
                "permittable": false,
                "permit": false,
                "coins": [
                  "0xdAC17F958D2ee523a2206206994597C13D831ec7",
                  "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
                  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
                ]
              }
            },
            "fees": [
              {
                "type": "eywaStableSwapFee",
                "token": {
                  "logos": {
                    "16": "https://s2.coinmarketcap.com/static/img/coins/16x16/825.png",
                    "32": "https://s2.coinmarketcap.com/static/img/coins/32x32/825.png",
                    "64": "https://s2.coinmarketcap.com/static/img/coins/64x64/825.png",
                    "128": "https://s2.coinmarketcap.com/static/img/coins/128x128/825.png",
                    "200": "https://s2.coinmarketcap.com/static/img/coins/200x200/825.png"
                  },
                  "chainId": 1,
                  "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
                  "name": "Tether USD",
                  "symbol": "USDT",
                  "decimals": 6,
                  "originalName": "Tether USD",
                  "originalSymbol": "USDT",
                  "tags": ["erc20", "stable"],
                  "permittable": false,
                  "permit": false
                },
                "percent": "0.0654473",
                "amount": "65447"
              }
            ]
          },
          {
            "type": "bridgeIn",
            "chainId": 1,
            "params": {
              "tokenIn": {
                "chainId": 1,
                "address": "0xc4ad29ba4b3c580e6d59105fff484999997675ff",
                "name": "Curve.fi USD-BTC-ETH",
                "symbol": "crv3crypto",
                "decimals": 18,
                "originalName": "Curve.fi USD-BTC-ETH",
                "originalSymbol": "crv3crypto",
                "tags": ["erc20", "curve_lp"],
                "permittable": false,
                "permit": false,
                "coins": [
                  "0xdAC17F958D2ee523a2206206994597C13D831ec7",
                  "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
                  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
                ]
              },
              "chainIdIn": 1,
              "tokenOut": {
                "chainId": 250,
                "address": "0xe71286fC887189C562410af12eD521C8e58e5fA3",
                "name": "s3crypto_e",
                "symbol": "s3crypto_e",
                "decimals": 18,
                "originalName": "s3crypto_e",
                "originalSymbol": "s3crypto_e",
                "tags": ["erc20", "synth"],
                "permittable": false,
                "permit": false,
                "real": {
                  "chainId": 1,
                  "address": "0xc4ad29ba4b3c580e6d59105fff484999997675ff",
                  "name": "Curve.fi USD-BTC-ETH",
                  "symbol": "crv3crypto",
                  "decimals": 18,
                  "originalName": "Curve.fi USD-BTC-ETH",
                  "originalSymbol": "crv3crypto",
                  "tags": ["erc20", "curve_lp"],
                  "permittable": false,
                  "permit": false,
                  "coins": [
                    "0xdAC17F958D2ee523a2206206994597C13D831ec7",
                    "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
                    "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
                  ]
                },
                "realToken": {
                  "chainId": 1,
                  "address": "0xc4ad29ba4b3c580e6d59105fff484999997675ff",
                  "name": "Curve.fi USD-BTC-ETH",
                  "symbol": "crv3crypto",
                  "decimals": 18,
                  "originalName": "Curve.fi USD-BTC-ETH",
                  "originalSymbol": "crv3crypto",
                  "tags": ["erc20", "curve_lp"],
                  "permittable": false,
                  "permit": false,
                  "coins": [
                    "0xdAC17F958D2ee523a2206206994597C13D831ec7",
                    "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
                    "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
                  ]
                }
              },
              "chainIdOut": 250,
              "amountIn": "54972421143512185",
              "amountInWithoutSlippage": "55248664465841392",
              "amountOut": "54972421143512185",
              "amountOutWithoutSlippage": "55248664465841392",
              "slippage": 0.5
            },
            "fees": [
              {
                "type": "bridgeFee",
                "amount": "0",
                "percent": "0",
                "token": {
                  "chainId": 250,
                  "address": "0xe71286fC887189C562410af12eD521C8e58e5fA3",
                  "name": "s3crypto_e",
                  "symbol": "s3crypto_e",
                  "decimals": 18,
                  "originalName": "s3crypto_e",
                  "originalSymbol": "s3crypto_e",
                  "tags": ["erc20", "synth"],
                  "permittable": false,
                  "permit": false,
                  "real": {
                    "chainId": 1,
                    "address": "0xc4ad29ba4b3c580e6d59105fff484999997675ff",
                    "name": "Curve.fi USD-BTC-ETH",
                    "symbol": "crv3crypto",
                    "decimals": 18,
                    "originalName": "Curve.fi USD-BTC-ETH",
                    "originalSymbol": "crv3crypto",
                    "tags": ["erc20", "curve_lp"],
                    "permittable": false,
                    "permit": false,
                    "coins": [
                      "0xdAC17F958D2ee523a2206206994597C13D831ec7",
                      "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
                      "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
                    ]
                  },
                  "realToken": {
                    "chainId": 1,
                    "address": "0xc4ad29ba4b3c580e6d59105fff484999997675ff",
                    "name": "Curve.fi USD-BTC-ETH",
                    "symbol": "crv3crypto",
                    "decimals": 18,
                    "originalName": "Curve.fi USD-BTC-ETH",
                    "originalSymbol": "crv3crypto",
                    "tags": ["erc20", "curve_lp"],
                    "permittable": false,
                    "permit": false,
                    "coins": [
                      "0xdAC17F958D2ee523a2206206994597C13D831ec7",
                      "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
                      "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
                    ]
                  }
                }
              }
            ]
          }
        ],
        "amountIn": "100000000",
        "amountOut": "54972421143512185",
        "amountOutWithoutSlippage": "55248664465841392",
        "tokenInPrice": 1,
        "tokenOutPrice": 1811.0144629377985,
        "priceImpact": 0,
        "totalFee": {
          "type": "total",
          "percent": "0.07",
          "amount": 0.06545
        }
      }
    ]
    {
    "priceInDollars": "2.88",
    "executionPrice": "1408116103514941",
    "stablePrice": "0",
    "workerFee": "1408116103514941",
    "deadline": "1699876788",
    "signature": "RiyQlohm2vpof3Nnab4cUwZ/mHey/Su"
    }
    {
    "to": "0x9af02523431E9Ec1Cc649c75aB0322fF34cde337",
    "abi": "function start(string[],bytes[],tuple(uint256,uint256,uint8,bytes32,bytes32)) payable",
    "args": [
    ["LM", "As", "Ss", "Rs", "BU"],
    [
    "0x0000000000000000000000002e1ad108ff1d8c782fcbbb89aad783ac495867560000000000000000000000000000000000000000000000019274b259f6540000000000000000000000000000bd2c008c3467393c6f342a275ec8f2ccd7b4f40d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd2c008c3467393c6f342a275ec8f2ccd7b4f40d",
    "0x0000000000000000000000002827053d2f2c3ed312d2092e57d8537405fdfd0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054cc70a3324cac8308045d027415e4df82ee72b8000000000000000000000000000000000000000000000001906a0a6f860c0d89000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000",
    "0x00000000000000000000000054cc70a3324cac8308045d027415e4df82ee72b8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000da8a6f376f056f0b10980ef0756bd642bc3ecab00000000000000000000000000000000000000000000000018e421e43cc563e5a0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064d09662725ddc8d01a037618906a04326b9985f0000000000000000000000000000000000000000000000000000000000000000",
    "0x00000000000000000000000064d09662725ddc8d01a037618906a04326b9985fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064d09662725ddc8d01a037618906a04326b9985f0000000000000000000000000000000000000000000000000000000001b3d1dc000000000000000000000000000000000000000000000000000000000000000300000000000000000000000074aeed349f3fcae9c158504686f9304c4bbfa39a0000000000000000000000000000000000000000000000000000000000000000",
    "0x00000000000000000000000074aeed349f3fcae9c158504686f9304c4bbfa39affffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd2c008c3467393c6f342a275ec8f2ccd7b4f40d000000000000000000000000000000000000000000000000000000000000a86a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
    ],
    {
    "executionPrice": "449952232019487626",
    "deadline": "1698416184651",
    "v": 28,
    "r": "0x178afdb0baacdae7b6e0c920de67e2b981a3a8c141204f26c66f5d6d148cde0a",
    "s": "0x5391489d89f6955281245896199baee4d3a983caf0af027ceb93aac62e5b743f"
    }
    ],
    "value": "0"
    }
    {
    "result": [
    {
    "requestId": "0x5173910105f8c526ed1dbbac0dc3e0d1efd9b18b83f56d460a7a3dd09d4d198e",
    "status": "completed",
    "source": {
    "chainId": "137",
    "transactionHash": "0xad58de57530187afa410d3dc5356e843be0c123075a51fe38aeb46a7f0481133",
    "from": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "events": [
    {
    "args": {
    "_to": "0xBf0b5D561b986809924f88099c4FF0e6BccE60c9",
    "_from": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "_value": "29000000000000000000"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "to": "0x0000000000000000000000000000000000000000",
    "from": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "token": "0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756",
    "amount": "29000000000000000000"
    },
    "name": "Locked",
    "topic": "0xb5f411fa3c897c9b0b6cd61852278a67e73d885610724a5610a8580d3e94cfdb",
    "address": "0xBf0b5D561b986809924f88099c4FF0e6BccE60c9",
    "signature": "Locked(address,uint256,address,address)"
    },
    {
    "args": {
    "lastOp": 0,
    "result": 1,
    "nextChainId": "250",
    "nextRequestId": "0x5173910105f8c526ed1dbbac0dc3e0d1efd9b18b83f56d460a7a3dd09d4d198e",
    "currentChainId": "137",
    "currentRequestId": "0x0000000000000000000000000000000000000000000000000000000000000000"
    },
    "name": "ComplexOpProcessed",
    "topic": "0x830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c",
    "address": null,
    "signature": "ComplexOpProcessed(uint64,bytes32,uint64,bytes32,uint8,uint8)"
    },
    {
    "args": {
    "payer": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "accountant": "0x94a365CA808029AF8db18257ecd296c16C61AC05",
    "executionPrice": "449952232019487626"
    },
    "name": "FeePaid",
    "topic": "0xbf6afbaffb3b955bebbf43430bbf8eecb8d34ff86f293f592203ab5ed79c5268",
    "address": null,
    "signature": "FeePaid(address,address,uint256)"
    }
    ]
    },
    "destination": {
    "chainId": "250",
    "transactionHash": "0x6969c46c4c8b239486e309155cf75b2b4ea2d1b7a8ca8a868155546cd9ce6edf",
    "to": "0x1C61f207F50acAF3b15D1DE4eb7a02f290c3eE8A",
    "events": [
    {
    "args": {
    "_to": "0x4400671b8238B5E0c7c9d7572746d236cd292845",
    "_from": "0x0000000000000000000000000000000000000000",
    "_value": "0"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x2827053d2F2c3ED312d2092e57D8537405fdFd0f",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x14F98dcf918a451a15f3A11d824C65906bDDc296",
    "_from": "0x0000000000000000000000000000000000000000",
    "_value": "29000000000000000000"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x2827053d2F2c3ED312d2092e57D8537405fdFd0f",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x54cc70A3324cAc8308045D027415e4Df82EE72B8",
    "_from": "0x14F98dcf918a451a15f3A11d824C65906bDDc296",
    "_value": "29000000000000000000"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x2827053d2F2c3ED312d2092e57D8537405fdFd0f",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x14F98dcf918a451a15f3A11d824C65906bDDc296",
    "_from": "0x64D09662725dDc8D01a037618906a04326B9985f",
    "_value": "28994655"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x74aEeD349F3Fcae9C158504686f9304C4Bbfa39A",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x530aF883f135F135BE12A69DC33296fb8149f593",
    "_from": "0x14F98dcf918a451a15f3A11d824C65906bDDc296",
    "_value": "28994655"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x74aEeD349F3Fcae9C158504686f9304C4Bbfa39A",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x0000000000000000000000000000000000000000",
    "_from": "0x530aF883f135F135BE12A69DC33296fb8149f593",
    "_value": "28994655"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x74aEeD349F3Fcae9C158504686f9304C4Bbfa39A",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "to": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "from": "0x530aF883f135F135BE12A69DC33296fb8149f593",
    "token": "0x74aEeD349F3Fcae9C158504686f9304C4Bbfa39A",
    "amount": "28994655"
    },
    "name": "Burn",
    "topic": "0xc489dd211b01a11cf2d73490ca466baa426e76a7811070af00cc9a2bfd322f1c",
    "address": "0x530aF883f135F135BE12A69DC33296fb8149f593",
    "signature": "Burn(address,uint256,address,address)"
    },
    {
    "args": {
    "lastOp": 4,
    "result": 1,
    "nextChainId": "43114",
    "nextRequestId": "0x5d685648f8c21405956e570d0796055dcf330e3768cfd27e1d50a5e46c9a1fae",
    "currentChainId": "250",
    "currentRequestId": "0x5173910105f8c526ed1dbbac0dc3e0d1efd9b18b83f56d460a7a3dd09d4d198e"
    },
    "name": "ComplexOpProcessed",
    "topic": "0x830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c",
    "address": null,
    "signature": "ComplexOpProcessed(uint64,bytes32,uint64,bytes32,uint8,uint8)"
    }
    ]
    }
    }
    ],
    "total": 1
    }
    {
    "status": "completed",
    "inconsistency": false,
    "source": {
    "chainId": "137",
    "transactionHash": "0xad58de57530187afa410d3dc5356e843be0c123075a51fe38aeb46a7f0481133",
    "from": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "events": [
    {
    "args": {
    "_to": "0xBf0b5D561b986809924f88099c4FF0e6BccE60c9",
    "_from": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "_value": "29000000000000000000"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "to": "0x0000000000000000000000000000000000000000",
    "from": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "token": "0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756",
    "amount": "29000000000000000000"
    },
    "name": "Locked",
    "topic": "0xb5f411fa3c897c9b0b6cd61852278a67e73d885610724a5610a8580d3e94cfdb",
    "address": "0xBf0b5D561b986809924f88099c4FF0e6BccE60c9",
    "signature": "Locked(address,uint256,address,address)"
    },
    {
    "args": {
    "lastOp": 0,
    "result": 1,
    "nextChainId": "250",
    "nextRequestId": "0x5173910105f8c526ed1dbbac0dc3e0d1efd9b18b83f56d460a7a3dd09d4d198e",
    "currentChainId": "137",
    "currentRequestId": "0x0000000000000000000000000000000000000000000000000000000000000000"
    },
    "name": "ComplexOpProcessed",
    "topic": "0x830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c",
    "address": null,
    "signature": "ComplexOpProcessed(uint64,bytes32,uint64,bytes32,uint8,uint8)"
    },
    {
    "args": {
    "payer": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "accountant": "0x94a365CA808029AF8db18257ecd296c16C61AC05",
    "executionPrice": "449952232019487626"
    },
    "name": "FeePaid",
    "topic": "0xbf6afbaffb3b955bebbf43430bbf8eecb8d34ff86f293f592203ab5ed79c5268",
    "address": null,
    "signature": "FeePaid(address,address,uint256)"
    }
    ],
    "status": "completed"
    },
    "oracle": {
    "relayChainId": "137",
    "requestId": "0x5173910105f8c526ed1dbbac0dc3e0d1efd9b18b83f56d460a7a3dd09d4d198e",
    "status": "completed",
    "height": "3942",
    "epoch": 4,
    "time": "2023-10-27T14:13:45.949Z"
    },
    "destination": {
    "chainId": "250",
    "transactionHash": "0x6969c46c4c8b239486e309155cf75b2b4ea2d1b7a8ca8a868155546cd9ce6edf",
    "to": "0x1C61f207F50acAF3b15D1DE4eb7a02f290c3eE8A",
    "events": [
    {
    "args": {
    "_to": "0x4400671b8238B5E0c7c9d7572746d236cd292845",
    "_from": "0x0000000000000000000000000000000000000000",
    "_value": "0"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x2827053d2F2c3ED312d2092e57D8537405fdFd0f",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x14F98dcf918a451a15f3A11d824C65906bDDc296",
    "_from": "0x0000000000000000000000000000000000000000",
    "_value": "29000000000000000000"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x2827053d2F2c3ED312d2092e57D8537405fdFd0f",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x54cc70A3324cAc8308045D027415e4Df82EE72B8",
    "_from": "0x14F98dcf918a451a15f3A11d824C65906bDDc296",
    "_value": "29000000000000000000"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x2827053d2F2c3ED312d2092e57D8537405fdFd0f",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x14F98dcf918a451a15f3A11d824C65906bDDc296",
    "_from": "0x64D09662725dDc8D01a037618906a04326B9985f",
    "_value": "28994655"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x74aEeD349F3Fcae9C158504686f9304C4Bbfa39A",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x530aF883f135F135BE12A69DC33296fb8149f593",
    "_from": "0x14F98dcf918a451a15f3A11d824C65906bDDc296",
    "_value": "28994655"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x74aEeD349F3Fcae9C158504686f9304C4Bbfa39A",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "_to": "0x0000000000000000000000000000000000000000",
    "_from": "0x530aF883f135F135BE12A69DC33296fb8149f593",
    "_value": "28994655"
    },
    "name": "Transfer",
    "topic": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "address": "0x74aEeD349F3Fcae9C158504686f9304C4Bbfa39A",
    "signature": "Transfer(address,address,uint256)"
    },
    {
    "args": {
    "to": "0xBD2c008C3467393C6F342A275EC8F2Ccd7B4F40D",
    "from": "0x530aF883f135F135BE12A69DC33296fb8149f593",
    "token": "0x74aEeD349F3Fcae9C158504686f9304C4Bbfa39A",
    "amount": "28994655"
    },
    "name": "Burn",
    "topic": "0xc489dd211b01a11cf2d73490ca466baa426e76a7811070af00cc9a2bfd322f1c",
    "address": "0x530aF883f135F135BE12A69DC33296fb8149f593",
    "signature": "Burn(address,uint256,address,address)"
    },
    {
    "args": {
    "lastOp": 4,
    "result": 1,
    "nextChainId": "43114",
    "nextRequestId": "0x5d685648f8c21405956e570d0796055dcf330e3768cfd27e1d50a5e46c9a1fae",
    "currentChainId": "250",
    "currentRequestId": "0x5173910105f8c526ed1dbbac0dc3e0d1efd9b18b83f56d460a7a3dd09d4d198e"
    },
    "name": "ComplexOpProcessed",
    "topic": "0x830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c",
    "address": null,
    "signature": "ComplexOpProcessed(uint64,bytes32,uint64,bytes32,uint8,uint8)"
    }
    ],
    "status": "completed",
    "emergency": false,
    "error": null
    },
    "data": {
    "callData": "0000000000000089f898c1cbab66ac331e48ad0854bdd7daeee8806c5a1342c68df5f8d3b5ba8c8bdab6060b0f5c53a9cf19a7eb0b478ac03faf58420ef143624e89feb96d5bdd9416d8208c06afb253d3b4d913a45f11f6661837a73706a9272680626e1b84b6c60000000002eefe7b0000000000000f6600000000653bc534",
    "size": 128
    }
    }