// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
library Error {
//////////////////////////////////////////////////////////////
// CONFIGURATION ERRORS //
//////////////////////////////////////////////////////////////
///@notice errors thrown in protocol setup
/// @dev thrown if chain id exceeds max(uint64)
error BLOCK_CHAIN_ID_OUT_OF_BOUNDS();
/// @dev thrown if not possible to revoke a role in broadcasting
error CANNOT_REVOKE_NON_BROADCASTABLE_ROLES();
/// @dev thrown if not possible to revoke last admin
error CANNOT_REVOKE_LAST_ADMIN();
/// @dev thrown if trying to set again pseudo immutables in super registry
error DISABLED();
/// @dev thrown if rescue delay is not yet set for a chain
error DELAY_NOT_SET();
/// @dev thrown if get native token price estimate in paymentHelper is 0
error INVALID_NATIVE_TOKEN_PRICE();
/// @dev thrown if wormhole refund chain id is not set
error REFUND_CHAIN_ID_NOT_SET();
/// @dev thrown if wormhole relayer is not set
error RELAYER_NOT_SET();
/// @dev thrown if a role to be revoked is not assigned
error ROLE_NOT_ASSIGNED();
//////////////////////////////////////////////////////////////
// AUTHORIZATION ERRORS //
//////////////////////////////////////////////////////////////
///@notice errors thrown if functions cannot be called
/// COMMON AUTHORIZATION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if caller is not address(this), internal call
error INVALID_INTERNAL_CALL();
/// @dev thrown if msg.sender is not a valid amb implementation
error NOT_AMB_IMPLEMENTATION();
/// @dev thrown if msg.sender is not an allowed broadcaster
error NOT_ALLOWED_BROADCASTER();
/// @dev thrown if msg.sender is not broadcast amb implementation
error NOT_BROADCAST_AMB_IMPLEMENTATION();
/// @dev thrown if msg.sender is not broadcast state registry
error NOT_BROADCAST_REGISTRY();
/// @dev thrown if msg.sender is not core state registry
error NOT_CORE_STATE_REGISTRY();
/// @dev thrown if msg.sender is not emergency admin
error NOT_EMERGENCY_ADMIN();
/// @dev thrown if msg.sender is not emergency queue
error NOT_EMERGENCY_QUEUE();
/// @dev thrown if msg.sender is not minter
error NOT_MINTER();
/// @dev thrown if msg.sender is not minter state registry
error NOT_MINTER_STATE_REGISTRY_ROLE();
/// @dev thrown if msg.sender is not paymaster
error NOT_PAYMASTER();
/// @dev thrown if msg.sender is not payment admin
error NOT_PAYMENT_ADMIN();
/// @dev thrown if msg.sender is not protocol admin
error NOT_PROTOCOL_ADMIN();
/// @dev thrown if msg.sender is not state registry
error NOT_STATE_REGISTRY();
/// @dev thrown if msg.sender is not super registry
error NOT_SUPER_REGISTRY();
/// @dev thrown if msg.sender is not superform router
error NOT_SUPERFORM_ROUTER();
/// @dev thrown if msg.sender is not a superform
error NOT_SUPERFORM();
/// @dev thrown if msg.sender is not superform factory
error NOT_SUPERFORM_FACTORY();
/// @dev thrown if msg.sender is not timelock form
error NOT_TIMELOCK_SUPERFORM();
/// @dev thrown if msg.sender is not timelock state registry
error NOT_TIMELOCK_STATE_REGISTRY();
/// @dev thrown if msg.sender is not user or disputer
error NOT_VALID_DISPUTER();
/// @dev thrown if the msg.sender is not privileged caller
error NOT_PRIVILEGED_CALLER(bytes32 role);
/// STATE REGISTRY AUTHORIZATION ERRORS
/// ---------------------------------------------------------
/// @dev layerzero adapter specific error, thrown if caller not layerzero endpoint
error CALLER_NOT_ENDPOINT();
/// @dev hyperlane adapter specific error, thrown if caller not hyperlane mailbox
error CALLER_NOT_MAILBOX();
/// @dev wormhole relayer specific error, thrown if caller not wormhole relayer
error CALLER_NOT_RELAYER();
/// @dev thrown if src chain sender is not valid
error INVALID_SRC_SENDER();
//////////////////////////////////////////////////////////////
// INPUT VALIDATION ERRORS //
//////////////////////////////////////////////////////////////
///@notice errors thrown if input variables are not valid
/// COMMON INPUT VALIDATION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if there is an array length mismatch
error ARRAY_LENGTH_MISMATCH();
/// @dev thrown if payload id does not exist
error INVALID_PAYLOAD_ID();
/// @dev error thrown when msg value should be zero in certain payable functions
error MSG_VALUE_NOT_ZERO();
/// @dev thrown if amb ids length is 0
error ZERO_AMB_ID_LENGTH();
/// @dev thrown if address input is address 0
error ZERO_ADDRESS();
/// @dev thrown if amount input is 0
error ZERO_AMOUNT();
/// @dev thrown if final token is address 0
error ZERO_FINAL_TOKEN();
/// @dev thrown if value input is 0
error ZERO_INPUT_VALUE();
/// SUPERFORM ROUTER INPUT VALIDATION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if the vaults data is invalid
error INVALID_SUPERFORMS_DATA();
/// @dev thrown if receiver address is not set
error RECEIVER_ADDRESS_NOT_SET();
/// SUPERFORM FACTORY INPUT VALIDATION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if a form is not ERC165 compatible
error ERC165_UNSUPPORTED();
/// @dev thrown if a form is not form interface compatible
error FORM_INTERFACE_UNSUPPORTED();
/// @dev error thrown if form implementation address already exists
error FORM_IMPLEMENTATION_ALREADY_EXISTS();
/// @dev error thrown if form implementation id already exists
error FORM_IMPLEMENTATION_ID_ALREADY_EXISTS();
/// @dev thrown if a form does not exist
error FORM_DOES_NOT_EXIST();
/// @dev thrown if form id is larger than max uint16
error INVALID_FORM_ID();
/// @dev thrown if superform not on factory
error SUPERFORM_ID_NONEXISTENT();
/// @dev thrown if same vault and form implementation is used to create new superform
error VAULT_FORM_IMPLEMENTATION_COMBINATION_EXISTS();
/// FORM INPUT VALIDATION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if in case of no txData, if liqData.token != vault.asset()
/// in case of txData, if token output of swap != vault.asset()
error DIFFERENT_TOKENS();
/// @dev thrown if the amount in direct withdraw is not correct
error DIRECT_WITHDRAW_INVALID_LIQ_REQUEST();
/// @dev thrown if the amount in xchain withdraw is not correct
error XCHAIN_WITHDRAW_INVALID_LIQ_REQUEST();
/// LIQUIDITY BRIDGE INPUT VALIDATION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if route id is blacklisted in socket
error BLACKLISTED_ROUTE_ID();
/// @dev thrown if route id is not blacklisted in socket
error NOT_BLACKLISTED_ROUTE_ID();
/// @dev error thrown when txData selector of lifi bridge is a blacklisted selector
error BLACKLISTED_SELECTOR();
/// @dev error thrown when txData selector of lifi bridge is not a blacklisted selector
error NOT_BLACKLISTED_SELECTOR();
/// @dev thrown if a certain action of the user is not allowed given the txData provided
error INVALID_ACTION();
/// @dev thrown if in deposits, the liqDstChainId doesn't match the stateReq dstChainId
error INVALID_DEPOSIT_LIQ_DST_CHAIN_ID();
/// @dev thrown if index is invalid
error INVALID_INDEX();
/// @dev thrown if the chain id in the txdata is invalid
error INVALID_TXDATA_CHAIN_ID();
/// @dev thrown if the validation of bridge txData fails due to a destination call present
error INVALID_TXDATA_NO_DESTINATIONCALL_ALLOWED();
/// @dev thrown if the validation of bridge txData fails due to wrong receiver
error INVALID_TXDATA_RECEIVER();
/// @dev thrown if the validation of bridge txData fails due to wrong token
error INVALID_TXDATA_TOKEN();
/// @dev thrown if txData is not present (in case of xChain actions)
error NO_TXDATA_PRESENT();
/// STATE REGISTRY INPUT VALIDATION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if payload is being updated with final amounts length different than amounts length
error DIFFERENT_PAYLOAD_UPDATE_AMOUNTS_LENGTH();
/// @dev thrown if payload is being updated with tx data length different than liq data length
error DIFFERENT_PAYLOAD_UPDATE_TX_DATA_LENGTH();
/// @dev thrown if keeper update final token is different than the vault underlying
error INVALID_UPDATE_FINAL_TOKEN();
/// @dev thrown if broadcast finality for wormhole is invalid
error INVALID_BROADCAST_FINALITY();
/// @dev thrown if amb id is not valid leading to an address 0 of the implementation
error INVALID_BRIDGE_ID();
/// @dev thrown if chain id involved in xchain message is invalid
error INVALID_CHAIN_ID();
/// @dev thrown if payload update amount isn't equal to dst swapper amount
error INVALID_DST_SWAP_AMOUNT();
/// @dev thrown if message amb and proof amb are the same
error INVALID_PROOF_BRIDGE_ID();
/// @dev thrown if order of proof AMBs is incorrect, either duplicated or not incrementing
error INVALID_PROOF_BRIDGE_IDS();
/// @dev thrown if rescue data lengths are invalid
error INVALID_RESCUE_DATA();
/// @dev thrown if delay is invalid
error INVALID_TIMELOCK_DELAY();
/// @dev thrown if amounts being sent in update payload mean a negative slippage
error NEGATIVE_SLIPPAGE();
/// @dev thrown if slippage is outside of bounds
error SLIPPAGE_OUT_OF_BOUNDS();
/// SUPERPOSITION INPUT VALIDATION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if src senders mismatch in state sync
error SRC_SENDER_MISMATCH();
/// @dev thrown if src tx types mismatch in state sync
error SRC_TX_TYPE_MISMATCH();
//////////////////////////////////////////////////////////////
// EXECUTION ERRORS //
//////////////////////////////////////////////////////////////
///@notice errors thrown due to function execution logic
/// COMMON EXECUTION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if the swap in a direct deposit resulted in insufficient tokens
error DIRECT_DEPOSIT_SWAP_FAILED();
/// @dev thrown if payload is not unique
error DUPLICATE_PAYLOAD();
/// @dev thrown if native tokens fail to be sent to superform contracts
error FAILED_TO_SEND_NATIVE();
/// @dev thrown if allowance is not correct to deposit
error INSUFFICIENT_ALLOWANCE_FOR_DEPOSIT();
/// @dev thrown if contract has insufficient balance for operations
error INSUFFICIENT_BALANCE();
/// @dev thrown if native amount is not at least equal to the amount in the request
error INSUFFICIENT_NATIVE_AMOUNT();
/// @dev thrown if payload cannot be decoded
error INVALID_PAYLOAD();
/// @dev thrown if payload status is invalid
error INVALID_PAYLOAD_STATUS();
/// @dev thrown if payload type is invalid
error INVALID_PAYLOAD_TYPE();
/// LIQUIDITY BRIDGE EXECUTION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if we try to decode the final swap output token in a xChain liquidity bridging action
error CANNOT_DECODE_FINAL_SWAP_OUTPUT_TOKEN();
/// @dev thrown if liquidity bridge fails for erc20 or native tokens
error FAILED_TO_EXECUTE_TXDATA(address token);
/// @dev thrown if asset being used for deposit mismatches in multivault deposits
error INVALID_DEPOSIT_TOKEN();
/// STATE REGISTRY EXECUTION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if bridge tokens haven't arrived to destination
error BRIDGE_TOKENS_PENDING();
/// @dev thrown if withdrawal tx data cannot be updated
error CANNOT_UPDATE_WITHDRAW_TX_DATA();
/// @dev thrown if rescue passed dispute deadline
error DISPUTE_TIME_ELAPSED();
/// @dev thrown if message failed to reach the specified level of quorum needed
error INSUFFICIENT_QUORUM();
/// @dev thrown if broadcast payload is invalid
error INVALID_BROADCAST_PAYLOAD();
/// @dev thrown if broadcast fee is invalid
error INVALID_BROADCAST_FEE();
/// @dev thrown if retry fees is less than required
error INVALID_RETRY_FEE();
/// @dev thrown if broadcast message type is wrong
error INVALID_MESSAGE_TYPE();
/// @dev thrown if payload hash is invalid during `retryMessage` on Layezero implementation
error INVALID_PAYLOAD_HASH();
/// @dev thrown if update payload function was called on a wrong payload
error INVALID_PAYLOAD_UPDATE_REQUEST();
/// @dev thrown if a state registry id is 0
error INVALID_REGISTRY_ID();
/// @dev thrown if a form state registry id is 0
error INVALID_FORM_REGISTRY_ID();
/// @dev thrown if trying to finalize the payload but the withdraw is still locked
error LOCKED();
/// @dev thrown if payload is already updated (during xChain deposits)
error PAYLOAD_ALREADY_UPDATED();
/// @dev thrown if payload is already processed
error PAYLOAD_ALREADY_PROCESSED();
/// @dev thrown if payload is not in UPDATED state
error PAYLOAD_NOT_UPDATED();
/// @dev thrown if rescue is still in timelocked state
error RESCUE_LOCKED();
/// @dev thrown if rescue is already proposed
error RESCUE_ALREADY_PROPOSED();
/// @dev thrown if payload hash is zero during `retryMessage` on Layezero implementation
error ZERO_PAYLOAD_HASH();
/// DST SWAPPER EXECUTION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if process dst swap is tried for processed payload id
error DST_SWAP_ALREADY_PROCESSED();
/// @dev thrown if indices have duplicates
error DUPLICATE_INDEX();
/// @dev thrown if failed dst swap is already updated
error FAILED_DST_SWAP_ALREADY_UPDATED();
/// @dev thrown if indices are out of bounds
error INDEX_OUT_OF_BOUNDS();
/// @dev thrown if failed swap token amount is 0
error INVALID_DST_SWAPPER_FAILED_SWAP();
/// @dev thrown if failed swap token amount is not 0 and if token balance is less than amount (non zero)
error INVALID_DST_SWAPPER_FAILED_SWAP_NO_TOKEN_BALANCE();
/// @dev thrown if failed swap token amount is not 0 and if native amount is less than amount (non zero)
error INVALID_DST_SWAPPER_FAILED_SWAP_NO_NATIVE_BALANCE();
/// @dev forbid xChain deposits with destination swaps without interim token set (for user protection)
error INVALID_INTERIM_TOKEN();
/// @dev thrown if dst swap output is less than minimum expected
error INVALID_SWAP_OUTPUT();
/// FORM EXECUTION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if try to forward 4626 share from the superform
error CANNOT_FORWARD_4646_TOKEN();
/// @dev thrown in KYCDAO form if no KYC token is present
error NO_VALID_KYC_TOKEN();
/// @dev thrown in forms where a certain functionality is not allowed or implemented
error NOT_IMPLEMENTED();
/// @dev thrown if form implementation is PAUSED, users cannot perform any action
error PAUSED();
/// @dev thrown if shares != deposit output or assets != redeem output when minting SuperPositions
error VAULT_IMPLEMENTATION_FAILED();
/// @dev thrown if withdrawal tx data is not updated
error WITHDRAW_TOKEN_NOT_UPDATED();
/// @dev thrown if withdrawal tx data is not updated
error WITHDRAW_TX_DATA_NOT_UPDATED();
/// @dev thrown when redeeming from vault yields zero collateral
error WITHDRAW_ZERO_COLLATERAL();
/// PAYMENT HELPER EXECUTION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if chainlink is reporting an improper price
error CHAINLINK_MALFUNCTION();
/// @dev thrown if chainlink is reporting an incomplete round
error CHAINLINK_INCOMPLETE_ROUND();
/// @dev thrown if feed decimals is not 8
error CHAINLINK_UNSUPPORTED_DECIMAL();
/// EMERGENCY QUEUE EXECUTION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if emergency withdraw is not queued
error EMERGENCY_WITHDRAW_NOT_QUEUED();
/// @dev thrown if emergency withdraw is already processed
error EMERGENCY_WITHDRAW_PROCESSED_ALREADY();
/// SUPERPOSITION EXECUTION ERRORS
/// ---------------------------------------------------------
/// @dev thrown if uri cannot be updated
error DYNAMIC_URI_FROZEN();
/// @dev thrown if tx history is not found while state sync
error TX_HISTORY_NOT_FOUND();
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
/// @title IRewardsDistributor
/// @notice interface for the RewardsDistributor contract
/// @author Zeropoint Labs
interface IRewardsDistributor {
//////////////////////////////////////////////////////////////
// ERRORS //
//////////////////////////////////////////////////////////////
/// @dev error message for not being a rewards admin
error NOT_REWARDS_ADMIN();
/// @dev error message for an invalid claim
error INVALID_CLAIM();
/// @dev error message for an invalid batch request (different array lengths)
error INVALID_BATCH_REQ();
/// @dev error message for an invalid claim (invalid token/amounts array length)
error INVALID_REQ_TOKENS_AMOUNTS();
/// @dev error message for an invalid batch claim (invalid token/amounts array length)
error INVALID_BATCH_REQ_TOKENS_AMOUNTS();
/// @dev error message for an invalid merkle root
error INVALID_MERKLE_ROOT();
/// @dev error message for an invalid receiver
error INVALID_RECEIVER();
/// @dev error message for when merkle root is not set
error MERKLE_ROOT_NOT_SET();
/// @dev error message for when the array length is zero
error ZERO_ARR_LENGTH();
/// @dev error message for when the claimer has already claimed their rewards
error ALREADY_CLAIMED();
/// @dev error message for when the claim deadline has passed
error CLAIM_DEADLINE_PASSED();
//////////////////////////////////////////////////////////////
// EVENTS //
//////////////////////////////////////////////////////////////
/// @dev Emitted when tokens are claimed.
event RewardsClaimed(
address indexed claimer,
address indexed receiver,
uint256 periodId,
address[] rewardTokens_,
uint256[] amountsClaimed_
);
/// @dev Emitted when new periodic rewards are set.
event PeriodicRewardsSet(uint256 indexed periodId, bytes32 merkleRoot, uint256 startTimestamp);
//////////////////////////////////////////////////////////////
// STRUCTS //
//////////////////////////////////////////////////////////////
struct PeriodicRewardsData {
uint256 startTimestamp;
bytes32 merkleRoot;
}
//////////////////////////////////////////////////////////////
// EXTERNAL WRITE FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @notice allows owner to set the merkle root for the current period rewards
/// @param root_ is the merkle root for that period generated offchain
/// @dev [gas-opt]: function is payable to avoid msg.value checks
function setPeriodicRewards(bytes32 root_) external payable;
/// @notice lets an account claim a given quantity of reward tokens.
/// @param receiver_ is the receiver of the tokens to claim.
/// @param periodId_ is the specific period to claim
/// @param rewardTokens_ are the address of the rewards token to claim on the specific period
/// @param amountsClaimed_ adre the amount of tokens to claim for each reward token
/// @param proof_ the merkle proof
function claim(
address receiver_,
uint256 periodId_,
address[] calldata rewardTokens_,
uint256[] calldata amountsClaimed_,
bytes32[] calldata proof_
)
external;
/// @notice is a batching version of claim()
function batchClaim(
address receiver_,
uint256[] calldata periodIds_,
address[][] calldata rewardTokens_,
uint256[][] calldata amountsClaimed_,
bytes32[][] calldata proofs_
)
external;
/// @notice allows the owner to rescue any ERC20 tokens sent to the contract
/// @param rewardTokens_ are the address of the rewards token to claim on the specific period
/// @param amounts_ are the amount of tokens to claim for each reward token
function rescueRewards(address[] calldata rewardTokens_, uint256[] calldata amounts_) external;
/// @notice allows the owner to invalidate a period
/// @param periodId_ is the period identifier
function invalidatePeriod(uint256 periodId_) external;
//////////////////////////////////////////////////////////////
// EXTERNAL VIEW FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @notice helps validate if the claim is valid
/// @param claimer_ is the address of the claiming wallet
/// @param periodId_ is the period identifier
/// @param rewardTokens_ are the address of the rewards token to claim on the specific period
/// @param amountsClaimed_ adre the amount of tokens to claim for each reward token
/// @param proof_ is the merkle proof
/// @dev returns false even if proof is valid and user already claimed their periodic rewards
function verifyClaim(
address claimer_,
uint256 periodId_,
address[] calldata rewardTokens_,
uint256[] calldata amountsClaimed_,
bytes32[] calldata proof_
)
external
view
returns (bool valid);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import { IAccessControl } from "openzeppelin-contracts/contracts/access/IAccessControl.sol";
/// @title ISuperRBAC
/// @dev Interface for SuperRBAC
/// @author Zeropoint Labs
interface ISuperRBAC is IAccessControl {
//////////////////////////////////////////////////////////////
// STRUCTS //
//////////////////////////////////////////////////////////////
struct InitialRoleSetup {
address admin;
address emergencyAdmin;
address paymentAdmin;
address csrProcessor;
address tlProcessor;
address brProcessor;
address csrUpdater;
address srcVaaRelayer;
address dstSwapper;
address csrRescuer;
address csrDisputer;
}
//////////////////////////////////////////////////////////////
// EVENTS //
//////////////////////////////////////////////////////////////
/// @dev is emitted when superRegistry is set
event SuperRegistrySet(address indexed superRegistry);
/// @dev is emitted when an admin is set for a role
event RoleAdminSet(bytes32 role, bytes32 adminRole);
//////////////////////////////////////////////////////////////
// EXTERNAL VIEW FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @dev returns the id of the protocol admin role
function PROTOCOL_ADMIN_ROLE() external view returns (bytes32);
/// @dev returns the id of the emergency admin role
function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);
/// @dev returns the id of the payment admin role
function PAYMENT_ADMIN_ROLE() external view returns (bytes32);
/// @dev returns the id of the broadcaster role
function BROADCASTER_ROLE() external view returns (bytes32);
/// @dev returns the id of the core state registry processor role
function CORE_STATE_REGISTRY_PROCESSOR_ROLE() external view returns (bytes32);
/// @dev returns the id of the timelock state registry processor role
function TIMELOCK_STATE_REGISTRY_PROCESSOR_ROLE() external view returns (bytes32);
/// @dev returns the id of the broadcast state registry processor role
function BROADCAST_STATE_REGISTRY_PROCESSOR_ROLE() external view returns (bytes32);
/// @dev returns the id of the core state registry updater role
function CORE_STATE_REGISTRY_UPDATER_ROLE() external view returns (bytes32);
/// @dev returns the id of the dst swapper role
function DST_SWAPPER_ROLE() external view returns (bytes32);
/// @dev returns the id of the core state registry rescuer role
function CORE_STATE_REGISTRY_RESCUER_ROLE() external view returns (bytes32);
/// @dev returns the id of the core state registry rescue disputer role
function CORE_STATE_REGISTRY_DISPUTER_ROLE() external view returns (bytes32);
/// @dev returns the id of wormhole vaa relayer role
function WORMHOLE_VAA_RELAYER_ROLE() external view returns (bytes32);
/// @dev returns whether the given address has the protocol admin role
/// @param admin_ the address to check
function hasProtocolAdminRole(address admin_) external view returns (bool);
/// @dev returns whether the given address has the emergency admin role
/// @param admin_ the address to check
function hasEmergencyAdminRole(address admin_) external view returns (bool);
//////////////////////////////////////////////////////////////
// EXTERNAL WRITE FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @dev updates the super registry address
function setSuperRegistry(address superRegistry_) external;
/// @dev configures a new role in superForm
/// @param role_ the role to set
/// @param adminRole_ the admin role to set as admin
function setRoleAdmin(bytes32 role_, bytes32 adminRole_) external;
/// @dev revokes the role_ from superRegistryAddressId_ on all chains
/// @param role_ the role to revoke
/// @param extraData_ amb config if broadcasting is required
/// @param superRegistryAddressId_ the super registry address id
function revokeRoleSuperBroadcast(
bytes32 role_,
bytes memory extraData_,
bytes32 superRegistryAddressId_
)
external
payable;
/// @dev allows sync of global roles from different chains using broadcast registry
/// @notice may not work for all roles
function stateSyncBroadcast(bytes memory data_) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
/// @title ISuperRegistry
/// @dev Interface for SuperRegistry
/// @author Zeropoint Labs
interface ISuperRegistry {
//////////////////////////////////////////////////////////////
// EVENTS //
//////////////////////////////////////////////////////////////
/// @dev emitted when permit2 is set.
event SetPermit2(address indexed permit2);
/// @dev is emitted when an address is set.
event AddressUpdated(
bytes32 indexed protocolAddressId, uint64 indexed chainId, address indexed oldAddress, address newAddress
);
/// @dev is emitted when a new token bridge is configured.
event SetBridgeAddress(uint256 indexed bridgeId, address indexed bridgeAddress);
/// @dev is emitted when a new bridge validator is configured.
event SetBridgeValidator(uint256 indexed bridgeId, address indexed bridgeValidator);
/// @dev is emitted when a new amb is configured.
event SetAmbAddress(uint8 indexed ambId_, address indexed ambAddress_, bool indexed isBroadcastAMB_);
/// @dev is emitted when a new state registry is configured.
event SetStateRegistryAddress(uint8 indexed registryId_, address indexed registryAddress_);
/// @dev is emitted when a new delay is configured.
event SetDelay(uint256 indexed oldDelay_, uint256 indexed newDelay_);
/// @dev is emitted when a new vault limit is configured
event SetVaultLimitPerDestination(uint64 indexed chainId_, uint256 indexed vaultLimit_);
//////////////////////////////////////////////////////////////
// EXTERNAL VIEW FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @dev gets the deposit rescue delay
function delay() external view returns (uint256);
/// @dev returns the permit2 address
function PERMIT2() external view returns (address);
/// @dev returns the id of the superform router module
function SUPERFORM_ROUTER() external view returns (bytes32);
/// @dev returns the id of the superform factory module
function SUPERFORM_FACTORY() external view returns (bytes32);
/// @dev returns the id of the superform paymaster contract
function PAYMASTER() external view returns (bytes32);
/// @dev returns the id of the superform payload helper contract
function PAYMENT_HELPER() external view returns (bytes32);
/// @dev returns the id of the core state registry module
function CORE_STATE_REGISTRY() external view returns (bytes32);
/// @dev returns the id of the timelock form state registry module
function TIMELOCK_STATE_REGISTRY() external view returns (bytes32);
/// @dev returns the id of the broadcast state registry module
function BROADCAST_REGISTRY() external view returns (bytes32);
/// @dev returns the id of the super positions module
function SUPER_POSITIONS() external view returns (bytes32);
/// @dev returns the id of the super rbac module
function SUPER_RBAC() external view returns (bytes32);
/// @dev returns the id of the payload helper module
function PAYLOAD_HELPER() external view returns (bytes32);
/// @dev returns the id of the dst swapper keeper
function DST_SWAPPER() external view returns (bytes32);
/// @dev returns the id of the emergency queue
function EMERGENCY_QUEUE() external view returns (bytes32);
/// @dev returns the id of the superform receiver
function SUPERFORM_RECEIVER() external view returns (bytes32);
/// @dev returns the id of the payment admin keeper
function PAYMENT_ADMIN() external view returns (bytes32);
/// @dev returns the id of the core state registry processor keeper
function CORE_REGISTRY_PROCESSOR() external view returns (bytes32);
/// @dev returns the id of the broadcast registry processor keeper
function BROADCAST_REGISTRY_PROCESSOR() external view returns (bytes32);
/// @dev returns the id of the timelock form state registry processor keeper
function TIMELOCK_REGISTRY_PROCESSOR() external view returns (bytes32);
/// @dev returns the id of the core state registry updater keeper
function CORE_REGISTRY_UPDATER() external view returns (bytes32);
/// @dev returns the id of the core state registry updater keeper
function CORE_REGISTRY_RESCUER() external view returns (bytes32);
/// @dev returns the id of the core state registry updater keeper
function CORE_REGISTRY_DISPUTER() external view returns (bytes32);
/// @dev returns the id of the core state registry updater keeper
function DST_SWAPPER_PROCESSOR() external view returns (bytes32);
/// @dev gets the address of a contract on current chain
/// @param id_ is the id of the contract
function getAddress(bytes32 id_) external view returns (address);
/// @dev gets the address of a contract on a target chain
/// @param id_ is the id of the contract
/// @param chainId_ is the chain id of that chain
function getAddressByChainId(bytes32 id_, uint64 chainId_) external view returns (address);
/// @dev gets the address of a bridge
/// @param bridgeId_ is the id of a bridge
/// @return bridgeAddress_ is the address of the form
function getBridgeAddress(uint8 bridgeId_) external view returns (address bridgeAddress_);
/// @dev gets the address of a bridge validator
/// @param bridgeId_ is the id of a bridge
/// @return bridgeValidator_ is the address of the form
function getBridgeValidator(uint8 bridgeId_) external view returns (address bridgeValidator_);
/// @dev gets the address of a amb
/// @param ambId_ is the id of a bridge
/// @return ambAddress_ is the address of the form
function getAmbAddress(uint8 ambId_) external view returns (address ambAddress_);
/// @dev gets the id of the amb
/// @param ambAddress_ is the address of an amb
/// @return ambId_ is the identifier of an amb
function getAmbId(address ambAddress_) external view returns (uint8 ambId_);
/// @dev gets the address of the registry
/// @param registryId_ is the id of the state registry
/// @return registryAddress_ is the address of the state registry
function getStateRegistry(uint8 registryId_) external view returns (address registryAddress_);
/// @dev gets the id of the registry
/// @notice reverts if the id is not found
/// @param registryAddress_ is the address of the state registry
/// @return registryId_ is the id of the state registry
function getStateRegistryId(address registryAddress_) external view returns (uint8 registryId_);
/// @dev gets the safe vault limit
/// @param chainId_ is the id of the remote chain
/// @return vaultLimitPerDestination_ is the safe number of vaults to deposit
/// without hitting out of gas error
function getVaultLimitPerDestination(uint64 chainId_) external view returns (uint256 vaultLimitPerDestination_);
/// @dev helps validate if an address is a valid state registry
/// @param registryAddress_ is the address of the state registry
/// @return valid_ a flag indicating if its valid.
function isValidStateRegistry(address registryAddress_) external view returns (bool valid_);
/// @dev helps validate if an address is a valid amb implementation
/// @param ambAddress_ is the address of the amb implementation
/// @return valid_ a flag indicating if its valid.
function isValidAmbImpl(address ambAddress_) external view returns (bool valid_);
/// @dev helps validate if an address is a valid broadcast amb implementation
/// @param ambAddress_ is the address of the broadcast amb implementation
/// @return valid_ a flag indicating if its valid.
function isValidBroadcastAmbImpl(address ambAddress_) external view returns (bool valid_);
//////////////////////////////////////////////////////////////
// EXTERNAL WRITE FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @dev sets the deposit rescue delay
/// @param delay_ the delay in seconds before the deposit rescue can be finalized
function setDelay(uint256 delay_) external;
/// @dev sets the permit2 address
/// @param permit2_ the address of the permit2 contract
function setPermit2(address permit2_) external;
/// @dev sets the safe vault limit
/// @param chainId_ is the remote chain identifier
/// @param vaultLimit_ is the max limit of vaults per transaction
function setVaultLimitPerDestination(uint64 chainId_, uint256 vaultLimit_) external;
/// @dev sets new addresses on specific chains.
/// @param ids_ are the identifiers of the address on that chain
/// @param newAddresses_ are the new addresses on that chain
/// @param chainIds_ are the chain ids of that chain
function batchSetAddress(
bytes32[] calldata ids_,
address[] calldata newAddresses_,
uint64[] calldata chainIds_
)
external;
/// @dev sets a new address on a specific chain.
/// @param id_ the identifier of the address on that chain
/// @param newAddress_ the new address on that chain
/// @param chainId_ the chain id of that chain
function setAddress(bytes32 id_, address newAddress_, uint64 chainId_) external;
/// @dev allows admin to set the bridge address for an bridge id.
/// @notice this function operates in an APPEND-ONLY fashion.
/// @param bridgeId_ represents the bridge unique identifier.
/// @param bridgeAddress_ represents the bridge address.
/// @param bridgeValidator_ represents the bridge validator address.
function setBridgeAddresses(
uint8[] memory bridgeId_,
address[] memory bridgeAddress_,
address[] memory bridgeValidator_
)
external;
/// @dev allows admin to set the amb address for an amb id.
/// @notice this function operates in an APPEND-ONLY fashion.
/// @param ambId_ represents the bridge unique identifier.
/// @param ambAddress_ represents the bridge address.
/// @param isBroadcastAMB_ represents whether the amb implementation supports broadcasting
function setAmbAddress(
uint8[] memory ambId_,
address[] memory ambAddress_,
bool[] memory isBroadcastAMB_
)
external;
/// @dev allows admin to set the state registry address for an state registry id.
/// @notice this function operates in an APPEND-ONLY fashion.
/// @param registryId_ represents the state registry's unique identifier.
/// @param registryAddress_ represents the state registry's address.
function setStateRegistryAddress(uint8[] memory registryId_, address[] memory registryAddress_) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
/// library imports
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import { IERC20 } from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import { SafeERC20 } from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
/// local imports
import { Error } from "src/libraries/Error.sol";
import { ISuperRBAC } from "src/interfaces/ISuperRBAC.sol";
import { ISuperRegistry } from "src/interfaces/ISuperRegistry.sol";
import { IRewardsDistributor } from "src/interfaces/IRewardsDistributor.sol";
/// @title RewardsDistributor
/// @author Zeropoint Labs
/// @notice This will be SUPERFORM_RECEIVER in SuperRegistry.
/// @notice Also, requires a new REWARDS_ADMIN_ROLE (a fireblocks address)
contract RewardsDistributor is IRewardsDistributor {
using SafeERC20 for IERC20;
//////////////////////////////////////////////////////////////
// CONSTANTS //
//////////////////////////////////////////////////////////////
ISuperRegistry public immutable superRegistry;
uint64 public immutable CHAIN_ID;
bytes32 internal constant ZERO_BYTES32 = bytes32(0);
uint256 public constant DEADLINE = 52 weeks;
//////////////////////////////////////////////////////////////
// STATE VARIABLES //
//////////////////////////////////////////////////////////////
uint256 public currentPeriodId;
/// @dev maps the periodic rewards id to its corresponding merkle root and startTimestamp
mapping(uint256 periodId => PeriodicRewardsData data) public periodicRewardsMerkleRootData;
/// @dev mapping from periodId to claimer address to claimed status
mapping(uint256 periodId => mapping(address claimerAddress => bool claimed)) public periodicRewardsClaimed;
//////////////////////////////////////////////////////////////
// MODIFIERS //
//////////////////////////////////////////////////////////////
modifier onlyRewardsAdmin() {
if (
!ISuperRBAC(superRegistry.getAddress(keccak256("SUPER_RBAC"))).hasRole(
keccak256("REWARDS_ADMIN_ROLE"), msg.sender
)
) {
revert NOT_REWARDS_ADMIN();
}
_;
}
//////////////////////////////////////////////////////////////
// CONSTRUCTOR //
//////////////////////////////////////////////////////////////
/// @param superRegistry_ the superform registry contract
constructor(address superRegistry_) {
if (superRegistry_ == address(0)) {
revert Error.ZERO_ADDRESS();
}
if (block.chainid > type(uint64).max) {
revert Error.BLOCK_CHAIN_ID_OUT_OF_BOUNDS();
}
superRegistry = ISuperRegistry(superRegistry_);
CHAIN_ID = uint64(block.chainid);
}
//////////////////////////////////////////////////////////////
// EXTERNAL WRITE FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @inheritdoc IRewardsDistributor
function setPeriodicRewards(bytes32 root_) external payable override onlyRewardsAdmin {
if (root_ == ZERO_BYTES32) revert INVALID_MERKLE_ROOT();
uint256 periodId = currentPeriodId;
uint256 startTimestamp = block.timestamp;
periodicRewardsMerkleRootData[periodId].startTimestamp = startTimestamp;
periodicRewardsMerkleRootData[periodId].merkleRoot = root_;
++currentPeriodId;
emit PeriodicRewardsSet(periodId, root_, startTimestamp);
}
/// @inheritdoc IRewardsDistributor
function claim(
address receiver_,
uint256 periodId_,
address[] calldata rewardTokens_,
uint256[] calldata amountsClaimed_,
bytes32[] calldata proof_
)
external
override
{
if (receiver_ == address(0)) revert INVALID_RECEIVER();
uint256 tokensToClaim = rewardTokens_.length;
if (tokensToClaim == 0) revert ZERO_ARR_LENGTH();
if (tokensToClaim != amountsClaimed_.length) revert INVALID_REQ_TOKENS_AMOUNTS();
_claim(receiver_, periodId_, rewardTokens_, amountsClaimed_, tokensToClaim, proof_);
emit RewardsClaimed(msg.sender, receiver_, periodId_, rewardTokens_, amountsClaimed_);
}
/// @inheritdoc IRewardsDistributor
function batchClaim(
address receiver_,
uint256[] calldata periodIds_,
address[][] calldata rewardTokens_,
uint256[][] calldata amountsClaimed_,
bytes32[][] calldata proofs_
)
external
override
{
if (receiver_ == address(0)) revert INVALID_RECEIVER();
uint256 len = periodIds_.length;
if (len == 0) revert ZERO_ARR_LENGTH();
if (!(len == proofs_.length && len == rewardTokens_.length && len == amountsClaimed_.length)) {
revert INVALID_BATCH_REQ();
}
for (uint256 i; i < len; ++i) {
uint256 tokensToClaim = rewardTokens_[i].length;
if (tokensToClaim == 0) revert ZERO_ARR_LENGTH();
if (tokensToClaim != amountsClaimed_[i].length) revert INVALID_BATCH_REQ_TOKENS_AMOUNTS();
_claim(receiver_, periodIds_[i], rewardTokens_[i], amountsClaimed_[i], tokensToClaim, proofs_[i]);
emit RewardsClaimed(msg.sender, receiver_, periodIds_[i], rewardTokens_[i], amountsClaimed_[i]);
}
}
/// @inheritdoc IRewardsDistributor
function rescueRewards(
address[] calldata rewardTokens_,
uint256[] calldata amounts_
)
external
override
onlyRewardsAdmin
{
address receiver = superRegistry.getAddress(keccak256("PAYMASTER"));
uint256 len = rewardTokens_.length;
if (len == 0) revert ZERO_ARR_LENGTH();
if (len != amounts_.length) revert INVALID_REQ_TOKENS_AMOUNTS();
_transferRewards(receiver, rewardTokens_, amounts_, rewardTokens_.length);
}
/// @inheritdoc IRewardsDistributor
function invalidatePeriod(uint256 periodId_) external override onlyRewardsAdmin {
delete periodicRewardsMerkleRootData[periodId_];
}
//////////////////////////////////////////////////////////////
// EXTERNAL VIEW FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @inheritdoc IRewardsDistributor
function verifyClaim(
address receiver_,
uint256 periodId_,
address[] calldata rewardTokens_,
uint256[] calldata amountsClaimed_,
bytes32[] calldata proof_
)
public
view
override
returns (bool valid)
{
bytes32 root = periodicRewardsMerkleRootData[periodId_].merkleRoot;
if (root == ZERO_BYTES32) revert MERKLE_ROOT_NOT_SET();
uint256 deadlineTimestamp = periodicRewardsMerkleRootData[periodId_].startTimestamp + DEADLINE;
if (block.timestamp > deadlineTimestamp) revert CLAIM_DEADLINE_PASSED();
/// @dev a given receiver cannot claim rewards a second time for a given period
if (periodicRewardsClaimed[periodId_][receiver_]) revert ALREADY_CLAIMED();
/// @dev double hashing is used to protect from
/// https://www.rareskills.io/post/merkle-tree-second-preimage-attack
bytes32 leaf = keccak256(
bytes.concat(keccak256(abi.encode(receiver_, periodId_, rewardTokens_, amountsClaimed_, CHAIN_ID)))
);
return MerkleProof.verify(proof_, root, leaf);
}
//////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS //
//////////////////////////////////////////////////////////////
/// @notice helper function for processing claim
function _claim(
address receiver_,
uint256 periodId_,
address[] calldata rewardTokens_,
uint256[] calldata amountsClaimed_,
uint256 tokensToClaim_,
bytes32[] calldata proof_
)
internal
{
/// @dev claim verification is on receiver, this allows anyone to permissionessly claim on behalf of any address
if (!verifyClaim(receiver_, periodId_, rewardTokens_, amountsClaimed_, proof_)) revert INVALID_CLAIM();
periodicRewardsClaimed[periodId_][receiver_] = true;
_transferRewards(receiver_, rewardTokens_, amountsClaimed_, tokensToClaim_);
}
/// @notice transfer token rewards to the receiver
function _transferRewards(
address receiver_,
address[] calldata rewardTokens_,
uint256[] calldata amountsClaimed_,
uint256 tokensToClaim_
)
internal
{
for (uint256 i; i < tokensToClaim_; ++i) {
IERC20(rewardTokens_[i]).safeTransfer(receiver_, amountsClaimed_[i]);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
{
"compilationTarget": {
"src/RewardsDistributor.sol": "RewardsDistributor"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/contracts/=lib/ERC1155A/lib/openzeppelin-contracts/contracts/",
":ERC1155A/=lib/ERC1155A/src/",
":ds-test/=lib/ds-test/src/",
":erc4626-tests/=lib/ERC1155A/lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/ERC1155A/lib/openzeppelin-contracts/",
":pigeon/=lib/pigeon/src/",
":solady/=lib/pigeon/lib/solady/",
":solmate/=lib/ERC1155A/lib/solmate/src/",
":super-vaults/=lib/super-vaults/src/",
":v2-core/=lib/super-vaults/lib/v2-core/contracts/",
":v2-periphery/=lib/super-vaults/lib/v2-periphery/contracts/",
":v3-core/=lib/super-vaults/lib/v3-core/"
]
}
[{"inputs":[{"internalType":"address","name":"superRegistry_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ALREADY_CLAIMED","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"BLOCK_CHAIN_ID_OUT_OF_BOUNDS","type":"error"},{"inputs":[],"name":"CLAIM_DEADLINE_PASSED","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"INVALID_BATCH_REQ","type":"error"},{"inputs":[],"name":"INVALID_BATCH_REQ_TOKENS_AMOUNTS","type":"error"},{"inputs":[],"name":"INVALID_CLAIM","type":"error"},{"inputs":[],"name":"INVALID_MERKLE_ROOT","type":"error"},{"inputs":[],"name":"INVALID_RECEIVER","type":"error"},{"inputs":[],"name":"INVALID_REQ_TOKENS_AMOUNTS","type":"error"},{"inputs":[],"name":"MERKLE_ROOT_NOT_SET","type":"error"},{"inputs":[],"name":"NOT_REWARDS_ADMIN","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ZERO_ADDRESS","type":"error"},{"inputs":[],"name":"ZERO_ARR_LENGTH","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"periodId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"startTimestamp","type":"uint256"}],"name":"PeriodicRewardsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"periodId","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"rewardTokens_","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amountsClaimed_","type":"uint256[]"}],"name":"RewardsClaimed","type":"event"},{"inputs":[],"name":"CHAIN_ID","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEADLINE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256[]","name":"periodIds_","type":"uint256[]"},{"internalType":"address[][]","name":"rewardTokens_","type":"address[][]"},{"internalType":"uint256[][]","name":"amountsClaimed_","type":"uint256[][]"},{"internalType":"bytes32[][]","name":"proofs_","type":"bytes32[][]"}],"name":"batchClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"periodId_","type":"uint256"},{"internalType":"address[]","name":"rewardTokens_","type":"address[]"},{"internalType":"uint256[]","name":"amountsClaimed_","type":"uint256[]"},{"internalType":"bytes32[]","name":"proof_","type":"bytes32[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentPeriodId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"periodId_","type":"uint256"}],"name":"invalidatePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"periodId","type":"uint256"},{"internalType":"address","name":"claimerAddress","type":"address"}],"name":"periodicRewardsClaimed","outputs":[{"internalType":"bool","name":"claimed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"periodId","type":"uint256"}],"name":"periodicRewardsMerkleRootData","outputs":[{"internalType":"uint256","name":"startTimestamp","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"rewardTokens_","type":"address[]"},{"internalType":"uint256[]","name":"amounts_","type":"uint256[]"}],"name":"rescueRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"root_","type":"bytes32"}],"name":"setPeriodicRewards","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"superRegistry","outputs":[{"internalType":"contract ISuperRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"periodId_","type":"uint256"},{"internalType":"address[]","name":"rewardTokens_","type":"address[]"},{"internalType":"uint256[]","name":"amountsClaimed_","type":"uint256[]"},{"internalType":"bytes32[]","name":"proof_","type":"bytes32[]"}],"name":"verifyClaim","outputs":[{"internalType":"bool","name":"valid","type":"bool"}],"stateMutability":"view","type":"function"}]