Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Loading...
Loading
Contract Name:
Yaho
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 10000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { MessageIdCalculator } from "./utils/MessageIdCalculator.sol"; import { MessageHashCalculator } from "./utils/MessageHashCalculator.sol"; import { IYaho } from "./interfaces/IYaho.sol"; import { IReporter } from "./interfaces/IReporter.sol"; import { Message } from "./interfaces/IMessage.sol"; import { IAdapter } from "./interfaces/IAdapter.sol"; contract Yaho is IYaho, MessageIdCalculator, MessageHashCalculator { mapping(uint256 => bytes32) private _pendingMessageHashes; uint256 public currentNonce; /// @inheritdoc IYaho function dispatchMessage( uint256 targetChainId, uint256 threshold, address receiver, bytes calldata data, IReporter[] calldata reporters, IAdapter[] calldata adapters ) external returns (uint256) { _checkReportersAndAdapters(threshold, reporters, adapters); (uint256 messageId, bytes32 messageHash) = _dispatchMessage( targetChainId, threshold, receiver, data, reporters, adapters ); _pendingMessageHashes[messageId] = messageHash; return messageId; } /// @inheritdoc IYaho function dispatchMessageToAdapters( uint256 targetChainId, uint256 threshold, address receiver, bytes calldata data, IReporter[] calldata reporters, IAdapter[] calldata adapters ) external payable returns (uint256, bytes32[] memory) { _checkReportersAndAdapters(threshold, reporters, adapters); (uint256 messageId, bytes32 messageHash) = _dispatchMessage( targetChainId, threshold, receiver, data, reporters, adapters ); bytes32[] memory reportersReceipts = _dispatchMessageToAdapters( targetChainId, messageId, messageHash, reporters, adapters ); return (messageId, reportersReceipts); } /// @inheritdoc IYaho function dispatchMessagesToAdapters( uint256 targetChainId, uint256[] calldata thresholds, address[] calldata receivers, bytes[] calldata data, IReporter[] calldata reporters, IAdapter[] calldata adapters ) external payable returns (uint256[] memory, bytes32[] memory) { if (thresholds.length != receivers.length) revert UnequalArrayLengths(thresholds.length, receivers.length); if (thresholds.length != data.length) revert UnequalArrayLengths(thresholds.length, data.length); uint256[] memory messageIds = new uint256[](receivers.length); bytes32[] memory messageHashes = new bytes32[](receivers.length); for (uint256 i = 0; i < receivers.length; ) { _checkReportersAndAdapters(thresholds[i], reporters, adapters); (messageIds[i], messageHashes[i]) = _dispatchMessage( targetChainId, thresholds[i], receivers[i], data[i], reporters, adapters ); unchecked { ++i; } } bytes32[] memory reportersReceipts = new bytes32[](reporters.length); reportersReceipts = _dispatchMessagesToAdapters(targetChainId, messageIds, messageHashes, reporters, adapters); return (messageIds, reportersReceipts); } /// @inheritdoc IYaho function getPendingMessageHash(uint256 messageId) external view returns (bytes32) { return _pendingMessageHashes[messageId]; } /// @inheritdoc IYaho function relayMessagesToAdapters(Message[] calldata messages) external payable returns (bytes32[] memory) { if (messages.length == 0) revert NoMessagesGiven(); bytes32 expectedParams = keccak256( abi.encode(messages[0].targetChainId, messages[0].reporters, messages[0].adapters) ); bytes32[] memory messageHashes = new bytes32[](messages.length); uint256[] memory messageIds = new uint256[](messages.length); for (uint256 i = 0; i < messages.length; i++) { Message memory message = messages[i]; if ( i > 0 && expectedParams != keccak256(abi.encode(message.targetChainId, message.reporters, message.adapters)) ) revert InvalidMessage(message); uint256 messageId = calculateMessageId(block.chainid, address(this), calculateMessageHash(message)); bytes32 messageHash = _pendingMessageHashes[messageId]; if (messageHash == bytes32(0)) revert MessageHashNotFound(messageId); messageHashes[i] = messageHash; messageIds[i] = messageId; delete _pendingMessageHashes[messageId]; } return _dispatchMessagesToAdapters( messages[0].targetChainId, messageIds, messageHashes, messages[0].reporters, messages[0].adapters ); } function _checkReportersAndAdapters( uint256 threshold, IReporter[] calldata reporters, IAdapter[] calldata adapters ) internal pure { if (reporters.length == 0) revert NoReportersGiven(); if (adapters.length == 0) revert NoAdaptersGiven(); if (reporters.length != adapters.length) revert UnequalArrayLengths(reporters.length, adapters.length); if (threshold > reporters.length || threshold == 0) revert InvalidThreshold(threshold, reporters.length); } function _dispatchMessage( uint256 targetChainId, uint256 threshold, address receiver, bytes calldata data, IReporter[] calldata reporters, IAdapter[] calldata adapters ) internal returns (uint256, bytes32) { address sender = msg.sender; Message memory message = Message( currentNonce, targetChainId, threshold, sender, receiver, data, reporters, adapters ); bytes32 messageHash = calculateMessageHash(message); uint256 messageId = calculateMessageId(block.chainid, address(this), messageHash); unchecked { ++currentNonce; } emit MessageDispatched(messageId, message); return (messageId, messageHash); } function _dispatchMessageToAdapters( uint256 targetChainId, uint256 messageId, bytes32 messageHash, IReporter[] memory reporters, IAdapter[] memory adapters ) internal returns (bytes32[] memory) { uint256[] memory messageIds = new uint256[](1); bytes32[] memory messageHashes = new bytes32[](1); messageIds[0] = messageId; messageHashes[0] = messageHash; return _dispatchMessagesToAdapters(targetChainId, messageIds, messageHashes, reporters, adapters); } function _dispatchMessagesToAdapters( uint256 targetChainId, uint256[] memory messageIds, bytes32[] memory messageHashes, IReporter[] memory reporters, IAdapter[] memory adapters ) internal returns (bytes32[] memory) { bytes32[] memory reportersReceipts = new bytes32[](reporters.length); for (uint256 i = 0; i < reporters.length; ) { IReporter reporter = reporters[i]; if (address(reporter) != address(0)) { reportersReceipts[i] = reporter.dispatchMessages(targetChainId, adapters[i], messageIds, messageHashes); } unchecked { ++i; } } return reportersReceipts; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IAxelarGateway } from '../interfaces/IAxelarGateway.sol'; import { IAxelarExecutable } from '../interfaces/IAxelarExecutable.sol'; contract AxelarExecutable is IAxelarExecutable { IAxelarGateway public immutable gateway; constructor(address gateway_) { if (gateway_ == address(0)) revert InvalidAddress(); gateway = IAxelarGateway(gateway_); } function execute( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) external { bytes32 payloadHash = keccak256(payload); if (!gateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash)) revert NotApprovedByGateway(); _execute(sourceChain, sourceAddress, payload); } function executeWithToken( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload, string calldata tokenSymbol, uint256 amount ) external { bytes32 payloadHash = keccak256(payload); if ( !gateway.validateContractCallAndMint( commandId, sourceChain, sourceAddress, payloadHash, tokenSymbol, amount ) ) revert NotApprovedByGateway(); _executeWithToken(sourceChain, sourceAddress, payload, tokenSymbol, amount); } function _execute( string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) internal virtual {} function _executeWithToken( string calldata sourceChain, string calldata sourceAddress, bytes calldata payload, string calldata tokenSymbol, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IAxelarGateway } from './IAxelarGateway.sol'; interface IAxelarExecutable { error InvalidAddress(); error NotApprovedByGateway(); function gateway() external view returns (IAxelarGateway); function execute( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) external; function executeWithToken( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload, string calldata tokenSymbol, uint256 amount ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IUpgradable } from '../interfaces/IUpgradable.sol'; /** * @title IAxelarGasService Interface * @notice This is an interface for the AxelarGasService contract which manages gas payments * and refunds for cross-chain communication on the Axelar network. * @dev This interface inherits IUpgradable */ interface IAxelarGasService is IUpgradable { error NothingReceived(); error InvalidAddress(); error NotCollector(); error InvalidAmounts(); event GasPaidForContractCall( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, address gasToken, uint256 gasFeeAmount, address refundAddress ); event GasPaidForContractCallWithToken( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, address gasToken, uint256 gasFeeAmount, address refundAddress ); event NativeGasPaidForContractCall( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, uint256 gasFeeAmount, address refundAddress ); event NativeGasPaidForContractCallWithToken( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, uint256 gasFeeAmount, address refundAddress ); event GasPaidForExpressCall( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, address gasToken, uint256 gasFeeAmount, address refundAddress ); event GasPaidForExpressCallWithToken( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, address gasToken, uint256 gasFeeAmount, address refundAddress ); event NativeGasPaidForExpressCall( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, uint256 gasFeeAmount, address refundAddress ); event NativeGasPaidForExpressCallWithToken( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, uint256 gasFeeAmount, address refundAddress ); event GasAdded( bytes32 indexed txHash, uint256 indexed logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress ); event NativeGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress); event ExpressGasAdded( bytes32 indexed txHash, uint256 indexed logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress ); event NativeExpressGasAdded( bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress ); event Refunded( bytes32 indexed txHash, uint256 indexed logIndex, address payable receiver, address token, uint256 amount ); /** * @notice Pay for gas using ERC20 tokens for a contract call on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param gasToken The address of the ERC20 token used to pay for gas * @param gasFeeAmount The amount of tokens to pay for gas * @param refundAddress The address where refunds, if any, should be sent */ function payGasForContractCall( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Pay for gas using ERC20 tokens for a contract call with tokens on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call with tokens will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call with tokens * @param symbol The symbol of the token to be sent with the call * @param amount The amount of tokens to be sent with the call * @param gasToken The address of the ERC20 token used to pay for gas * @param gasFeeAmount The amount of tokens to pay for gas * @param refundAddress The address where refunds, if any, should be sent */ function payGasForContractCallWithToken( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, string calldata symbol, uint256 amount, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Pay for gas using native currency for a contract call on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param refundAddress The address where refunds, if any, should be sent */ function payNativeGasForContractCall( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, address refundAddress ) external payable; /** * @notice Pay for gas using native currency for a contract call with tokens on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call with tokens will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call with tokens * @param symbol The symbol of the token to be sent with the call * @param amount The amount of tokens to be sent with the call * @param refundAddress The address where refunds, if any, should be sent */ function payNativeGasForContractCallWithToken( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, string calldata symbol, uint256 amount, address refundAddress ) external payable; /** * @notice Pay for gas using ERC20 tokens for an express contract call on a destination chain. * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param gasToken The address of the ERC20 token used to pay for gas * @param gasFeeAmount The amount of tokens to pay for gas * @param refundAddress The address where refunds, if any, should be sent */ function payGasForExpressCall( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Pay for gas using ERC20 tokens for an express contract call with tokens on a destination chain. * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call with tokens will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call with tokens * @param symbol The symbol of the token to be sent with the call * @param amount The amount of tokens to be sent with the call * @param gasToken The address of the ERC20 token used to pay for gas * @param gasFeeAmount The amount of tokens to pay for gas * @param refundAddress The address where refunds, if any, should be sent */ function payGasForExpressCallWithToken( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, string calldata symbol, uint256 amount, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Pay for gas using native currency for an express contract call on a destination chain. * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param refundAddress The address where refunds, if any, should be sent */ function payNativeGasForExpressCall( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, address refundAddress ) external payable; /** * @notice Pay for gas using native currency for an express contract call with tokens on a destination chain. * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call with tokens will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call with tokens * @param symbol The symbol of the token to be sent with the call * @param amount The amount of tokens to be sent with the call * @param refundAddress The address where refunds, if any, should be sent */ function payNativeGasForExpressCallWithToken( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, string calldata symbol, uint256 amount, address refundAddress ) external payable; /** * @notice Add additional gas payment using ERC20 tokens after initiating a cross-chain call. * @dev This function can be called on the source chain after calling the gateway to execute a remote contract. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param gasToken The ERC20 token address used to add gas * @param gasFeeAmount The amount of tokens to add as gas * @param refundAddress The address where refunds, if any, should be sent */ function addGas( bytes32 txHash, uint256 logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Add additional gas payment using native currency after initiating a cross-chain call. * @dev This function can be called on the source chain after calling the gateway to execute a remote contract. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param refundAddress The address where refunds, if any, should be sent */ function addNativeGas( bytes32 txHash, uint256 logIndex, address refundAddress ) external payable; /** * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call. * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param gasToken The ERC20 token address used to add gas * @param gasFeeAmount The amount of tokens to add as gas * @param refundAddress The address where refunds, if any, should be sent */ function addExpressGas( bytes32 txHash, uint256 logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Add additional gas payment using native currency after initiating an express cross-chain call. * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param refundAddress The address where refunds, if any, should be sent */ function addNativeExpressGas( bytes32 txHash, uint256 logIndex, address refundAddress ) external payable; /** * @notice Allows the gasCollector to collect accumulated fees from the contract. * @dev Use address(0) as the token address for native currency. * @param receiver The address to receive the collected fees * @param tokens Array of token addresses to be collected * @param amounts Array of amounts to be collected for each respective token address */ function collectFees( address payable receiver, address[] calldata tokens, uint256[] calldata amounts ) external; /** * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction. * @dev Only callable by the gasCollector. * @dev Use address(0) as the token address to refund native currency. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param receiver The address to receive the refund * @param token The token address to be refunded * @param amount The amount to refund */ function refund( bytes32 txHash, uint256 logIndex, address payable receiver, address token, uint256 amount ) external; /** * @notice Returns the address of the designated gas collector. * @return address of the gas collector */ function gasCollector() external returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IGovernable } from './IGovernable.sol'; import { IImplementation } from './IImplementation.sol'; interface IAxelarGateway is IImplementation, IGovernable { /**********\ |* Errors *| \**********/ error NotSelf(); error InvalidCodeHash(); error SetupFailed(); error InvalidAuthModule(); error InvalidTokenDeployer(); error InvalidAmount(); error InvalidChainId(); error InvalidCommands(); error TokenDoesNotExist(string symbol); error TokenAlreadyExists(string symbol); error TokenDeployFailed(string symbol); error TokenContractDoesNotExist(address token); error BurnFailed(string symbol); error MintFailed(string symbol); error InvalidSetMintLimitsParams(); error ExceedMintLimit(string symbol); /**********\ |* Events *| \**********/ event TokenSent( address indexed sender, string destinationChain, string destinationAddress, string symbol, uint256 amount ); event ContractCall( address indexed sender, string destinationChain, string destinationContractAddress, bytes32 indexed payloadHash, bytes payload ); event ContractCallWithToken( address indexed sender, string destinationChain, string destinationContractAddress, bytes32 indexed payloadHash, bytes payload, string symbol, uint256 amount ); event Executed(bytes32 indexed commandId); event TokenDeployed(string symbol, address tokenAddresses); event ContractCallApproved( bytes32 indexed commandId, string sourceChain, string sourceAddress, address indexed contractAddress, bytes32 indexed payloadHash, bytes32 sourceTxHash, uint256 sourceEventIndex ); event ContractCallApprovedWithMint( bytes32 indexed commandId, string sourceChain, string sourceAddress, address indexed contractAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, bytes32 sourceTxHash, uint256 sourceEventIndex ); event ContractCallExecuted(bytes32 indexed commandId); event TokenMintLimitUpdated(string symbol, uint256 limit); event OperatorshipTransferred(bytes newOperatorsData); event Upgraded(address indexed implementation); /********************\ |* Public Functions *| \********************/ function sendToken( string calldata destinationChain, string calldata destinationAddress, string calldata symbol, uint256 amount ) external; function callContract( string calldata destinationChain, string calldata contractAddress, bytes calldata payload ) external; function callContractWithToken( string calldata destinationChain, string calldata contractAddress, bytes calldata payload, string calldata symbol, uint256 amount ) external; function isContractCallApproved( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, address contractAddress, bytes32 payloadHash ) external view returns (bool); function isContractCallAndMintApproved( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, address contractAddress, bytes32 payloadHash, string calldata symbol, uint256 amount ) external view returns (bool); function validateContractCall( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash ) external returns (bool); function validateContractCallAndMint( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash, string calldata symbol, uint256 amount ) external returns (bool); /***********\ |* Getters *| \***********/ function authModule() external view returns (address); function tokenDeployer() external view returns (address); function tokenMintLimit(string memory symbol) external view returns (uint256); function tokenMintAmount(string memory symbol) external view returns (uint256); function allTokensFrozen() external view returns (bool); function implementation() external view returns (address); function tokenAddresses(string memory symbol) external view returns (address); function tokenFrozen(string memory symbol) external view returns (bool); function isCommandExecuted(bytes32 commandId) external view returns (bool); /************************\ |* Governance Functions *| \************************/ function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external; function upgrade( address newImplementation, bytes32 newImplementationCodeHash, bytes calldata setupParams ) external; /**********************\ |* External Functions *| \**********************/ function execute(bytes calldata input) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // General interface for upgradable contracts interface IContractIdentifier { /** * @notice Returns the contract ID. It can be used as a check during upgrades. * @dev Meant to be overridden in derived contracts. * @return bytes32 The contract ID */ function contractId() external pure returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title IGovernable Interface * @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles. */ interface IGovernable { error NotGovernance(); error NotMintLimiter(); error InvalidGovernance(); error InvalidMintLimiter(); event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance); event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance); /** * @notice Returns the governance address. * @return address of the governance */ function governance() external view returns (address); /** * @notice Returns the mint limiter address. * @return address of the mint limiter */ function mintLimiter() external view returns (address); /** * @notice Transfer the governance role to another address. * @param newGovernance The new governance address */ function transferGovernance(address newGovernance) external; /** * @notice Transfer the mint limiter role to another address. * @param newGovernance The new mint limiter address */ function transferMintLimiter(address newGovernance) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IContractIdentifier } from './IContractIdentifier.sol'; interface IImplementation is IContractIdentifier { error NotProxy(); function setup(bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title IOwnable Interface * @notice IOwnable is an interface that abstracts the implementation of a * contract with ownership control features. It's commonly used in upgradable * contracts and includes the functionality to get current owner, transfer * ownership, and propose and accept ownership. */ interface IOwnable { error NotOwner(); error InvalidOwner(); error InvalidOwnerAddress(); event OwnershipTransferStarted(address indexed newOwner); event OwnershipTransferred(address indexed newOwner); /** * @notice Returns the current owner of the contract. * @return address The address of the current owner */ function owner() external view returns (address); /** * @notice Returns the address of the pending owner of the contract. * @return address The address of the pending owner */ function pendingOwner() external view returns (address); /** * @notice Transfers ownership of the contract to a new address * @param newOwner The address to transfer ownership to */ function transferOwnership(address newOwner) external; /** * @notice Proposes to transfer the contract's ownership to a new address. * The new owner needs to accept the ownership explicitly. * @param newOwner The address to transfer ownership to */ function proposeOwnership(address newOwner) external; /** * @notice Transfers ownership to the pending owner. * @dev Can only be called by the pending owner */ function acceptOwnership() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IOwnable } from './IOwnable.sol'; import { IImplementation } from './IImplementation.sol'; // General interface for upgradable contracts interface IUpgradable is IOwnable, IImplementation { error InvalidCodeHash(); error InvalidImplementation(); error SetupFailed(); event Upgraded(address indexed newImplementation); function implementation() external view returns (address); function upgrade( address newImplementation, bytes32 newImplementationCodeHash, bytes calldata params ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IAny2EVMMessageReceiver} from "../interfaces/IAny2EVMMessageReceiver.sol"; import {Client} from "../libraries/Client.sol"; import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.0/utils/introspection/IERC165.sol"; /// @title CCIPReceiver - Base contract for CCIP applications that can receive messages. abstract contract CCIPReceiver is IAny2EVMMessageReceiver, IERC165 { address internal immutable i_router; constructor(address router) { if (router == address(0)) revert InvalidRouter(address(0)); i_router = router; } /// @notice IERC165 supports an interfaceId /// @param interfaceId The interfaceId to check /// @return true if the interfaceId is supported function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { return interfaceId == type(IAny2EVMMessageReceiver).interfaceId || interfaceId == type(IERC165).interfaceId; } /// @inheritdoc IAny2EVMMessageReceiver function ccipReceive(Client.Any2EVMMessage calldata message) external virtual override onlyRouter { _ccipReceive(message); } /// @notice Override this function in your implementation. /// @param message Any2EVMMessage function _ccipReceive(Client.Any2EVMMessage memory message) internal virtual; ///////////////////////////////////////////////////////////////////// // Plumbing ///////////////////////////////////////////////////////////////////// /// @notice Return the current router /// @return i_router address function getRouter() public view returns (address) { return address(i_router); } error InvalidRouter(address router); /// @dev only calls from the set router are accepted. modifier onlyRouter() { if (msg.sender != address(i_router)) revert InvalidRouter(msg.sender); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Client} from "../libraries/Client.sol"; /// @notice Application contracts that intend to receive messages from /// the router should implement this interface. interface IAny2EVMMessageReceiver { /// @notice Called by the Router to deliver a message. /// If this reverts, any token transfers also revert. The message /// will move to a FAILED state and become available for manual execution. /// @param message CCIP Message /// @dev Note ensure you check the msg.sender is the OffRampRouter function ccipReceive(Client.Any2EVMMessage calldata message) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Client} from "../libraries/Client.sol"; interface IRouterClient { error UnsupportedDestinationChain(uint64 destChainSelector); error InsufficientFeeTokenAmount(); error InvalidMsgValue(); /// @notice Checks if the given chain ID is supported for sending/receiving. /// @param chainSelector The chain to check. /// @return supported is true if it is supported, false if not. function isChainSupported(uint64 chainSelector) external view returns (bool supported); /// @notice Gets a list of all supported tokens which can be sent or received /// to/from a given chain id. /// @param chainSelector The chainSelector. /// @return tokens The addresses of all tokens that are supported. function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens); /// @param destinationChainSelector The destination chainSelector /// @param message The cross-chain CCIP message including data and/or tokens /// @return fee returns guaranteed execution fee for the specified message /// delivery to destination chain /// @dev returns 0 fee on invalid message. function getFee( uint64 destinationChainSelector, Client.EVM2AnyMessage memory message ) external view returns (uint256 fee); /// @notice Request a message to be sent to the destination chain /// @param destinationChainSelector The destination chain ID /// @param message The cross-chain CCIP message including data and/or tokens /// @return messageId The message ID /// @dev Note if msg.value is larger than the required fee (from getFee) we accept /// the overpayment with no refund. function ccipSend( uint64 destinationChainSelector, Client.EVM2AnyMessage calldata message ) external payable returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // End consumer library. library Client { struct EVMTokenAmount { address token; // token address on the local chain. uint256 amount; // Amount of tokens. } struct Any2EVMMessage { bytes32 messageId; // MessageId corresponding to ccipSend on source. uint64 sourceChainSelector; // Source chain selector. bytes sender; // abi.decode(sender) if coming from an EVM chain. bytes data; // payload sent in original message. EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation. } // If extraArgs is empty bytes, the default is 200k gas limit and strict = false. struct EVM2AnyMessage { bytes receiver; // abi.encode(receiver address) for dest EVM chains bytes data; // Data payload EVMTokenAmount[] tokenAmounts; // Token transfers address feeToken; // Address of feeToken. address(0) means you will send msg.value. bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1) } // extraArgs will evolve to support new features // bytes4(keccak256("CCIP EVMExtraArgsV1")); bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9; struct EVMExtraArgsV1 { uint256 gasLimit; // ATTENTION!!! MAX GAS LIMIT 4M FOR BETA TESTING bool strict; // See strict sequencing details below. } function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) { return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import {ExecuteArgs, TransferInfo, DestinationTransferStatus} from "../libraries/LibConnextStorage.sol"; import {TokenId} from "../libraries/TokenId.sol"; interface IConnext { // ============ BRIDGE ============== function xcall( uint32 _destination, address _to, address _asset, address _delegate, uint256 _amount, uint256 _slippage, bytes calldata _callData ) external payable returns (bytes32); function xcallIntoLocal( uint32 _destination, address _to, address _asset, address _delegate, uint256 _amount, uint256 _slippage, bytes calldata _callData ) external payable returns (bytes32); function execute(ExecuteArgs calldata _args) external returns (bytes32 transferId); function forceUpdateSlippage(TransferInfo calldata _params, uint256 _slippage) external; function forceReceiveLocal(TransferInfo calldata _params) external; function bumpTransfer(bytes32 _transferId) external payable; function routedTransfers(bytes32 _transferId) external view returns (address[] memory); function transferStatus(bytes32 _transferId) external view returns (DestinationTransferStatus); function remote(uint32 _domain) external view returns (address); function domain() external view returns (uint256); function nonce() external view returns (uint256); function approvedSequencers(address _sequencer) external view returns (bool); function xAppConnectionManager() external view returns (address); // ============ ROUTERS ============== function LIQUIDITY_FEE_NUMERATOR() external view returns (uint256); function LIQUIDITY_FEE_DENOMINATOR() external view returns (uint256); function getRouterApproval(address _router) external view returns (bool); function getRouterRecipient(address _router) external view returns (address); function getRouterOwner(address _router) external view returns (address); function getProposedRouterOwner(address _router) external view returns (address); function getProposedRouterOwnerTimestamp(address _router) external view returns (uint256); function maxRoutersPerTransfer() external view returns (uint256); function routerBalances(address _router, address _asset) external view returns (uint256); function getRouterApprovalForPortal(address _router) external view returns (bool); function initializeRouter(address _owner, address _recipient) external; function setRouterRecipient(address _router, address _recipient) external; function proposeRouterOwner(address _router, address _proposed) external; function acceptProposedRouterOwner(address _router) external; function addRouterLiquidityFor( uint256 _amount, address _local, address _router ) external payable; function addRouterLiquidity(uint256 _amount, address _local) external payable; function removeRouterLiquidityFor( TokenId memory _canonical, uint256 _amount, address payable _to, address _router ) external; function removeRouterLiquidity(TokenId memory _canonical, uint256 _amount, address payable _to) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; interface IXReceiver { function xReceive( bytes32 _transferId, uint256 _amount, address _asset, address _originSender, uint32 _origin, bytes memory _callData ) external returns (bytes memory); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; /** * @notice Enum representing status of destination transfer * @dev Status is only assigned on the destination domain, will always be "none" for the * origin domains * @return uint - Index of value in enum */ enum DestinationTransferStatus { None, // 0 Reconciled, // 1 Executed, // 2 Completed // 3 - executed + reconciled } /** * @notice These are the parameters that will remain constant between the * two chains. They are supplied on `xcall` and should be asserted on `execute` * @property to - The account that receives funds, in the event of a crosschain call, * will receive funds if the call fails. * * @param originDomain - The originating domain (i.e. where `xcall` is called) * @param destinationDomain - The final domain (i.e. where `execute` / `reconcile` are called)\ * @param canonicalDomain - The canonical domain of the asset you are bridging * @param to - The address you are sending funds (and potentially data) to * @param delegate - An address who can execute txs on behalf of `to`, in addition to allowing relayers * @param receiveLocal - If true, will use the local asset on the destination instead of adopted. * @param callData - The data to execute on the receiving chain. If no crosschain call is needed, then leave empty. * @param slippage - Slippage user is willing to accept from original amount in expressed in BPS (i.e. if * a user takes 1% slippage, this is expressed as 1_000) * @param originSender - The msg.sender of the xcall * @param bridgedAmt - The amount sent over the bridge (after potential AMM on xcall) * @param normalizedIn - The amount sent to `xcall`, normalized to 18 decimals * @param nonce - The nonce on the origin domain used to ensure the transferIds are unique * @param canonicalId - The unique identifier of the canonical token corresponding to bridge assets */ struct TransferInfo { uint32 originDomain; uint32 destinationDomain; uint32 canonicalDomain; address to; address delegate; bool receiveLocal; bytes callData; uint256 slippage; address originSender; uint256 bridgedAmt; uint256 normalizedIn; uint256 nonce; bytes32 canonicalId; } /** * @notice * @param params - The TransferInfo. These are consistent across sending and receiving chains. * @param routers - The routers who you are sending the funds on behalf of. * @param routerSignatures - Signatures belonging to the routers indicating permission to use funds * for the signed transfer ID. * @param sequencer - The sequencer who assigned the router path to this transfer. * @param sequencerSignature - Signature produced by the sequencer for path assignment accountability * for the path that was signed. */ struct ExecuteArgs { TransferInfo params; address[] routers; bytes[] routerSignatures; address sequencer; bytes sequencerSignature; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.17; // ============= Structs ============= // Tokens are identified by a TokenId: // domain - 4 byte chain ID of the chain from which the token originates // id - 32 byte identifier of the token address on the origin chain, in that chain's address format struct TokenId { uint32 domain; bytes32 id; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; /*@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@ HYPERLANE @@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@*/ interface IPostDispatchHook { enum Types { UNUSED, ROUTING, AGGREGATION, MERKLE_TREE, INTERCHAIN_GAS_PAYMASTER, FALLBACK_ROUTING, ID_AUTH_ISM, PAUSABLE, PROTOCOL_FEE } /** * @notice Returns an enum that represents the type of hook */ function hookType() external view returns (uint8); /** * @notice Returns whether the hook supports metadata * @param metadata metadata * @return Whether the hook supports metadata */ function supportsMetadata( bytes calldata metadata ) external view returns (bool); /** * @notice Post action after a message is dispatched via the Mailbox * @param metadata The metadata required for the hook * @param message The message passed from the Mailbox.dispatch() call */ function postDispatch( bytes calldata metadata, bytes calldata message ) external payable; /** * @notice Compute the payment required by the postDispatch call * @param metadata The metadata required for the hook * @param message The message passed from the Mailbox.dispatch() call * @return Quoted payment for the postDispatch call */ function quoteDispatch( bytes calldata metadata, bytes calldata message ) external view returns (uint256); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.6.11; interface IInterchainSecurityModule { enum Types { UNUSED, ROUTING, AGGREGATION, LEGACY_MULTISIG, MERKLE_ROOT_MULTISIG, MESSAGE_ID_MULTISIG, NULL, // used with relayer carrying no metadata CCIP_READ } /** * @notice Returns an enum that represents the type of security model * encoded by this ISM. * @dev Relayers infer how to fetch and format metadata. */ function moduleType() external view returns (uint8); /** * @notice Defines a security model responsible for verifying interchain * messages based on the provided metadata. * @param _metadata Off-chain metadata provided by a relayer, specific to * the security model encoded by the module (e.g. validator signatures) * @param _message Hyperlane encoded interchain message * @return True if the message was verified */ function verify( bytes calldata _metadata, bytes calldata _message ) external returns (bool); } interface ISpecifiesInterchainSecurityModule { function interchainSecurityModule() external view returns (IInterchainSecurityModule); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; import {IInterchainSecurityModule} from "./IInterchainSecurityModule.sol"; import {IPostDispatchHook} from "./hooks/IPostDispatchHook.sol"; interface IMailbox { // ============ Events ============ /** * @notice Emitted when a new message is dispatched via Hyperlane * @param sender The address that dispatched the message * @param destination The destination domain of the message * @param recipient The message recipient address on `destination` * @param message Raw bytes of message */ event Dispatch( address indexed sender, uint32 indexed destination, bytes32 indexed recipient, bytes message ); /** * @notice Emitted when a new message is dispatched via Hyperlane * @param messageId The unique message identifier */ event DispatchId(bytes32 indexed messageId); /** * @notice Emitted when a Hyperlane message is processed * @param messageId The unique message identifier */ event ProcessId(bytes32 indexed messageId); /** * @notice Emitted when a Hyperlane message is delivered * @param origin The origin domain of the message * @param sender The message sender address on `origin` * @param recipient The address that handled the message */ event Process( uint32 indexed origin, bytes32 indexed sender, address indexed recipient ); function localDomain() external view returns (uint32); function delivered(bytes32 messageId) external view returns (bool); function defaultIsm() external view returns (IInterchainSecurityModule); function defaultHook() external view returns (IPostDispatchHook); function requiredHook() external view returns (IPostDispatchHook); function latestDispatchedId() external view returns (bytes32); function dispatch( uint32 destinationDomain, bytes32 recipientAddress, bytes calldata messageBody ) external payable returns (bytes32 messageId); function quoteDispatch( uint32 destinationDomain, bytes32 recipientAddress, bytes calldata messageBody ) external view returns (uint256 fee); function dispatch( uint32 destinationDomain, bytes32 recipientAddress, bytes calldata body, bytes calldata defaultHookMetadata ) external payable returns (bytes32 messageId); function quoteDispatch( uint32 destinationDomain, bytes32 recipientAddress, bytes calldata messageBody, bytes calldata defaultHookMetadata ) external view returns (uint256 fee); function dispatch( uint32 destinationDomain, bytes32 recipientAddress, bytes calldata body, bytes calldata customHookMetadata, IPostDispatchHook customHook ) external payable returns (bytes32 messageId); function quoteDispatch( uint32 destinationDomain, bytes32 recipientAddress, bytes calldata messageBody, bytes calldata customHookMetadata, IPostDispatchHook customHook ) external view returns (uint256 fee); function process( bytes calldata metadata, bytes calldata message ) external payable; function recipientIsm( address recipient ) external view returns (IInterchainSecurityModule module); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.6.11; interface IMessageRecipient { function handle( uint32 _origin, bytes32 _sender, bytes calldata _message ) external payable; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.6.11; library TypeCasts { // alignment preserving cast function addressToBytes32(address _addr) internal pure returns (bytes32) { return bytes32(uint256(uint160(_addr))); } // alignment preserving cast function bytes32ToAddress(bytes32 _buf) internal pure returns (address) { return address(uint160(uint256(_buf))); } }
// SPDX-License-Identifier: LZBL-1.2 pragma solidity ^0.8.20; import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol"; import { BitMap256 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol"; import { CalldataBytesLib } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol"; library DVNOptions { using CalldataBytesLib for bytes; using BytesLib for bytes; uint8 internal constant WORKER_ID = 2; uint8 internal constant OPTION_TYPE_PRECRIME = 1; error DVN_InvalidDVNIdx(); error DVN_InvalidDVNOptions(uint256 cursor); /// @dev group dvn options by its idx /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]... /// dvn_option = [option_size][dvn_idx][option_type][option] /// option_size = len(dvn_idx) + len(option_type) + len(option) /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes /// @return dvnOptions the grouped options, still share the same format of _options /// @return dvnIndices the dvn indices function groupDVNOptionsByIdx( bytes memory _options ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) { if (_options.length == 0) return (dvnOptions, dvnIndices); uint8 numDVNs = getNumDVNs(_options); // if there is only 1 dvn, we can just return the whole options if (numDVNs == 1) { dvnOptions = new bytes[](1); dvnOptions[0] = _options; dvnIndices = new uint8[](1); dvnIndices[0] = _options.toUint8(3); // dvn idx return (dvnOptions, dvnIndices); } // otherwise, we need to group the options by dvn_idx dvnIndices = new uint8[](numDVNs); dvnOptions = new bytes[](numDVNs); unchecked { uint256 cursor = 0; uint256 start = 0; uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx while (cursor < _options.length) { ++cursor; // skip worker_id // optionLength asserted in getNumDVNs (skip check) uint16 optionLength = _options.toUint16(cursor); cursor += 2; // dvnIdx asserted in getNumDVNs (skip check) uint8 dvnIdx = _options.toUint8(cursor); // dvnIdx must equal to the lastDVNIdx for the first option // so it is always skipped in the first option // this operation slices out options whenever the scan finds a different lastDVNIdx if (lastDVNIdx == 255) { lastDVNIdx = dvnIdx; } else if (dvnIdx != lastDVNIdx) { uint256 len = cursor - start - 3; // 3 is for worker_id and option_length bytes memory opt = _options.slice(start, len); _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt); // reset the start and lastDVNIdx start += len; lastDVNIdx = dvnIdx; } cursor += optionLength; } // skip check the cursor here because the cursor is asserted in getNumDVNs // if we have reached the end of the options, we need to process the last dvn uint256 size = cursor - start; bytes memory op = _options.slice(start, size); _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op); // revert dvnIndices to start from 0 for (uint8 i = 0; i < numDVNs; ++i) { --dvnIndices[i]; } } } function _insertDVNOptions( bytes[] memory _dvnOptions, uint8[] memory _dvnIndices, uint8 _dvnIdx, bytes memory _newOptions ) internal pure { // dvnIdx starts from 0 but default value of dvnIndices is 0, // so we tell if the slot is empty by adding 1 to dvnIdx if (_dvnIdx == 255) revert DVN_InvalidDVNIdx(); uint8 dvnIdxAdj = _dvnIdx + 1; for (uint256 j = 0; j < _dvnIndices.length; ++j) { uint8 index = _dvnIndices[j]; if (dvnIdxAdj == index) { _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions); break; } else if (index == 0) { // empty slot, that means it is the first time we see this dvn _dvnIndices[j] = dvnIdxAdj; _dvnOptions[j] = _newOptions; break; } } } /// @dev get the number of unique dvns /// @param _options the format is the same as groupDVNOptionsByIdx function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) { uint256 cursor = 0; BitMap256 bitmap; // find number of unique dvn_idx unchecked { while (cursor < _options.length) { ++cursor; // skip worker_id uint16 optionLength = _options.toUint16(cursor); cursor += 2; if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type uint8 dvnIdx = _options.toUint8(cursor); // if dvnIdx is not set, increment numDVNs // max num of dvns is 255, 255 is an invalid dvn_idx // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has // already enforced certain options can append additional options to the end of the enforced // ones without restrictions. if (dvnIdx == 255) revert DVN_InvalidDVNIdx(); if (!bitmap.get(dvnIdx)) { ++numDVNs; bitmap = bitmap.set(dvnIdx); } cursor += optionLength; } } if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor); } /// @dev decode the next dvn option from _options starting from the specified cursor /// @param _options the format is the same as groupDVNOptionsByIdx /// @param _cursor the cursor to start decoding /// @return optionType the type of the option /// @return option the option /// @return cursor the cursor to start decoding the next option function nextDVNOption( bytes calldata _options, uint256 _cursor ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) { unchecked { // skip worker id cursor = _cursor + 1; // read option size uint16 size = _options.toU16(cursor); cursor += 2; // read option type optionType = _options.toU8(cursor + 1); // skip dvn_idx // startCursor and endCursor are used to slice the option from _options uint256 startCursor = cursor + 2; // skip option type and dvn_idx uint256 endCursor = cursor + size; option = _options[startCursor:endCursor]; cursor += size; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; /** * @title IOAppCore */ interface IOAppCore { // Custom error messages error OnlyPeer(uint32 eid, bytes32 sender); error NoPeer(uint32 eid); error InvalidEndpointCall(); error InvalidDelegate(); // Event emitted when a peer (OApp) is set for a corresponding endpoint event PeerSet(uint32 eid, bytes32 peer); /** * @notice Retrieves the OApp version information. * @return senderVersion The version of the OAppSender.sol contract. * @return receiverVersion The version of the OAppReceiver.sol contract. */ function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion); /** * @notice Retrieves the LayerZero endpoint associated with the OApp. * @return iEndpoint The LayerZero endpoint as an interface. */ function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint); /** * @notice Retrieves the peer (OApp) associated with a corresponding endpoint. * @param _eid The endpoint ID. * @return peer The peer address (OApp instance) associated with the corresponding endpoint. */ function peers(uint32 _eid) external view returns (bytes32 peer); /** * @notice Sets the peer address (OApp instance) for a corresponding endpoint. * @param _eid The endpoint ID. * @param _peer The address of the peer to be associated with the corresponding endpoint. */ function setPeer(uint32 _eid, bytes32 _peer) external; /** * @notice Sets the delegate address for the OApp Core. * @param _delegate The address of the delegate to be set. */ function setDelegate(address _delegate) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import { ExecutorOptions } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol"; import { DVNOptions } from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol"; /** * @title OptionsBuilder * @dev Library for building and encoding various message options. */ library OptionsBuilder { using SafeCast for uint256; using BytesLib for bytes; // Constants for options types uint16 internal constant TYPE_1 = 1; // legacy options type 1 uint16 internal constant TYPE_2 = 2; // legacy options type 2 uint16 internal constant TYPE_3 = 3; // Custom error message error InvalidSize(uint256 max, uint256 actual); error InvalidOptionType(uint16 optionType); // Modifier to ensure only options of type 3 are used modifier onlyType3(bytes memory _options) { if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0)); _; } /** * @dev Creates a new options container with type 3. * @return options The newly created options container. */ function newOptions() internal pure returns (bytes memory) { return abi.encodePacked(TYPE_3); } /** * @dev Adds an executor LZ receive option to the existing options. * @param _options The existing options container. * @param _gas The gasLimit used on the lzReceive() function in the OApp. * @param _value The msg.value passed to the lzReceive() function in the OApp. * @return options The updated options container. * * @dev When multiples of this option are added, they are summed by the executor * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint, * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function. */ function addExecutorLzReceiveOption( bytes memory _options, uint128 _gas, uint128 _value ) internal pure onlyType3(_options) returns (bytes memory) { bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value); return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option); } /** * @dev Adds an executor native drop option to the existing options. * @param _options The existing options container. * @param _amount The amount for the native value that is airdropped to the 'receiver'. * @param _receiver The receiver address for the native drop option. * @return options The updated options container. * * @dev When multiples of this option are added, they are summed by the executor on the remote chain. */ function addExecutorNativeDropOption( bytes memory _options, uint128 _amount, bytes32 _receiver ) internal pure onlyType3(_options) returns (bytes memory) { bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver); return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option); } /** * @dev Adds an executor LZ compose option to the existing options. * @param _options The existing options container. * @param _index The index for the lzCompose() function call. * @param _gas The gasLimit for the lzCompose() function call. * @param _value The msg.value for the lzCompose() function call. * @return options The updated options container. * * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain. * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0. * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2 */ function addExecutorLzComposeOption( bytes memory _options, uint16 _index, uint128 _gas, uint128 _value ) internal pure onlyType3(_options) returns (bytes memory) { bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value); return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option); } /** * @dev Adds an executor ordered execution option to the existing options. * @param _options The existing options container. * @return options The updated options container. */ function addExecutorOrderedExecutionOption( bytes memory _options ) internal pure onlyType3(_options) returns (bytes memory) { return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes("")); } /** * @dev Adds a DVN pre-crime option to the existing options. * @param _options The existing options container. * @param _dvnIdx The DVN index for the pre-crime option. * @return options The updated options container. */ function addDVNPreCrimeOption( bytes memory _options, uint8 _dvnIdx ) internal pure onlyType3(_options) returns (bytes memory) { return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes("")); } /** * @dev Adds an executor option to the existing options. * @param _options The existing options container. * @param _optionType The type of the executor option. * @param _option The encoded data for the executor option. * @return options The updated options container. */ function addExecutorOption( bytes memory _options, uint8 _optionType, bytes memory _option ) internal pure onlyType3(_options) returns (bytes memory) { return abi.encodePacked( _options, ExecutorOptions.WORKER_ID, _option.length.toUint16() + 1, // +1 for optionType _optionType, _option ); } /** * @dev Adds a DVN option to the existing options. * @param _options The existing options container. * @param _dvnIdx The DVN index for the DVN option. * @param _optionType The type of the DVN option. * @param _option The encoded data for the DVN option. * @return options The updated options container. */ function addDVNOption( bytes memory _options, uint8 _dvnIdx, uint8 _optionType, bytes memory _option ) internal pure onlyType3(_options) returns (bytes memory) { return abi.encodePacked( _options, DVNOptions.WORKER_ID, _option.length.toUint16() + 2, // +2 for optionType and dvnIdx _dvnIdx, _optionType, _option ); } /** * @dev Encodes legacy options of type 1. * @param _executionGas The gasLimit value passed to lzReceive(). * @return legacyOptions The encoded legacy options. */ function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) { if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas); return abi.encodePacked(TYPE_1, _executionGas); } /** * @dev Encodes legacy options of type 2. * @param _executionGas The gasLimit value passed to lzReceive(). * @param _nativeForDst The amount of native air dropped to the receiver. * @param _receiver The _nativeForDst receiver address. * @return legacyOptions The encoded legacy options of type 2. */ function encodeLegacyOptionsType2( uint256 _executionGas, uint256 _nativeForDst, bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver. ) internal pure returns (bytes memory) { if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas); if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst); if (_receiver.length > 32) revert InvalidSize(32, _receiver.length); return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol"; /** * @title OAppCore * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations. */ abstract contract OAppCore is IOAppCore, Ownable { // The LayerZero endpoint associated with the given OApp ILayerZeroEndpointV2 public immutable endpoint; // Mapping to store peers associated with corresponding endpoints mapping(uint32 eid => bytes32 peer) public peers; /** * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate. * @param _endpoint The address of the LOCAL Layer Zero endpoint. * @param _delegate The delegate capable of making OApp configurations inside of the endpoint. * * @dev The delegate typically should be set as the owner of the contract. */ constructor(address _endpoint, address _delegate) { endpoint = ILayerZeroEndpointV2(_endpoint); if (_delegate == address(0)) revert InvalidDelegate(); endpoint.setDelegate(_delegate); } /** * @notice Sets the peer address (OApp instance) for a corresponding endpoint. * @param _eid The endpoint ID. * @param _peer The address of the peer to be associated with the corresponding endpoint. * * @dev Only the owner/admin of the OApp can call this function. * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp. * @dev Set this to bytes32(0) to remove the peer address. * @dev Peer is a bytes32 to accommodate non-evm chains. */ function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner { _setPeer(_eid, _peer); } /** * @notice Sets the peer address (OApp instance) for a corresponding endpoint. * @param _eid The endpoint ID. * @param _peer The address of the peer to be associated with the corresponding endpoint. * * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp. * @dev Set this to bytes32(0) to remove the peer address. * @dev Peer is a bytes32 to accommodate non-evm chains. */ function _setPeer(uint32 _eid, bytes32 _peer) internal virtual { peers[_eid] = _peer; emit PeerSet(_eid, _peer); } /** * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set. * ie. the peer is set to bytes32(0). * @param _eid The endpoint ID. * @return peer The address of the peer associated with the specified endpoint. */ function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) { bytes32 peer = peers[_eid]; if (peer == bytes32(0)) revert NoPeer(_eid); return peer; } /** * @notice Sets the delegate address for the OApp. * @param _delegate The address of the delegate to be set. * * @dev Only the owner/admin of the OApp can call this function. * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract. */ function setDelegate(address _delegate) public onlyOwner { endpoint.setDelegate(_delegate); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import { IMessageLibManager } from "./IMessageLibManager.sol"; import { IMessagingComposer } from "./IMessagingComposer.sol"; import { IMessagingChannel } from "./IMessagingChannel.sol"; import { IMessagingContext } from "./IMessagingContext.sol"; struct MessagingParams { uint32 dstEid; bytes32 receiver; bytes message; bytes options; bool payInLzToken; } struct MessagingReceipt { bytes32 guid; uint64 nonce; MessagingFee fee; } struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; } struct Origin { uint32 srcEid; bytes32 sender; uint64 nonce; } interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext { event PacketSent(bytes encodedPayload, bytes options, address sendLibrary); event PacketVerified(Origin origin, address receiver, bytes32 payloadHash); event PacketDelivered(Origin origin, address receiver); event LzReceiveAlert( address indexed receiver, address indexed executor, Origin origin, bytes32 guid, uint256 gas, uint256 value, bytes message, bytes extraData, bytes reason ); event LzTokenSet(address token); event DelegateSet(address sender, address delegate); function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory); function send( MessagingParams calldata _params, address _refundAddress ) external payable returns (MessagingReceipt memory); function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external; function verifiable(Origin calldata _origin, address _receiver) external view returns (bool); function initializable(Origin calldata _origin, address _receiver) external view returns (bool); function lzReceive( Origin calldata _origin, address _receiver, bytes32 _guid, bytes calldata _message, bytes calldata _extraData ) external payable; // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external; function setLzToken(address _lzToken) external; function lzToken() external view returns (address); function nativeToken() external view returns (address); function setDelegate(address _delegate) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; struct SetConfigParam { uint32 eid; uint32 configType; bytes config; } interface IMessageLibManager { struct Timeout { address lib; uint256 expiry; } event LibraryRegistered(address newLib); event DefaultSendLibrarySet(uint32 eid, address newLib); event DefaultReceiveLibrarySet(uint32 eid, address newLib); event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry); event SendLibrarySet(address sender, uint32 eid, address newLib); event ReceiveLibrarySet(address receiver, uint32 eid, address newLib); event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout); function registerLibrary(address _lib) external; function isRegisteredLibrary(address _lib) external view returns (bool); function getRegisteredLibraries() external view returns (address[] memory); function setDefaultSendLibrary(uint32 _eid, address _newLib) external; function defaultSendLibrary(uint32 _eid) external view returns (address); function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external; function defaultReceiveLibrary(uint32 _eid) external view returns (address); function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external; function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry); function isSupportedEid(uint32 _eid) external view returns (bool); function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool); /// ------------------- OApp interfaces ------------------- function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external; function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib); function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool); function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external; function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault); function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external; function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry); function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external; function getConfig( address _oapp, address _lib, uint32 _eid, uint32 _configType ) external view returns (bytes memory config); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IMessagingChannel { event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce); event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash); event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash); function eid() external view returns (uint32); // this is an emergency function if a message cannot be verified for some reasons // required to provide _nextNonce to avoid race condition function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external; function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external; function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external; function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32); function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64); function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64); function inboundPayloadHash( address _receiver, uint32 _srcEid, bytes32 _sender, uint64 _nonce ) external view returns (bytes32); function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IMessagingComposer { event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message); event ComposeDelivered(address from, address to, bytes32 guid, uint16 index); event LzComposeAlert( address indexed from, address indexed to, address indexed executor, bytes32 guid, uint16 index, uint256 gas, uint256 value, bytes message, bytes extraData, bytes reason ); function composeQueue( address _from, address _to, bytes32 _guid, uint16 _index ) external view returns (bytes32 messageHash); function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external; function lzCompose( address _from, address _to, bytes32 _guid, uint16 _index, bytes calldata _message, bytes calldata _extraData ) external payable; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IMessagingContext { function isSendingMessage() external view returns (bool); function getSendContext() external view returns (uint32 dstEid, address sender); }
// SPDX-License-Identifier: LZBL-1.2 pragma solidity ^0.8.20; library CalldataBytesLib { function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) { return uint8(_bytes[_start]); } function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) { unchecked { uint256 end = _start + 2; return uint16(bytes2(_bytes[_start:end])); } } function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) { unchecked { uint256 end = _start + 4; return uint32(bytes4(_bytes[_start:end])); } } function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) { unchecked { uint256 end = _start + 8; return uint64(bytes8(_bytes[_start:end])); } } function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) { unchecked { uint256 end = _start + 16; return uint128(bytes16(_bytes[_start:end])); } } function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) { unchecked { uint256 end = _start + 32; return uint256(bytes32(_bytes[_start:end])); } } function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) { unchecked { uint256 end = _start + 20; return address(bytes20(_bytes[_start:end])); } } function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) { unchecked { uint256 end = _start + 32; return bytes32(_bytes[_start:end]); } } }
// SPDX-License-Identifier: MIT // modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol pragma solidity ^0.8.20; type BitMap256 is uint256; using BitMaps for BitMap256 global; library BitMaps { /** * @dev Returns whether the bit at `index` is set. */ function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) { uint256 mask = 1 << index; return BitMap256.unwrap(bitmap) & mask != 0; } /** * @dev Sets the bit at `index`. */ function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) { uint256 mask = 1 << index; return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask); } }
// SPDX-License-Identifier: LZBL-1.2 pragma solidity ^0.8.20; import { CalldataBytesLib } from "../../libs/CalldataBytesLib.sol"; library ExecutorOptions { using CalldataBytesLib for bytes; uint8 internal constant WORKER_ID = 1; uint8 internal constant OPTION_TYPE_LZRECEIVE = 1; uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2; uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3; uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4; error Executor_InvalidLzReceiveOption(); error Executor_InvalidNativeDropOption(); error Executor_InvalidLzComposeOption(); /// @dev decode the next executor option from the options starting from the specified cursor /// @param _options [executor_id][executor_option][executor_id][executor_option]... /// executor_option = [option_size][option_type][option] /// option_size = len(option_type) + len(option) /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes /// @param _cursor the cursor to start decoding from /// @return optionType the type of the option /// @return option the option of the executor /// @return cursor the cursor to start decoding the next executor option function nextExecutorOption( bytes calldata _options, uint256 _cursor ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) { unchecked { // skip worker id cursor = _cursor + 1; // read option size uint16 size = _options.toU16(cursor); cursor += 2; // read option type optionType = _options.toU8(cursor); // startCursor and endCursor are used to slice the option from _options uint256 startCursor = cursor + 1; // skip option type uint256 endCursor = cursor + size; option = _options[startCursor:endCursor]; cursor += size; } } function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) { if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption(); gas = _option.toU128(0); value = _option.length == 32 ? _option.toU128(16) : 0; } function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) { if (_option.length != 48) revert Executor_InvalidNativeDropOption(); amount = _option.toU128(0); receiver = _option.toB32(16); } function decodeLzComposeOption( bytes calldata _option ) internal pure returns (uint16 index, uint128 gas, uint128 value) { if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption(); index = _option.toU16(0); gas = _option.toU128(2); value = _option.length == 34 ? _option.toU128(18) : 0; } function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) { return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value); } function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) { return abi.encodePacked(_amount, _receiver); } function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) { return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Registry.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC1820RegistryUpgradeable.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/introspection/IERC1820Registry.sol) pragma solidity ^0.8.0; /** * @dev Interface of the global ERC1820 Registry, as defined in the * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register * implementers for interfaces in this registry, as well as query support. * * Implementers may be shared by multiple accounts, and can also implement more * than a single interface for each account. Contracts can implement interfaces * for themselves, but externally-owned accounts (EOA) must delegate this to a * contract. * * {IERC165} interfaces can also be queried via the registry. * * For an in-depth explanation and source code analysis, see the EIP text. */ interface IERC1820RegistryUpgradeable { event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); event ManagerChanged(address indexed account, address indexed newManager); /** * @dev Sets `newManager` as the manager for `account`. A manager of an * account is able to set interface implementers for it. * * By default, each account is its own manager. Passing a value of `0x0` in * `newManager` will reset the manager to this initial state. * * Emits a {ManagerChanged} event. * * Requirements: * * - the caller must be the current manager for `account`. */ function setManager(address account, address newManager) external; /** * @dev Returns the manager for `account`. * * See {setManager}. */ function getManager(address account) external view returns (address); /** * @dev Sets the `implementer` contract as ``account``'s implementer for * `interfaceHash`. * * `account` being the zero address is an alias for the caller's address. * The zero address can also be used in `implementer` to remove an old one. * * See {interfaceHash} to learn how these are created. * * Emits an {InterfaceImplementerSet} event. * * Requirements: * * - the caller must be the current manager for `account`. * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not * end in 28 zeroes). * - `implementer` must implement {IERC1820Implementer} and return true when * queried for support, unless `implementer` is the caller. See * {IERC1820Implementer-canImplementInterfaceForAddress}. */ function setInterfaceImplementer( address account, bytes32 _interfaceHash, address implementer ) external; /** * @dev Returns the implementer of `interfaceHash` for `account`. If no such * implementer is registered, returns the zero address. * * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 * zeroes), `account` will be queried for support of it. * * `account` being the zero address is an alias for the caller's address. */ function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address); /** * @dev Returns the interface hash for an `interfaceName`, as defined in the * corresponding * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. */ function interfaceHash(string calldata interfaceName) external pure returns (bytes32); /** * @notice Updates the cache with whether the contract implements an ERC165 interface or not. * @param account Address of the contract for which to update the cache. * @param interfaceId ERC165 interface for which to update the cache. */ function updateERC165Cache(address account, bytes4 interfaceId) external; /** * @notice Checks whether a contract implements an ERC165 interface or not. * If the result is not cached a direct lookup on the contract address is performed. * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling * {updateERC165Cache} with the contract address. * @param account Address of the contract to check. * @param interfaceId ERC165 interface to check. * @return True if `account` implements `interfaceId`, false otherwise. */ function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); /** * @notice Checks whether a contract implements an ERC165 interface or not without using or updating the cache. * @param account Address of the contract to check. * @param interfaceId ERC165 interface to check. * @return True if `account` implements `interfaceId`, false otherwise. */ function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControl.sol"; import "../utils/Context.sol"; import "../utils/Strings.sol"; import "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `_msgSender()` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. * * Format of the revert message is described in {_checkRole}. * * _Available since v4.6._ */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(account), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @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. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @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 revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * May emit a {RoleGranted} event. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @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. * * _Available since v3.1._ */ 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 `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Recipient.sol) pragma solidity ^0.8.0; import "../token/ERC777/IERC777Recipient.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 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: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ 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 amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` 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 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 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 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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @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.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @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, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @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.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. * * Accounts can be notified of {IERC777} tokens being sent to them by having a * contract implement this interface (contract holders can be their own * implementer) and registering it on the * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. * * See {IERC1820Registry} and {ERC1820Implementer}. */ interface IERC777Recipient { /** * @dev Called by an {IERC777} token contract whenever tokens are being * moved or created into a registered account (`to`). The type of operation * is conveyed by `from` being the zero address or not. * * This call occurs _after_ the token contract's state is updated, so * {IERC777-balanceOf}, etc., can be used to query the post-operation state. * * This function may revert to prevent the operation from being executed. */ function tokensReceived( address operator, address from, address to, uint256 amount, bytes calldata userData, bytes calldata operatorData ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import {PostRequest, StateMachineHeight} from "./Message.sol"; // @notice An object for dispatching post requests to the Hyperbridge struct DispatchPost { // bytes representation of the destination state machine bytes dest; // the destination module bytes to; // the request body bytes body; // timeout for this request in seconds uint64 timeout; // the amount put up to be paid to the relayer, // this is charged in `IIsmpHost.feeToken` to `msg.sender` uint256 fee; // who pays for this request? address payer; } // @notice An object for dispatching get requests to the Hyperbridge struct DispatchGet { // bytes representation of the destination state machine bytes dest; // height at which to read the state machine uint64 height; // storage keys to read bytes[] keys; // timeout for this request in seconds uint64 timeout; // Hyperbridge protocol fees for processing this request. uint256 fee; // Some application-specific metadata relating to this request bytes context; } struct DispatchPostResponse { // The request that initiated this response PostRequest request; // bytes for post response bytes response; // timeout for this response in seconds uint64 timeout; // the amount put up to be paid to the relayer, // this is charged in `IIsmpHost.feeToken` to `msg.sender` uint256 fee; // who pays for this request? address payer; } /* * @title The Ismp Dispatcher * @author Polytope Labs ([email protected]) * * @notice The IDispatcher serves as the interface requests & response messages. */ interface IDispatcher { /** * @dev Returns the address for the Uniswap V2 Router implementation used for swaps * @return routerAddress - The address to the in-use RouterV02 implementation */ function uniswapV2Router() external view returns (address); /** * @dev Returns the nonce immediately available for requests * @return the `nonce` */ function nonce() external view returns (uint256); /** * @dev Returns the address of the ERC-20 fee token contract configured for this state machine. * * @notice Hyperbridge collects it's dispatch fees in the provided token denomination. This will typically be in stablecoins. * * @return feeToken - The ERC20 contract address for fees. */ function feeToken() external view returns (address); /** * @dev Returns the address of the per byte fee configured for this state machine. * * @notice Hyperbridge collects it's dispatch fees per every byte of the outgoing message. * * @return perByteFee - The per byte fee for outgoing messages. */ function perByteFee() external view returns (uint256); /** * @dev Dispatch a POST request to Hyperbridge * * @notice Payment for the request can be made with either the native token or the IIsmpHost.feeToken. * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router. * Will revert if enough native tokens are not provided. * * If no native tokens are provided then it will try to collect payment from the calling contract in * the IIsmpHost.feeToken. * * @param request - post request * @return commitment - the request commitment */ function dispatch(DispatchPost memory request) external payable returns (bytes32 commitment); /** * @dev Dispatch a GET request to Hyperbridge * * @notice Payment for the request can be made with either the native token or the IIsmpHost.feeToken. * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router. * Will revert if enough native tokens are not provided. * * If no native tokens are provided then it will try to collect payment from the calling contract in * the IIsmpHost.feeToken. * * @param request - get request * @return commitment - the request commitment */ function dispatch(DispatchGet memory request) external payable returns (bytes32 commitment); /** * @dev Dispatch a POST response to Hyperbridge * * @notice Payment for the request can be made with either the native token or the IIsmpHost.feeToken. * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router. * Will revert if enough native tokens are not provided. * * If no native tokens are provided then it will try to collect payment from the calling contract in * the IIsmpHost.feeToken. * * @param response - post response * @return commitment - the request commitment */ function dispatch(DispatchPostResponse memory response) external payable returns (bytes32 commitment); /** * @dev Increase the relayer fee for a previously dispatched request. * This is provided for use only on pending requests, such that when they timeout, * the user can recover the entire relayer fee. * * @notice Payment can be made with either the native token or the IIsmpHost.feeToken. * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router. * Will revert if enough native tokens are not provided. * * If no native tokens are provided then it will try to collect payment from the calling contract in * the IIsmpHost.feeToken. * * If called on an already delivered request, these funds will be seen as a donation to the hyperbridge protocol. * @param commitment - The request commitment * @param amount - The amount provided in `IIsmpHost.feeToken()` */ function fundRequest(bytes32 commitment, uint256 amount) external payable; /** * @dev Increase the relayer fee for a previously dispatched response. * This is provided for use only on pending responses, such that when they timeout, * the user can recover the entire relayer fee. * * @notice Payment can be made with either the native token or the IIsmpHost.feeToken. * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router. * Will revert if enough native tokens are not provided. * * If no native tokens are provided then it will try to collect payment from the calling contract in * the IIsmpHost.feeToken. * * If called on an already delivered response, these funds will be seen as a donation to the hyperbridge protocol. * @param commitment - The response commitment * @param amount - The amount to be provided in `IIsmpHost.feeToken()` */ function fundResponse(bytes32 commitment, uint256 amount) external payable; }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import {PostRequest, PostResponse, GetResponse, GetRequest} from "./Message.sol"; import {DispatchPost, DispatchPostResponse, DispatchGet, IDispatcher} from "./IDispatcher.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; struct IncomingPostRequest { // The Post request PostRequest request; // Relayer responsible for delivering the request address relayer; } struct IncomingPostResponse { // The Post response PostResponse response; // Relayer responsible for delivering the response address relayer; } struct IncomingGetResponse { // The Get response GetResponse response; // Relayer responsible for delivering the response address relayer; } interface IIsmpModule { /** * @dev Called by the `IsmpHost` to notify a module of a new request the module may choose to respond immediately, or in a later block * @param incoming post request */ function onAccept(IncomingPostRequest memory incoming) external; /** * @dev Called by the `IsmpHost` to notify a module of a post response to a previously sent out request * @param incoming post response */ function onPostResponse(IncomingPostResponse memory incoming) external; /** * @dev Called by the `IsmpHost` to notify a module of a get response to a previously sent out request * @param incoming get response */ function onGetResponse(IncomingGetResponse memory incoming) external; /** * @dev Called by the `IsmpHost` to notify a module of post requests that were previously sent but have now timed-out * @param request post request */ function onPostRequestTimeout(PostRequest memory request) external; /** * @dev Called by the `IsmpHost` to notify a module of post requests that were previously sent but have now timed-out * @param request post request */ function onPostResponseTimeout(PostResponse memory request) external; /** * @dev Called by the `IsmpHost` to notify a module of get requests that were previously sent but have now timed-out * @param request get request */ function onGetTimeout(GetRequest memory request) external; } // @notice Abstract contract to make implementing `IIsmpModule` easier. abstract contract BaseIsmpModule is IIsmpModule { // @notice Call was not expected error UnexpectedCall(); // @notice Account is unauthorized error UnauthorizedCall(); // @dev restricts caller to the local `IsmpHost` modifier onlyHost() { if (msg.sender != host()) revert UnauthorizedCall(); _; } constructor() { address hostAddr = host(); if (hostAddr != address(0)) { // approve the host infintely IERC20(IDispatcher(hostAddr).feeToken()).approve(hostAddr, type(uint256).max); } } // @dev Returns the `IsmpHost` address for the current chain. // The `IsmpHost` is an immutable contract that will never change. function host() public view returns (address h) { assembly { switch chainid() // Ethereum Sepolia case 11155111 { h := 0x27B0c6960B792a8dCb01F0652bDE48015cd5f23e } // Arbitrum Sepolia case 421614 { h := 0xfd7E2b2ad0b29Ec817dC7d406881b225B81dbFCf } // Optimism Sepolia case 11155420 { h := 0x30e3af1747B155F37F935E0EC995De5EA4e67586 } // Base Sepolia case 84532 { h := 0x0D7037bd9CEAEF25e5215f808d309ADD0A65Cdb9 } // Binance Smart Chain Testnet case 97 { h := 0x4cB0f5750f6fE14d4B86acA6fe126943bdA3c8c4 } // Gnosis Chiado Testnet case 10200 { h := 0x11EB87c745D97a4Fa8Aec805359837459d240d1b } } } // @dev returns the quoted fee for a dispatch function quote(DispatchPost memory post) public view returns (uint256) { return post.fee + (post.body.length * IDispatcher(host()).perByteFee()); } // @dev returns the quoted fee for a dispatch function quote(DispatchPostResponse memory res) public view returns (uint256) { return res.fee + (res.response.length * IDispatcher(host()).perByteFee()); } // @dev returns the quoted fee for a dispatch function quote(DispatchGet memory get) public view returns (uint256) { return get.fee + (get.context.length * IDispatcher(host()).perByteFee()); } function onAccept(IncomingPostRequest calldata) external virtual onlyHost { revert UnexpectedCall(); } function onPostRequestTimeout(PostRequest memory) external virtual onlyHost { revert UnexpectedCall(); } function onPostResponse(IncomingPostResponse memory) external virtual onlyHost { revert UnexpectedCall(); } function onPostResponseTimeout(PostResponse memory) external virtual onlyHost { revert UnexpectedCall(); } function onGetResponse(IncomingGetResponse memory) external virtual onlyHost { revert UnexpectedCall(); } function onGetTimeout(GetRequest memory) external virtual onlyHost { revert UnexpectedCall(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import {StorageValue} from "@polytope-labs/solidity-merkle-trees/src/Types.sol"; // Identifies some state machine height. We allow for a state machine identifier here // as some consensus clients may track multiple, concurrent state machines. struct StateMachineHeight { // the state machine identifier uint256 stateMachineId; // height of this state machine uint256 height; } struct PostRequest { // the source state machine of this request bytes source; // the destination state machine of this request bytes dest; // request nonce uint64 nonce; // Module Id of this request origin bytes from; // destination module id bytes to; // timestamp by which this request times out. uint64 timeoutTimestamp; // request body bytes body; } struct GetRequest { // the source state machine of this request bytes source; // the destination state machine of this request bytes dest; // request nonce uint64 nonce; // Module Id of this request origin address from; // timestamp by which this request times out. uint64 timeoutTimestamp; // Storage keys to read. bytes[] keys; // height at which to read destination state machine uint64 height; // Some application-specific metadata relating to this request bytes context; } struct GetResponse { // The request that initiated this response GetRequest request; // storage values for get response StorageValue[] values; } struct PostResponse { // The request that initiated this response PostRequest request; // bytes for post response bytes response; // timestamp by which this response times out. uint64 timeoutTimestamp; } // A post request as a leaf in a merkle tree struct PostRequestLeaf { // The request PostRequest request; // It's index in the mmr leaves uint256 index; // it's k-index uint256 kIndex; } // A post response as a leaf in a merkle tree struct PostResponseLeaf { // The response PostResponse response; // It's index in the mmr leaves uint256 index; // it's k-index uint256 kIndex; } // A get response as a leaf in a merkle mountain range tree struct GetResponseLeaf { // The response GetResponse response; // It's index in the mmr leaves uint256 index; // it's k-index uint256 kIndex; } // A merkle mountain range proof. struct Proof { // height of the state machine StateMachineHeight height; // the multi-proof bytes32[] multiproof; // The total number of leaves in the mmr for this proof. uint256 leafCount; } // A message for handling incoming requests struct PostRequestMessage { // proof for the requests Proof proof; // The requests, contained in the merkle mountain range tree PostRequestLeaf[] requests; } // A message for handling incoming GET responses struct GetResponseMessage { // proof for the responses Proof proof; // The responses, contained in the merkle mountain range tree GetResponseLeaf[] responses; } struct GetTimeoutMessage { // requests which have timed-out GetRequest[] timeouts; // the height of the state machine proof StateMachineHeight height; // non-membership proof of the requests bytes[] proof; } struct PostRequestTimeoutMessage { // requests which have timed-out PostRequest[] timeouts; // the height of the state machine proof StateMachineHeight height; // non-membership proof of the requests bytes[] proof; } struct PostResponseTimeoutMessage { // responses which have timed-out PostResponse[] timeouts; // the height of the state machine proof StateMachineHeight height; // non-membership proof of the requests bytes[] proof; } // A message for handling incoming responses struct PostResponseMessage { // proof for the responses Proof proof; // the responses, contained in a merkle tree leaf PostResponseLeaf[] responses; } library Message { /** * @dev Calculates the absolute timeout value for a PostRequest */ function timeout(PostRequest memory req) internal pure returns (uint64) { if (req.timeoutTimestamp == 0) { return type(uint64).max; } else { return req.timeoutTimestamp; } } /** * @dev Calculates the absolute timeout value for a GetRequest */ function timeout(GetRequest memory req) internal pure returns (uint64) { if (req.timeoutTimestamp == 0) { return type(uint64).max; } else { return req.timeoutTimestamp; } } /** * @dev Calculates the absolute timeout value for a PostResponse */ function timeout(PostResponse memory res) internal pure returns (uint64) { if (res.timeoutTimestamp == 0) { return type(uint64).max; } else { return res.timeoutTimestamp; } } /** * @dev Encode the given post request for commitment */ function encode(PostRequest memory req) internal pure returns (bytes memory) { return abi.encodePacked(req.source, req.dest, req.nonce, req.timeoutTimestamp, req.from, req.to, req.body); } /** * @dev Encode the given get request for commitment */ function encode(GetRequest memory req) internal pure returns (bytes memory) { bytes memory keysEncoding = bytes(""); uint256 len = req.keys.length; for (uint256 i = 0; i < len; i++) { keysEncoding = bytes.concat(keysEncoding, req.keys[i]); } return abi.encodePacked( req.source, req.dest, req.nonce, req.height, req.timeoutTimestamp, abi.encodePacked(req.from), keysEncoding, req.context ); } /** * @dev Returns the commitment for the given post response */ function hash(PostResponse memory res) internal pure returns (bytes32) { return keccak256(bytes.concat(encode(res.request), abi.encodePacked(res.response, res.timeoutTimestamp))); } /** * @dev Returns the commitment for the given post request */ function hash(PostRequest memory req) internal pure returns (bytes32) { return keccak256(encode(req)); } /** * @dev Returns the commitment for the given get request */ function hash(GetRequest memory req) internal pure returns (bytes32) { return keccak256(encode(req)); } /** * @dev Returns the commitment for the given get response */ function hash(GetResponse memory res) internal pure returns (bytes32) { bytes memory response = bytes(""); uint256 len = res.values.length; for (uint256 i = 0; i < len; i++) { response = bytes.concat(response, bytes.concat(res.values[i].key, res.values[i].value)); } return keccak256(bytes.concat(encode(res.request), response)); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; library StateMachine { /// @notice The identifier for the relay chain. uint256 public constant RELAY_CHAIN = 0; // @notice Address a state machine on the polkadot relay chain function polkadot(uint256 id) internal pure returns (bytes memory) { return bytes(string.concat("POLKADOT-", Strings.toString(id))); } // @notice Address a state machine on the kusama relay chain function kusama(uint256 id) internal pure returns (bytes memory) { return bytes(string.concat("KUSAMA-", Strings.toString(id))); } // @notice Address an evm state machine function evm(uint chainid) internal pure returns (bytes memory) { return bytes(string.concat("EVM-", Strings.toString(chainid))); } }
pragma solidity 0.8.20; import "./trie/Node.sol"; import "./trie/Option.sol"; import "./trie/NibbleSlice.sol"; import "./trie/TrieDB.sol"; import "./trie/substrate/SubstrateTrieDB.sol"; import "./trie/ethereum/EthereumTrieDB.sol"; import "./Types.sol"; // SPDX-License-Identifier: Apache2 /** * @title A Merkle Patricia library * @author Polytope Labs * @dev Use this library to verify merkle patricia proofs * @dev refer to research for more info. https://research.polytope.technology/state-(machine)-proofs */ library MerklePatricia { /** * @notice Verifies substrate specific merkle patricia proofs. * @param root hash of the merkle patricia trie * @param proof a list of proof nodes * @param keys a list of keys to verify * @return bytes[] a list of values corresponding to the supplied keys. */ function VerifySubstrateProof( bytes32 root, bytes[] memory proof, bytes[] memory keys ) public pure returns (StorageValue[] memory) { StorageValue[] memory values = new StorageValue[](keys.length); TrieNode[] memory nodes = new TrieNode[](proof.length); for (uint256 i = 0; i < proof.length; i++) { nodes[i] = TrieNode(keccak256(proof[i]), proof[i]); } for (uint256 i = 0; i < keys.length; i++) { values[i].key = keys[i]; NibbleSlice memory keyNibbles = NibbleSlice(keys[i], 0); NodeKind memory node = SubstrateTrieDB.decodeNodeKind( TrieDB.get(nodes, root) ); // This loop is unbounded so that an adversary cannot insert a deeply nested key in the trie // and successfully convince us of it's non-existence, if we consume the block gas limit while // traversing the trie, then the transaction should revert. for (uint256 j = 1; j > 0; j++) { NodeHandle memory nextNode; if (TrieDB.isLeaf(node)) { Leaf memory leaf = SubstrateTrieDB.decodeLeaf(node); if (NibbleSliceOps.eq(leaf.key, keyNibbles)) { values[i].value = TrieDB.load(nodes, leaf.value); } break; } else if (TrieDB.isNibbledBranch(node)) { NibbledBranch memory nibbled = SubstrateTrieDB .decodeNibbledBranch(node); uint256 nibbledBranchKeyLength = NibbleSliceOps.len( nibbled.key ); if (!NibbleSliceOps.startsWith(keyNibbles, nibbled.key)) { break; } if ( NibbleSliceOps.len(keyNibbles) == nibbledBranchKeyLength ) { if (Option.isSome(nibbled.value)) { values[i].value = TrieDB.load( nodes, nibbled.value.value ); } break; } else { uint256 index = NibbleSliceOps.at( keyNibbles, nibbledBranchKeyLength ); NodeHandleOption memory handle = nibbled.children[ index ]; if (Option.isSome(handle)) { keyNibbles = NibbleSliceOps.mid( keyNibbles, nibbledBranchKeyLength + 1 ); nextNode = handle.value; } else { break; } } } else if (TrieDB.isEmpty(node)) { break; } node = SubstrateTrieDB.decodeNodeKind( TrieDB.load(nodes, nextNode) ); } } return values; } /** * @notice Verify child trie keys * @dev substrate specific method in order to verify keys in the child trie. * @param root hash of the merkle root * @param proof a list of proof nodes * @param keys a list of keys to verify * @param childInfo data that can be used to compute the root of the child trie * @return bytes[], a list of values corresponding to the supplied keys. */ function ReadChildProofCheck( bytes32 root, bytes[] memory proof, bytes[] memory keys, bytes memory childInfo ) public pure returns (StorageValue[] memory) { // fetch the child trie root hash; bytes memory prefix = bytes(":child_storage:default:"); bytes memory key = bytes.concat(prefix, childInfo); bytes[] memory _keys = new bytes[](1); _keys[0] = key; StorageValue[] memory values = VerifySubstrateProof(root, proof, _keys); bytes32 childRoot = bytes32(values[0].value); require(childRoot != bytes32(0), "Invalid child trie proof"); return VerifySubstrateProof(childRoot, proof, keys); } /** * @notice Verifies ethereum specific merkle patricia proofs as described by EIP-1188. * @param root hash of the merkle patricia trie * @param proof a list of proof nodes * @param keys a list of keys to verify * @return bytes[] a list of values corresponding to the supplied keys. */ function VerifyEthereumProof( bytes32 root, bytes[] memory proof, bytes[] memory keys ) public pure returns (StorageValue[] memory) { StorageValue[] memory values = new StorageValue[](keys.length); TrieNode[] memory nodes = new TrieNode[](proof.length); for (uint256 i = 0; i < proof.length; i++) { nodes[i] = TrieNode(keccak256(proof[i]), proof[i]); } for (uint256 i = 0; i < keys.length; i++) { values[i].key = keys[i]; NibbleSlice memory keyNibbles = NibbleSlice(keys[i], 0); NodeKind memory node = EthereumTrieDB.decodeNodeKind( TrieDB.get(nodes, root) ); // This loop is unbounded so that an adversary cannot insert a deeply nested key in the trie // and successfully convince us of it's non-existence, if we consume the block gas limit while // traversing the trie, then the transaction should revert. for (uint256 j = 1; j > 0; j++) { NodeHandle memory nextNode; if (TrieDB.isLeaf(node)) { Leaf memory leaf = EthereumTrieDB.decodeLeaf(node); // Let's retrieve the offset to be used uint256 offset = keyNibbles.offset % 2 == 0 ? keyNibbles.offset / 2 : keyNibbles.offset / 2 + 1; // Let's cut the key passed as input keyNibbles = NibbleSlice( NibbleSliceOps.bytesSlice(keyNibbles.data, offset), 0 ); if (NibbleSliceOps.eq(leaf.key, keyNibbles)) { values[i].value = TrieDB.load(nodes, leaf.value); } break; } else if (TrieDB.isExtension(node)) { Extension memory extension = EthereumTrieDB.decodeExtension( node ); if (NibbleSliceOps.startsWith(keyNibbles, extension.key)) { // Let's cut the key passed as input uint256 cutNibble = keyNibbles.offset + NibbleSliceOps.len(extension.key); keyNibbles = NibbleSlice( NibbleSliceOps.bytesSlice( keyNibbles.data, cutNibble / 2 ), cutNibble % 2 ); nextNode = extension.node; } else { break; } } else if (TrieDB.isBranch(node)) { Branch memory branch = EthereumTrieDB.decodeBranch(node); if (NibbleSliceOps.isEmpty(keyNibbles)) { if (Option.isSome(branch.value)) { values[i].value = TrieDB.load( nodes, branch.value.value ); } break; } else { NodeHandleOption memory handle = branch.children[ NibbleSliceOps.at(keyNibbles, 0) ]; if (Option.isSome(handle)) { keyNibbles = NibbleSliceOps.mid(keyNibbles, 1); nextNode = handle.value; } else { break; } } } else if (TrieDB.isEmpty(node)) { break; } node = EthereumTrieDB.decodeNodeKind( TrieDB.load(nodes, nextNode) ); } } return values; } }
pragma solidity 0.8.20; // SPDX-License-Identifier: Apache2 import {Memory} from "./Memory.sol"; struct ByteSlice { bytes data; uint256 offset; } library Bytes { uint256 internal constant BYTES_HEADER_SIZE = 32; // Checks if two `bytes memory` variables are equal. This is done using hashing, // which is much more gas efficient then comparing each byte individually. // Equality means that: // - 'self.length == other.length' // - For 'n' in '[0, self.length)', 'self[n] == other[n]' function equals( bytes memory self, bytes memory other ) internal pure returns (bool equal) { if (self.length != other.length) { return false; } uint256 addr; uint256 addr2; assembly { addr := add(self, /*BYTES_HEADER_SIZE*/ 32) addr2 := add(other, /*BYTES_HEADER_SIZE*/ 32) } equal = Memory.equals(addr, addr2, self.length); } function readByte(ByteSlice memory self) internal pure returns (uint8) { if (self.offset + 1 > self.data.length) { revert("Out of range"); } uint8 b = uint8(self.data[self.offset]); self.offset += 1; return b; } // Copies 'len' bytes from 'self' into a new array, starting at the provided 'startIndex'. // Returns the new copy. // Requires that: // - 'startIndex + len <= self.length' // The length of the substring is: 'len' function read( ByteSlice memory self, uint256 len ) internal pure returns (bytes memory) { require(self.offset + len <= self.data.length); if (len == 0) { return ""; } uint256 addr = Memory.dataPtr(self.data); bytes memory slice = Memory.toBytes(addr + self.offset, len); self.offset += len; return slice; } // Copies a section of 'self' into a new array, starting at the provided 'startIndex'. // Returns the new copy. // Requires that 'startIndex <= self.length' // The length of the substring is: 'self.length - startIndex' function substr( bytes memory self, uint256 startIndex ) internal pure returns (bytes memory) { require(startIndex <= self.length); uint256 len = self.length - startIndex; uint256 addr = Memory.dataPtr(self); return Memory.toBytes(addr + startIndex, len); } // Copies 'len' bytes from 'self' into a new array, starting at the provided 'startIndex'. // Returns the new copy. // Requires that: // - 'startIndex + len <= self.length' // The length of the substring is: 'len' function substr( bytes memory self, uint256 startIndex, uint256 len ) internal pure returns (bytes memory) { require(startIndex + len <= self.length); if (len == 0) { return ""; } uint256 addr = Memory.dataPtr(self); return Memory.toBytes(addr + startIndex, len); } // Combines 'self' and 'other' into a single array. // Returns the concatenated arrays: // [self[0], self[1], ... , self[self.length - 1], other[0], other[1], ... , other[other.length - 1]] // The length of the new array is 'self.length + other.length' function concat( bytes memory self, bytes memory other ) internal pure returns (bytes memory) { bytes memory ret = new bytes(self.length + other.length); uint256 src; uint256 srcLen; (src, srcLen) = Memory.fromBytes(self); uint256 src2; uint256 src2Len; (src2, src2Len) = Memory.fromBytes(other); uint256 dest; (dest, ) = Memory.fromBytes(ret); uint256 dest2 = dest + srcLen; Memory.copy(src, dest, srcLen); Memory.copy(src2, dest2, src2Len); return ret; } function toBytes32(bytes memory self) internal pure returns (bytes32 out) { require(self.length >= 32, "Bytes:: toBytes32: data is to short."); assembly { out := mload(add(self, 32)) } } function toBytes16( bytes memory self, uint256 offset ) internal pure returns (bytes16 out) { for (uint256 i = 0; i < 16; i++) { out |= bytes16(bytes1(self[offset + i]) & 0xFF) >> (i * 8); } } function toBytes8( bytes memory self, uint256 offset ) internal pure returns (bytes8 out) { for (uint256 i = 0; i < 8; i++) { out |= bytes8(bytes1(self[offset + i]) & 0xFF) >> (i * 8); } } function toBytes4( bytes memory self, uint256 offset ) internal pure returns (bytes4) { bytes4 out; for (uint256 i = 0; i < 4; i++) { out |= bytes4(self[offset + i] & 0xFF) >> (i * 8); } return out; } function toBytes2( bytes memory self, uint256 offset ) internal pure returns (bytes2) { bytes2 out; for (uint256 i = 0; i < 2; i++) { out |= bytes2(self[offset + i] & 0xFF) >> (i * 8); } return out; } function removeLeadingZero( bytes memory data ) internal pure returns (bytes memory) { uint256 length = data.length; uint256 startIndex = 0; for (uint256 i = 0; i < length; i++) { if (data[i] != 0) { startIndex = i; break; } } return substr(data, startIndex); } function removeEndingZero( bytes memory data ) internal pure returns (bytes memory) { uint256 length = data.length; uint256 endIndex = 0; for (uint256 i = length - 1; i >= 0; i--) { if (data[i] != 0) { endIndex = i; break; } } return substr(data, 0, endIndex + 1); } function reverse( bytes memory inbytes ) internal pure returns (bytes memory) { uint256 inlength = inbytes.length; bytes memory outbytes = new bytes(inlength); for (uint256 i = 0; i <= inlength - 1; i++) { outbytes[i] = inbytes[inlength - i - 1]; } return outbytes; } }
pragma solidity 0.8.20; import "../Node.sol"; import "../Bytes.sol"; import {NibbleSliceOps} from "../NibbleSlice.sol"; import "./RLPReader.sol"; // SPDX-License-Identifier: Apache2 library EthereumTrieDB { using RLPReader for bytes; using RLPReader for RLPReader.RLPItem; using RLPReader for RLPReader.Iterator; bytes constant HASHED_NULL_NODE = hex"56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"; function decodeNodeKind( bytes memory encoded ) external pure returns (NodeKind memory) { NodeKind memory node; ByteSlice memory input = ByteSlice(encoded, 0); if (Bytes.equals(encoded, HASHED_NULL_NODE)) { node.isEmpty = true; return node; } RLPReader.RLPItem[] memory itemList = encoded.toRlpItem().toList(); uint256 numItems = itemList.length; if (numItems == 0) { node.isEmpty = true; return node; } else if (numItems == 2) { //It may be a leaf or extension bytes memory key = itemList[0].toBytes(); uint256 prefix; assembly { let first := shr(248, mload(add(key, 32))) prefix := shr(4, first) } if (prefix == 2 || prefix == 3) { node.isLeaf = true; } else { node.isExtension = true; } } else if (numItems == 17) { node.isBranch = true; } else { revert("Invalid data"); } node.data = input; return node; } function decodeLeaf( NodeKind memory node ) external pure returns (Leaf memory) { Leaf memory leaf; RLPReader.RLPItem[] memory decoded = node .data .data .toRlpItem() .toList(); bytes memory data = decoded[1].toBytes(); //Remove the first byte, which is the prefix and not present in the user provided key leaf.key = NibbleSlice(Bytes.substr(decoded[0].toBytes(), 1), 0); leaf.value = NodeHandle(false, bytes32(0), true, data); return leaf; } function decodeExtension( NodeKind memory node ) external pure returns (Extension memory) { Extension memory extension; RLPReader.RLPItem[] memory decoded = node .data .data .toRlpItem() .toList(); bytes memory data = decoded[1].toBytes(); uint8 isOdd = uint8(decoded[0].toBytes()[0] >> 4) & 0x01; //Remove the first byte, which is the prefix and not present in the user provided key extension.key = NibbleSlice( Bytes.substr(decoded[0].toBytes(), (isOdd + 1) % 2), isOdd ); extension.node = NodeHandle( true, Bytes.toBytes32(data), false, new bytes(0) ); return extension; } function decodeBranch( NodeKind memory node ) external pure returns (Branch memory) { Branch memory branch; RLPReader.RLPItem[] memory decoded = node .data .data .toRlpItem() .toList(); NodeHandleOption[16] memory childrens; for (uint256 i = 0; i < 16; i++) { bytes memory dataAsBytes = decoded[i].toBytes(); if (dataAsBytes.length != 32) { childrens[i] = NodeHandleOption( false, NodeHandle(false, bytes32(0), false, new bytes(0)) ); } else { bytes32 data = Bytes.toBytes32(dataAsBytes); childrens[i] = NodeHandleOption( true, NodeHandle(true, data, false, new bytes(0)) ); } } if (isEmpty(decoded[16].toBytes())) { branch.value = NodeHandleOption( false, NodeHandle(false, bytes32(0), false, new bytes(0)) ); } else { branch.value = NodeHandleOption( true, NodeHandle(false, bytes32(0), true, decoded[16].toBytes()) ); } branch.children = childrens; return branch; } function isEmpty(bytes memory item) internal pure returns (bool) { return item.length > 0 && (item[0] == 0xc0 || item[0] == 0x80); } }
// SPDX-License-Identifier: Apache-2.0 /* * @author Hamdi Allam [email protected] * Please reach out with any questions or concerns */ pragma solidity >=0.5.10 <0.9.0; library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint256 len; uint256 memPtr; } struct Iterator { RLPItem item; // Item that's being iterated over. uint256 nextPtr; // Position of the next item in the list. } /* * @dev Returns the next element in the iteration. Reverts if it has not next element. * @param self The iterator. * @return The next element in the iteration. */ function next(Iterator memory self) internal pure returns (RLPItem memory) { require(hasNext(self)); uint256 ptr = self.nextPtr; uint256 itemLength = _itemLength(ptr); self.nextPtr = ptr + itemLength; return RLPItem(itemLength, ptr); } /* * @dev Returns true if the iteration has more elements. * @param self The iterator. * @return true if the iteration has more elements. */ function hasNext(Iterator memory self) internal pure returns (bool) { RLPItem memory item = self.item; return self.nextPtr < item.memPtr + item.len; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint256 memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @dev Create an iterator. Reverts if item is not a list. * @param self The RLP item. * @return An 'Iterator' over the item. */ function iterator(RLPItem memory self) internal pure returns (Iterator memory) { require(isList(self)); uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); return Iterator(self, ptr); } /* * @param the RLP item. */ function rlpLen(RLPItem memory item) internal pure returns (uint256) { return item.len; } /* * @param the RLP item. * @return (memPtr, len) pair: location of the item's payload in memory. */ function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { uint256 offset = _payloadOffset(item.memPtr); uint256 memPtr = item.memPtr + offset; uint256 len = item.len - offset; // data length return (memPtr, len); } /* * @param the RLP item. */ function payloadLen(RLPItem memory item) internal pure returns (uint256) { (, uint256 len) = payloadLocation(item); return len; } /* * @param the RLP item containing the encoded list. */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint256 items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 dataLen; for (uint256 i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint256 memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /* * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. * @return keccak256 hash of RLP encoded bytes. */ function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { uint256 ptr = item.memPtr; uint256 len = item.len; bytes32 result; assembly { result := keccak256(ptr, len) } return result; } /* * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. * @return keccak256 hash of the item payload. */ function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { (uint256 memPtr, uint256 len) = payloadLocation(item); bytes32 result; assembly { result := keccak256(memPtr, len) } return result; } /** * RLPItem conversions into data types * */ // @returns raw rlp encoding in bytes function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); if (result.length == 0) return result; uint256 ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } // any non-zero byte except "0x80" is considered true function toBoolean(RLPItem memory item) internal pure returns (bool) { require(item.len == 1); uint256 result; uint256 memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } // SEE Github Issue #5. // Summary: Most commonly used RLP libraries (i.e Geth) will encode // "0" as "0x80" instead of as "0". We handle this edge case explicitly // here. if (result == 0 || result == STRING_SHORT_START) { return false; } else { return true; } } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix require(item.len == 21); return address(uint160(toUint(item))); } function toUint(RLPItem memory item) internal pure returns (uint256) { require(item.len > 0 && item.len <= 33); (uint256 memPtr, uint256 len) = payloadLocation(item); uint256 result; assembly { result := mload(memPtr) // shift to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } // enforces 32 byte length function toUintStrict(RLPItem memory item) internal pure returns (uint256) { // one byte prefix require(item.len == 33); uint256 result; uint256 memPtr = item.memPtr + 1; assembly { result := mload(memPtr) } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); (uint256 memPtr, uint256 len) = payloadLocation(item); bytes memory result = new bytes(len); uint256 destPtr; assembly { destPtr := add(0x20, result) } copy(memPtr, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint256) { if (item.len == 0) return 0; uint256 count = 0; uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint256 memPtr) private pure returns (uint256) { uint256 itemLen; uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) { itemLen = 1; } else if (byte0 < STRING_LONG_START) { itemLen = byte0 - STRING_SHORT_START + 1; } else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint256 memPtr) private pure returns (uint256) { uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) { return 0; } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) { return 1; } else if (byte0 < LIST_SHORT_START) { // being explicit return byte0 - (STRING_LONG_START - 1) + 1; } else { return byte0 - (LIST_LONG_START - 1) + 1; } } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy(uint256 src, uint256 dest, uint256 len) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } if (len > 0) { // left over bytes. Mask is used to remove unwanted bytes from the word uint256 mask = 256 ** (WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } } }
pragma solidity 0.8.20; // SPDX-License-Identifier: Apache2 library Memory { uint256 internal constant WORD_SIZE = 32; // Compares the 'len' bytes starting at address 'addr' in memory with the 'len' // bytes starting at 'addr2'. // Returns 'true' if the bytes are the same, otherwise 'false'. function equals( uint256 addr, uint256 addr2, uint256 len ) internal pure returns (bool equal) { assembly { equal := eq(keccak256(addr, len), keccak256(addr2, len)) } } // Compares the 'len' bytes starting at address 'addr' in memory with the bytes stored in // 'bts'. It is allowed to set 'len' to a lower value then 'bts.length', in which case only // the first 'len' bytes will be compared. // Requires that 'bts.length >= len' function equals( uint256 addr, uint256 len, bytes memory bts ) internal pure returns (bool equal) { require(bts.length >= len); uint256 addr2; assembly { addr2 := add(bts, /*BYTES_HEADER_SIZE*/ 32) } return equals(addr, addr2, len); } // Returns a memory pointer to the data portion of the provided bytes array. function dataPtr(bytes memory bts) internal pure returns (uint256 addr) { assembly { addr := add(bts, /*BYTES_HEADER_SIZE*/ 32) } } // Creates a 'bytes memory' variable from the memory address 'addr', with the // length 'len'. The function will allocate new memory for the bytes array, and // the 'len bytes starting at 'addr' will be copied into that new memory. function toBytes( uint256 addr, uint256 len ) internal pure returns (bytes memory bts) { bts = new bytes(len); uint256 btsptr; assembly { btsptr := add(bts, /*BYTES_HEADER_SIZE*/ 32) } copy(addr, btsptr, len); } // Copies 'self' into a new 'bytes memory'. // Returns the newly created 'bytes memory' // The returned bytes will be of length '32'. function toBytes(bytes32 self) internal pure returns (bytes memory bts) { bts = new bytes(32); assembly { mstore(add(bts, /*BYTES_HEADER_SIZE*/ 32), self) } } // Copy 'len' bytes from memory address 'src', to address 'dest'. // This function does not check the or destination, it only copies // the bytes. function copy(uint256 src, uint256 dest, uint256 len) internal pure { // Copy word-length chunks while possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } dest += WORD_SIZE; src += WORD_SIZE; } // Copy remaining bytes uint256 mask = len == 0 ? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff : 256 ** (WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } // This function does the same as 'dataPtr(bytes memory)', but will also return the // length of the provided bytes array. function fromBytes( bytes memory bts ) internal pure returns (uint256 addr, uint256 len) { len = bts.length; assembly { addr := add(bts, /*BYTES_HEADER_SIZE*/ 32) } } }
pragma solidity 0.8.20; // SPDX-License-Identifier: Apache2 struct NibbleSlice { bytes data; uint256 offset; } library NibbleSliceOps { uint256 internal constant NIBBLE_PER_BYTE = 2; uint256 internal constant BITS_PER_NIBBLE = 4; function len(NibbleSlice memory nibble) internal pure returns (uint256) { return nibble.data.length * NIBBLE_PER_BYTE - nibble.offset; } function mid( NibbleSlice memory self, uint256 i ) internal pure returns (NibbleSlice memory) { return NibbleSlice(self.data, self.offset + i); } function isEmpty(NibbleSlice memory self) internal pure returns (bool) { return len(self) == 0; } function eq( NibbleSlice memory self, NibbleSlice memory other ) internal pure returns (bool) { return len(self) == len(other) && startsWith(self, other); } function at( NibbleSlice memory self, uint256 i ) internal pure returns (uint256) { uint256 ix = (self.offset + i) / NIBBLE_PER_BYTE; uint256 pad = (self.offset + i) % NIBBLE_PER_BYTE; uint8 data = uint8(self.data[ix]); return (pad == 1) ? data & 0x0F : data >> BITS_PER_NIBBLE; } function startsWith( NibbleSlice memory self, NibbleSlice memory other ) internal pure returns (bool) { return commonPrefix(self, other) == len(other); } function commonPrefix( NibbleSlice memory self, NibbleSlice memory other ) internal pure returns (uint256) { uint256 self_align = self.offset % NIBBLE_PER_BYTE; uint256 other_align = other.offset % NIBBLE_PER_BYTE; if (self_align == other_align) { uint256 self_start = self.offset / NIBBLE_PER_BYTE; uint256 other_start = other.offset / NIBBLE_PER_BYTE; uint256 first = 0; if (self_align != 0) { if ( (self.data[self_start] & 0x0F) != (other.data[other_start] & 0x0F) ) { return 0; } ++self_start; ++other_start; ++first; } bytes memory selfSlice = bytesSlice(self.data, self_start); bytes memory otherSlice = bytesSlice(other.data, other_start); return biggestDepth(selfSlice, otherSlice) + first; } else { uint256 s = min(len(self), len(other)); uint256 i = 0; while (i < s) { if (at(self, i) != at(other, i)) { break; } ++i; } return i; } } function biggestDepth( bytes memory a, bytes memory b ) internal pure returns (uint256) { uint256 upperBound = min(a.length, b.length); uint256 i = 0; while (i < upperBound) { if (a[i] != b[i]) { return i * NIBBLE_PER_BYTE + leftCommon(a[i], b[i]); } ++i; } return i * NIBBLE_PER_BYTE; } function leftCommon(bytes1 a, bytes1 b) internal pure returns (uint256) { if (a == b) { return 2; } else if (uint8(a) & 0xF0 == uint8(b) & 0xF0) { return 1; } else { return 0; } } function bytesSlice( bytes memory _bytes, uint256 _start ) internal pure returns (bytes memory) { uint256 bytesLength = _bytes.length; uint256 _length = bytesLength - _start; require(bytesLength >= _start, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { tempBytes := mload(0x40) // load free memory pointer let lengthmod := and(_length, 31) let mc := add( add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)) ) let end := add(mc, _length) for { let cc := add( add( add(_bytes, lengthmod), mul(0x20, iszero(lengthmod)) ), _start ) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) mstore(0x40, and(add(mc, 31), not(31))) } default { tempBytes := mload(0x40) mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function min(uint256 a, uint256 b) private pure returns (uint256) { return (a < b) ? a : b; } }
pragma solidity 0.8.20; // SPDX-License-Identifier: Apache2 import "./NibbleSlice.sol"; import "./Bytes.sol"; /// This is an enum for the different node types. struct NodeKind { bool isEmpty; bool isLeaf; bool isHashedLeaf; bool isNibbledValueBranch; bool isNibbledHashedValueBranch; bool isNibbledBranch; bool isExtension; bool isBranch; uint256 nibbleSize; ByteSlice data; } struct NodeHandle { bool isHash; bytes32 hash; bool isInline; bytes inLine; } struct Extension { NibbleSlice key; NodeHandle node; } struct Branch { NodeHandleOption value; NodeHandleOption[16] children; } struct NibbledBranch { NibbleSlice key; NodeHandleOption value; NodeHandleOption[16] children; } struct ValueOption { bool isSome; bytes value; } struct NodeHandleOption { bool isSome; NodeHandle value; } struct Leaf { NibbleSlice key; NodeHandle value; } struct TrieNode { bytes32 hash; bytes node; }
pragma solidity 0.8.20; import "./Node.sol"; // SPDX-License-Identifier: Apache2 library Option { function isSome(ValueOption memory val) internal pure returns (bool) { return val.isSome == true; } function isSome(NodeHandleOption memory val) internal pure returns (bool) { return val.isSome == true; } }
pragma solidity 0.8.20; // SPDX-License-Identifier: Apache2 import {Bytes, ByteSlice} from "../Bytes.sol"; library ScaleCodec { // Decodes a SCALE encoded uint256 by converting bytes (bid endian) to little endian format function decodeUint256(bytes memory data) internal pure returns (uint256) { uint256 number; for (uint256 i = data.length; i > 0; i--) { number = number + uint256(uint8(data[i - 1])) * (2 ** (8 * (i - 1))); } return number; } // Decodes a SCALE encoded compact unsigned integer function decodeUintCompact( ByteSlice memory data ) internal pure returns (uint256 v) { uint8 b = Bytes.readByte(data); // read the first byte uint8 mode = b % 4; // bitwise operation uint256 value; if (mode == 0) { // [0, 63] value = b >> 2; // right shift to remove mode bits } else if (mode == 1) { // [64, 16383] uint8 bb = Bytes.readByte(data); // read the second byte uint64 r = bb; // convert to uint64 r <<= 6; // multiply by * 2^6 r += b >> 2; // right shift to remove mode bits value = r; } else if (mode == 2) { // [16384, 1073741823] uint8 b2 = Bytes.readByte(data); // read the next 3 bytes uint8 b3 = Bytes.readByte(data); uint8 b4 = Bytes.readByte(data); uint32 x1 = uint32(b) | (uint32(b2) << 8); // convert to little endian uint32 x2 = x1 | (uint32(b3) << 16); uint32 x3 = x2 | (uint32(b4) << 24); x3 >>= 2; // remove the last 2 mode bits value = uint256(x3); } else if (mode == 3) { // [1073741824, 4503599627370496] uint8 l = (b >> 2) + 4; // remove mode bits require(l <= 8, "unexpected prefix decoding Compact<Uint>"); return decodeUint256(Bytes.read(data, l)); } else { revert("Code should be unreachable"); } return value; } // Decodes a SCALE encoded compact unsigned integer function decodeUintCompact( bytes memory data ) internal pure returns (uint256 v, uint8 m) { uint8 b = readByteAtIndex(data, 0); // read the first byte uint8 mode = b & 3; // bitwise operation uint256 value; if (mode == 0) { // [0, 63] value = b >> 2; // right shift to remove mode bits } else if (mode == 1) { // [64, 16383] uint8 bb = readByteAtIndex(data, 1); // read the second byte uint64 r = bb; // convert to uint64 r <<= 6; // multiply by * 2^6 r += b >> 2; // right shift to remove mode bits value = r; } else if (mode == 2) { // [16384, 1073741823] uint8 b2 = readByteAtIndex(data, 1); // read the next 3 bytes uint8 b3 = readByteAtIndex(data, 2); uint8 b4 = readByteAtIndex(data, 3); uint32 x1 = uint32(b) | (uint32(b2) << 8); // convert to little endian uint32 x2 = x1 | (uint32(b3) << 16); uint32 x3 = x2 | (uint32(b4) << 24); x3 >>= 2; // remove the last 2 mode bits value = uint256(x3); } else if (mode == 3) { // [1073741824, 4503599627370496] uint8 l = b >> 2; // remove mode bits require( l > 32, "Not supported: number cannot be greater than 32 bytes" ); } else { revert("Code should be unreachable"); } return (value, mode); } // The biggest compact supported uint is 2 ** 536 - 1. // But the biggest value supported by this method is 2 ** 256 - 1(max of uint256) function encodeUintCompact(uint256 v) internal pure returns (bytes memory) { if (v < 64) { return abi.encodePacked(uint8(v << 2)); } else if (v < 2 ** 14) { return abi.encodePacked(reverse16(uint16(((v << 2) + 1)))); } else if (v < 2 ** 30) { return abi.encodePacked(reverse32(uint32(((v << 2) + 2)))); } else { bytes memory valueBytes = Bytes.removeEndingZero( abi.encodePacked(reverse256(v)) ); uint256 length = valueBytes.length; uint8 prefix = uint8(((length - 4) << 2) + 3); return abi.encodePacked(prefix, valueBytes); } } // Read a byte at a specific index and return it as type uint8 function readByteAtIndex( bytes memory data, uint8 index ) internal pure returns (uint8) { return uint8(data[index]); } // Sources: // * https://ethereum.stackexchange.com/questions/15350/how-to-convert-an-bytes-to-address-in-solidity/50528 // * https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel function reverse256(uint256 input) internal pure returns (uint256 v) { v = input; // swap bytes v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32) | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32); // swap 8-byte long pairs v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64) | ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64); // swap 16-byte long pairs v = (v >> 128) | (v << 128); } function reverse128(uint128 input) internal pure returns (uint128 v) { v = input; // swap bytes v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000) >> 32) | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF) << 32); // swap 8-byte long pairs v = (v >> 64) | (v << 64); } function reverse64(uint64 input) internal pure returns (uint64 v) { v = input; // swap bytes v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = (v >> 32) | (v << 32); } function reverse32(uint32 input) internal pure returns (uint32 v) { v = input; // swap bytes v = ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8); // swap 2-byte long pairs v = (v >> 16) | (v << 16); } function reverse16(uint16 input) internal pure returns (uint16 v) { v = input; // swap bytes v = (v >> 8) | (v << 8); } function encode256(uint256 input) internal pure returns (bytes32) { return bytes32(reverse256(input)); } function encode128(uint128 input) internal pure returns (bytes16) { return bytes16(reverse128(input)); } function encode64(uint64 input) internal pure returns (bytes8) { return bytes8(reverse64(input)); } function encode32(uint32 input) internal pure returns (bytes4) { return bytes4(reverse32(input)); } function encode16(uint16 input) internal pure returns (bytes2) { return bytes2(reverse16(input)); } function encodeBytes( bytes memory input ) internal pure returns (bytes memory) { return abi.encodePacked(encodeUintCompact(input.length), input); } }
pragma solidity 0.8.20; import "../Node.sol"; import "../Bytes.sol"; import {NibbleSliceOps} from "../NibbleSlice.sol"; import {ScaleCodec} from "./ScaleCodec.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; // SPDX-License-Identifier: Apache2 library SubstrateTrieDB { uint8 public constant FIRST_PREFIX = 0x00 << 6; uint8 public constant PADDING_BITMASK = 0x0F; uint8 public constant EMPTY_TRIE = FIRST_PREFIX | (0x00 << 4); uint8 public constant LEAF_PREFIX_MASK = 0x01 << 6; uint8 public constant BRANCH_WITH_MASK = 0x03 << 6; uint8 public constant BRANCH_WITHOUT_MASK = 0x02 << 6; uint8 public constant ALT_HASHING_LEAF_PREFIX_MASK = FIRST_PREFIX | (0x01 << 5); uint8 public constant ALT_HASHING_BRANCH_WITH_MASK = FIRST_PREFIX | (0x01 << 4); uint8 public constant NIBBLE_PER_BYTE = 2; uint256 public constant NIBBLE_SIZE_BOUND = uint256(type(uint16).max); uint256 public constant BITMAP_LENGTH = 2; uint256 public constant HASH_lENGTH = 32; function decodeNodeKind( bytes memory encoded ) internal pure returns (NodeKind memory) { NodeKind memory node; ByteSlice memory input = ByteSlice(encoded, 0); uint8 i = Bytes.readByte(input); if (i == EMPTY_TRIE) { node.isEmpty = true; return node; } uint8 mask = i & (0x03 << 6); if (mask == LEAF_PREFIX_MASK) { node.nibbleSize = decodeSize(i, input, 2); node.isLeaf = true; } else if (mask == BRANCH_WITH_MASK) { node.nibbleSize = decodeSize(i, input, 2); node.isNibbledValueBranch = true; } else if (mask == BRANCH_WITHOUT_MASK) { node.nibbleSize = decodeSize(i, input, 2); node.isNibbledBranch = true; } else if (mask == EMPTY_TRIE) { if (i & (0x07 << 5) == ALT_HASHING_LEAF_PREFIX_MASK) { node.nibbleSize = decodeSize(i, input, 3); node.isHashedLeaf = true; } else if (i & (0x0F << 4) == ALT_HASHING_BRANCH_WITH_MASK) { node.nibbleSize = decodeSize(i, input, 4); node.isNibbledHashedValueBranch = true; } else { // do not allow any special encoding revert("Unallowed encoding"); } } node.data = input; return node; } function decodeNibbledBranch( NodeKind memory node ) internal pure returns (NibbledBranch memory) { NibbledBranch memory nibbledBranch; ByteSlice memory input = node.data; bool padding = node.nibbleSize % NIBBLE_PER_BYTE != 0; if (padding && (padLeft(uint8(input.data[input.offset])) != 0)) { revert("Bad Format!"); } uint256 nibbleLen = ((node.nibbleSize + (NibbleSliceOps.NIBBLE_PER_BYTE - 1)) / NibbleSliceOps.NIBBLE_PER_BYTE); nibbledBranch.key = NibbleSlice( Bytes.read(input, nibbleLen), node.nibbleSize % NIBBLE_PER_BYTE ); bytes memory bitmapBytes = Bytes.read(input, BITMAP_LENGTH); uint16 bitmap = uint16(ScaleCodec.decodeUint256(bitmapBytes)); NodeHandleOption memory valueHandle; if (node.isNibbledHashedValueBranch) { valueHandle.isSome = true; valueHandle.value.isHash = true; valueHandle.value.hash = Bytes.toBytes32( Bytes.read(input, HASH_lENGTH) ); } else if (node.isNibbledValueBranch) { uint256 len = ScaleCodec.decodeUintCompact(input); valueHandle.isSome = true; valueHandle.value.isInline = true; valueHandle.value.inLine = Bytes.read(input, len); } nibbledBranch.value = valueHandle; for (uint256 i = 0; i < 16; i++) { NodeHandleOption memory childHandle; if (valueAt(bitmap, i)) { childHandle.isSome = true; uint256 len = ScaleCodec.decodeUintCompact(input); // revert(string.concat("node index: ", Strings.toString(len))); if (len == HASH_lENGTH) { childHandle.value.isHash = true; childHandle.value.hash = Bytes.toBytes32( Bytes.read(input, HASH_lENGTH) ); } else { childHandle.value.isInline = true; childHandle.value.inLine = Bytes.read(input, len); } } nibbledBranch.children[i] = childHandle; } return nibbledBranch; } function decodeLeaf( NodeKind memory node ) internal pure returns (Leaf memory) { Leaf memory leaf; ByteSlice memory input = node.data; bool padding = node.nibbleSize % NIBBLE_PER_BYTE != 0; if (padding && padLeft(uint8(input.data[input.offset])) != 0) { revert("Bad Format!"); } uint256 nibbleLen = (node.nibbleSize + (NibbleSliceOps.NIBBLE_PER_BYTE - 1)) / NibbleSliceOps.NIBBLE_PER_BYTE; bytes memory nibbleBytes = Bytes.read(input, nibbleLen); leaf.key = NibbleSlice(nibbleBytes, node.nibbleSize % NIBBLE_PER_BYTE); NodeHandle memory handle; if (node.isHashedLeaf) { handle.isHash = true; handle.hash = Bytes.toBytes32(Bytes.read(input, HASH_lENGTH)); } else { uint256 len = ScaleCodec.decodeUintCompact(input); handle.isInline = true; handle.inLine = Bytes.read(input, len); } leaf.value = handle; return leaf; } function decodeSize( uint8 first, ByteSlice memory encoded, uint8 prefixMask ) internal pure returns (uint256) { uint8 maxValue = uint8(255 >> prefixMask); uint256 result = uint256(first & maxValue); if (result < maxValue) { return result; } result -= 1; while (result <= NIBBLE_SIZE_BOUND) { uint256 n = uint256(Bytes.readByte(encoded)); if (n < 255) { return result + n + 1; } result += 255; } return NIBBLE_SIZE_BOUND; } function padLeft(uint8 b) internal pure returns (uint8) { return b & ~PADDING_BITMASK; } function valueAt(uint16 bitmap, uint256 i) internal pure returns (bool) { return bitmap & (uint16(1) << uint16(i)) != 0; } }
// SPDX-License-Identifier: Apache2 pragma solidity 0.8.20; import "./Node.sol"; library TrieDB { function get( TrieNode[] memory nodes, bytes32 hash ) internal pure returns (bytes memory) { for (uint256 i = 0; i < nodes.length; i++) { if (nodes[i].hash == hash) { return nodes[i].node; } } revert("Incomplete Proof!"); } function load( TrieNode[] memory nodes, NodeHandle memory node ) internal pure returns (bytes memory) { if (node.isInline) { return node.inLine; } else if (node.isHash) { return get(nodes, node.hash); } return bytes(""); } function isNibbledBranch( NodeKind memory node ) internal pure returns (bool) { return (node.isNibbledBranch || node.isNibbledHashedValueBranch || node.isNibbledValueBranch); } function isExtension(NodeKind memory node) internal pure returns (bool) { return node.isExtension; } function isBranch(NodeKind memory node) internal pure returns (bool) { return node.isBranch; } function isLeaf(NodeKind memory node) internal pure returns (bool) { return (node.isLeaf || node.isHashedLeaf); } function isEmpty(NodeKind memory node) internal pure returns (bool) { return node.isEmpty; } function isHash(NodeHandle memory node) internal pure returns (bool) { return node.isHash; } function isInline(NodeHandle memory node) internal pure returns (bool) { return node.isInline; } }
pragma solidity 0.8.20; // SPDX-License-Identifier: Apache2 // Outcome of a successfully verified merkle-patricia proof struct StorageValue { // the storage key bytes key; // the encoded value bytes value; } /// @title A representation of a Merkle tree node struct Node { // Distance of the node to the leftmost node uint256 k_index; // A hash of the node itself bytes32 node; } /// @title A representation of a MerkleMountainRange leaf struct MmrLeaf { // the leftmost index of a node uint256 k_index; // The position in the tree uint256 leaf_index; // The hash of the position in the tree bytes32 hash; } struct Iterator { uint256 offset; bytes32[] data; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; /** * @dev Interface of the Gateway Self External Calls. */ interface IDapp { function iReceive( string memory requestSender, bytes memory packet, string memory srcChainId ) external returns (bytes memory); function iAck(uint256 requestIdentifier, bool execFlags, bytes memory execData) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import "./Utils.sol"; /** * @dev Interface of the Gateway Self External Calls. */ interface IGateway { // requestMetadata = abi.encodePacked( // uint256 destGasLimit; // uint256 destGasPrice; // uint256 ackGasLimit; // uint256 ackGasPrice; // uint256 relayerFees; // uint8 ackType; // bool isReadCall; // bytes asmAddress; // ) function iSend( uint256 version, uint256 routeAmount, string calldata routeRecipient, string calldata destChainId, bytes calldata requestMetadata, bytes calldata requestPacket ) external payable returns (uint256); function setDappMetadata(string memory feePayerAddress) external payable returns (uint256); function currentVersion() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; library Utils { // This is used purely to avoid stack too deep errors // represents everything about a given validator set struct ValsetArgs { // the validators in this set, represented by an Ethereum address address[] validators; // the powers of the given validators in the same order as above uint64[] powers; // the nonce of this validator set uint256 valsetNonce; } struct RequestPayload { uint256 routeAmount; uint256 requestIdentifier; uint256 requestTimestamp; string srcChainId; address routeRecipient; string destChainId; address asmAddress; string requestSender; address handlerAddress; bytes packet; bool isReadCall; } struct CrossChainAckPayload { uint256 requestIdentifier; uint256 ackRequestIdentifier; string destChainId; address requestSender; bytes execData; bool execFlag; } enum AckType { NO_ACK, ACK_ON_SUCCESS, ACK_ON_ERROR, ACK_ON_BOTH } error IncorrectCheckpoint(); error InvalidValsetNonce(uint256 newNonce, uint256 currentNonce); error MalformedNewValidatorSet(); error MalformedCurrentValidatorSet(); error InsufficientPower(uint64 cumulativePower, uint64 powerThreshold); error InvalidSignature(); // constants string constant MSG_PREFIX = "\x19Ethereum Signed Message:\n32"; // The number of 'votes' required to execute a valset // update or batch execution, set to 2/3 of 2^32 uint64 constant CONSTANT_POWER_THRESHOLD = 2791728742; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IAdapter } from "../interfaces/IAdapter.sol"; abstract contract Adapter is IAdapter { mapping(uint256 => mapping(uint256 => bytes32)) private _hashes; /// @inheritdoc IAdapter function getHash(uint256 domain, uint256 id) public view returns (bytes32) { return _hashes[domain][id]; } function _storeHashes(uint256 domain, uint256[] memory ids, bytes32[] memory hashes) internal { for (uint256 i = 0; i < ids.length; ) { _storeHash(domain, ids[i], hashes[i]); unchecked { ++i; } } } function _storeHash(uint256 domain, uint256 id, bytes32 hash) internal { bytes32 currentHash = _hashes[domain][id]; if (currentHash != hash) { _hashes[domain][id] = hash; emit HashStored(id, hash); } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IAMB } from "./IAMB.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract AMBAdapter is BlockHashAdapter { string public constant PROVIDER = "amb"; IAMB public immutable AMB; address public immutable REPORTER; bytes32 public immutable SOURCE_CHAIN_ID; error ArrayLengthMissmatch(); error UnauthorizedAMB(address sender, address expectedSender); error UnauthorizedChainId(bytes32 sourceChainId, bytes32 expectedSourceChainId); error UnauthorizedHashReporter(address reporter, address expectedReporter); constructor(address amb, address reporter, bytes32 sourceChainId) { AMB = IAMB(amb); REPORTER = reporter; SOURCE_CHAIN_ID = sourceChainId; } modifier onlyValid() { bytes32 ambSourceChainId = AMB.messageSourceChainId(); address ambMessageSender = AMB.messageSender(); if (msg.sender != address(AMB)) revert UnauthorizedAMB(msg.sender, address(AMB)); if (ambSourceChainId != SOURCE_CHAIN_ID) revert UnauthorizedChainId(ambSourceChainId, SOURCE_CHAIN_ID); if (ambMessageSender != REPORTER) revert UnauthorizedHashReporter(ambMessageSender, REPORTER); _; } function storeHashes(uint256[] memory ids, bytes32[] memory _hashes) public onlyValid { if (ids.length != _hashes.length) revert ArrayLengthMissmatch(); _storeHashes(uint256(SOURCE_CHAIN_ID), ids, _hashes); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Reporter } from "../Reporter.sol"; import { AMBAdapter } from "./AMBAdapter.sol"; import { IAdapter } from "../../interfaces/IAdapter.sol"; import { IAMB } from "./IAMB.sol"; contract AMBReporter is Reporter { string public constant PROVIDER = "amb"; address public immutable AMB; uint256 public immutable GAS; uint256 public immutable TARGET_CHAIN_ID; error InvalidToChainId(uint256 chainId, uint256 expectedChainId); constructor( address headerStorage, address yaho, address amb, uint256 targetChainId, uint256 gas ) Reporter(headerStorage, yaho) { AMB = amb; TARGET_CHAIN_ID = targetChainId; GAS = gas; } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { if (targetChainId != TARGET_CHAIN_ID) revert InvalidToChainId(targetChainId, TARGET_CHAIN_ID); bytes memory payload = abi.encodeCall(AMBAdapter.storeHashes, (ids, hashes)); return IAMB(AMB).requireToPassMessage(adapter, payload, GAS); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; interface IAMB { function messageSender() external view returns (address); function messageSourceChainId() external view returns (bytes32); function requireToPassMessage(address _contract, bytes memory _data, uint256 _gas) external returns (bytes32); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IAMB } from "../IAMB.sol"; import { AMBAdapter } from "../AMBAdapter.sol"; contract MockAMB is IAMB { address private sender; bytes32 private immutable chainId = 0x0000000000000000000000000000000000000000000000000000000000007A69; event MessagePassed(address sender, bytes data); error TransactionFailed(); function messageSender() external view returns (address) { return sender; } function messageSourceChainId() external pure returns (bytes32) { return chainId; } function requireToPassMessage(address _contract, bytes memory _data, uint256) external returns (bytes32) { sender = msg.sender; (bool success, bytes memory returnData) = _contract.call(_data); if (!success) revert TransactionFailed(); emit MessagePassed(sender, _data); delete sender; return keccak256(returnData); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { AxelarExecutable } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract AxelarAdapter is BlockHashAdapter, Ownable, AxelarExecutable { string public constant PROVIDER = "axelar"; mapping(bytes32 => bytes32) public enabledReporters; mapping(bytes32 => uint256) public chainIds; error UnauthorizedAxelarReceive(); error ExecutionWithTokenNotSupported(); event ReporterSet(uint256 indexed chainId, string name, string indexed reporter); constructor(address axelarGateway) AxelarExecutable(axelarGateway) {} // solhint-disable no-empty-blocks function setReporterByChain( uint256 chainId, string calldata chainName, string calldata reporter ) external onlyOwner { bytes32 chainNameHash = keccak256(bytes(chainName)); enabledReporters[chainNameHash] = keccak256(bytes(reporter)); chainIds[chainNameHash] = chainId; emit ReporterSet(chainId, chainName, reporter); } function _execute( string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) internal override { bytes32 chainNameHash = keccak256(bytes(sourceChain)); bytes32 expectedSourceAddressHash = enabledReporters[chainNameHash]; uint256 sourceChainId = chainIds[chainNameHash]; if (expectedSourceAddressHash != keccak256(bytes(sourceAddress)) || sourceChainId == 0) { revert UnauthorizedAxelarReceive(); } (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(payload, (uint256[], bytes32[])); _storeHashes(sourceChainId, ids, hashes); } function _executeWithToken( string calldata /* sourceChain */, string calldata /* sourceAddress */, bytes calldata /* payload */, string calldata /* tokenSymbol */, uint256 /* amount */ ) internal pure override { revert ExecutionWithTokenNotSupported(); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IAxelarGateway } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; import { IAxelarGasService } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol"; import { Reporter } from "../Reporter.sol"; contract AxelarReporter is Reporter, Ownable { using Strings for uint256; string public constant PROVIDER = "axelar"; bytes32 private constant NULL_STRING = keccak256(""); IAxelarGateway public immutable AXELAR_GATEWAY; IAxelarGasService public immutable AXELAR_GAS_SERVICE; mapping(uint256 => string) public chainNames; error ChainIdNotSupported(uint256 chainId); event ChainNameSet(uint256 indexed chainId, string indexed chainName); constructor( address headerStorage, address yaho, address axelarGateway, address axelarGasService ) Reporter(headerStorage, yaho) { AXELAR_GATEWAY = IAxelarGateway(axelarGateway); AXELAR_GAS_SERVICE = IAxelarGasService(axelarGasService); } function setChainNameByChainId(uint256 chainId, string calldata chainName) external onlyOwner { chainNames[chainId] = chainName; emit ChainNameSet(chainId, chainName); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { string memory targetChainName = chainNames[targetChainId]; if (keccak256(abi.encode(targetChainName)) == NULL_STRING) revert ChainIdNotSupported(targetChainId); string memory sAdapter = uint256(uint160(adapter)).toHexString(20); bytes memory payload = abi.encode(ids, hashes); if (msg.value > 0) { AXELAR_GAS_SERVICE.payNativeGasForContractCall{ value: msg.value }( address(this), targetChainName, sAdapter, payload, msg.sender ); } AXELAR_GATEWAY.callContract(targetChainName, sAdapter, payload); return bytes32(0); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol"; import { Adapter } from "./Adapter.sol"; import { IBlockHashAdapter } from "../interfaces/IBlockHashAdapter.sol"; abstract contract BlockHashAdapter is IBlockHashAdapter, Adapter { using RLPReader for RLPReader.RLPItem; /// @inheritdoc IBlockHashAdapter function proveAncestralBlockHashes(uint256 chainId, bytes[] memory blockHeaders) external { for (uint256 i = 0; i < blockHeaders.length; i++) { RLPReader.RLPItem memory blockHeaderRLP = RLPReader.toRlpItem(blockHeaders[i]); if (!blockHeaderRLP.isList()) revert InvalidBlockHeaderRLP(); RLPReader.RLPItem[] memory blockHeaderContent = blockHeaderRLP.toList(); bytes32 blockParent = bytes32(blockHeaderContent[0].toUint()); uint256 blockNumber = uint256(blockHeaderContent[8].toUint()); bytes32 blockHash = keccak256(blockHeaders[i]); bytes32 storedBlockHash = getHash(chainId, blockNumber); if (blockHash != storedBlockHash) revert ConflictingBlockHeader(blockNumber, blockHash, storedBlockHash); _storeHash(chainId, blockNumber - 1, blockParent); } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IMessageReceiverApp } from "./interfaces/IMessageReceiverApp.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract CelerAdapter is BlockHashAdapter, Ownable, IMessageReceiverApp { string public constant PROVIDER = "celer"; address public immutable CELER_BUS; mapping(uint64 => address) public enabledReporters; error UnauthorizedCelerReceive(); event ReporterSet(uint64 indexed chainId, address indexed reporter); constructor(address celerBus) { CELER_BUS = celerBus; } function executeMessage( address sender, uint64 srcChainId, bytes calldata message, address /* executor */ ) external payable returns (ExecutionStatus) { address expectedReporter = enabledReporters[srcChainId]; if (msg.sender != CELER_BUS || sender != expectedReporter) revert UnauthorizedCelerReceive(); (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(message, (uint256[], bytes32[])); _storeHashes(srcChainId, ids, hashes); return ExecutionStatus.Success; } function setReporterByChainId(uint64 chainId, address reporter) external onlyOwner { enabledReporters[chainId] = reporter; emit ReporterSet(chainId, reporter); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Reporter } from "../Reporter.sol"; import { IMessageBus } from "./interfaces/IMessageBus.sol"; contract CelerReporter is Reporter { string public constant PROVIDER = "celer"; IMessageBus public immutable CELER_BUS; constructor(address headerStorage, address yaho, address celerBus) Reporter(headerStorage, yaho) { CELER_BUS = IMessageBus(celerBus); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { bytes memory payload = abi.encode(ids, hashes); CELER_BUS.sendMessage{ value: msg.value }(adapter, targetChainId, payload); return bytes32(0); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; interface IMessageBus { /** * @notice Send a message to a contract on another chain. * Sender needs to make sure the uniqueness of the message Id, which is computed as * hash(type.MessageOnly, sender, receiver, srcChainId, srcTxHash, dstChainId, message). * If messages with the same Id are sent, only one of them will succeed at dst chain.. * A fee is charged in the native gas token. * @param _receiver The address of the destination app contract. * @param _dstChainId The destination chain ID. * @param _message Arbitrary message bytes to be decoded by the destination app contract. */ function sendMessage(address _receiver, uint256 _dstChainId, bytes calldata _message) external payable; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; interface IMessageReceiverApp { enum ExecutionStatus { Fail, // execution failed, finalized Success, // execution succeeded, finalized Retry // execution rejected, can retry later } /** * @notice Called by MessageBus to execute a message * @param _sender The address of the source app contract * @param _srcChainId The source chain ID where the transfer is originated from * @param _message Arbitrary message bytes originated from and encoded by the source app contract * @param _executor Address who called the MessageBus execution function */ function executeMessage( address _sender, uint64 _srcChainId, bytes calldata _message, address _executor ) external payable returns (ExecutionStatus); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { CCIPReceiver } from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol"; import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract CCIPAdapter is BlockHashAdapter, Ownable, CCIPReceiver { string public constant PROVIDER = "ccip"; mapping(uint64 => address) public enabledReporters; mapping(uint64 => uint256) public chainIds; error UnauthorizedCCIPReceive(); event ReporterSet(uint256 indexed chainId, uint64 indexed chainSelector, address indexed reporter); constructor(address ccipRouter) CCIPReceiver(ccipRouter) {} // solhint-disable no-empty-blocks function setReporterByChain(uint256 chainId, uint64 chainSelector, address reporter) external onlyOwner { enabledReporters[chainSelector] = reporter; chainIds[chainSelector] = chainId; emit ReporterSet(chainId, chainSelector, reporter); } function _ccipReceive(Client.Any2EVMMessage memory message) internal override { // NOTE: validity of `msg.sender` is ensured by `CCIPReceiver` prior this internal function invocation address sender = abi.decode(message.sender, (address)); if (enabledReporters[message.sourceChainSelector] != sender) revert UnauthorizedCCIPReceive(); uint256 sourceChainId = chainIds[message.sourceChainSelector]; (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(message.data, (uint256[], bytes32[])); _storeHashes(sourceChainId, ids, hashes); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IRouterClient } from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol"; import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; import { Reporter } from "../Reporter.sol"; contract CCIPReporter is Reporter, Ownable { string public constant PROVIDER = "ccip"; IRouterClient public immutable CCIP_ROUTER; uint256 public fee; mapping(uint256 => uint64) public chainSelectors; error ChainSelectorNotAvailable(); event ChainSelectorSet(uint256 indexed chainId, uint64 indexed chainSelector); event FeeSet(uint256 fee); constructor(address headerStorage, address yaho, address ccipRouter) Reporter(headerStorage, yaho) { CCIP_ROUTER = IRouterClient(ccipRouter); } function setChainSelectorByChainId(uint256 chainId, uint64 chainSelector) external onlyOwner { chainSelectors[chainId] = chainSelector; emit ChainSelectorSet(chainId, chainSelector); } function setFee(uint256 fee_) external onlyOwner { fee = fee_; emit FeeSet(fee_); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { uint64 targetChainSelector = chainSelectors[targetChainId]; if (targetChainSelector == 0) revert ChainSelectorNotAvailable(); bytes memory payload = abi.encode(ids, hashes); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(adapter), data: payload, tokenAmounts: new Client.EVMTokenAmount[](0), // Empty array - no tokens are transferred extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({ gasLimit: 200_000, strict: false })), feeToken: address(0) // Pay fees with native }); CCIP_ROUTER.ccipSend{ value: fee }(targetChainSelector, message); return bytes32(0); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IXReceiver } from "@connext/interfaces/core/IXReceiver.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract ConnextAdapter is BlockHashAdapter, Ownable, IXReceiver { string public constant PROVIDER = "connext"; address public immutable CONNEXT; mapping(uint32 => address) public enabledReporters; mapping(uint32 => uint256) public chainIds; error UnauthorizedConnextReceive(); event ReporterSet(uint256 indexed chainId, uint32 indexed domainId, address indexed reporter); constructor(address connext) { CONNEXT = connext; } function setReporterByChain(uint256 chainId, uint32 domainId, address reporter) external onlyOwner { enabledReporters[domainId] = reporter; chainIds[domainId] = chainId; emit ReporterSet(chainId, domainId, reporter); } function xReceive( bytes32 /* transferId_ */, uint256 /* amount_ */, address /* asset_ */, address originSender, uint32 origin, bytes memory callData ) external returns (bytes memory) { if (msg.sender != CONNEXT || enabledReporters[origin] != originSender) revert UnauthorizedConnextReceive(); uint256 sourceChainId = chainIds[origin]; (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(callData, (uint256[], bytes32[])); _storeHashes(sourceChainId, ids, hashes); return ""; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IConnext } from "@connext/interfaces/core/IConnext.sol"; import { Reporter } from "../Reporter.sol"; contract ConnextReporter is Reporter, Ownable { string public constant PROVIDER = "connext"; IConnext public immutable CONNEXT; mapping(uint256 => uint32) public domainIds; error DomainIdNotAvailable(); event DomainIdSet(uint256 indexed chainId, uint32 indexed domainId); event ConnextTransfer(bytes32 transferId); constructor(address headerStorage, address yaho, address connext) Reporter(headerStorage, yaho) { CONNEXT = IConnext(connext); } function setDomainIdByChainId(uint256 chainId, uint32 domainId) external onlyOwner { domainIds[chainId] = domainId; emit DomainIdSet(chainId, domainId); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { uint32 targetDomainId = domainIds[targetChainId]; if (targetDomainId == 0) revert DomainIdNotAvailable(); bytes memory payload = abi.encode(ids, hashes); bytes32 transferId = CONNEXT.xcall{ value: msg.value }( targetDomainId, // _destination: Domain ID of the destination chain adapter, // _to: address of the target contract address(0), // _asset: use address zero for 0-value transfers msg.sender, // _delegate: address that can revert or forceLocal on destination 0, // _amount: 0 because no funds are being transferred 0, // _slippage: can be anything between 0-10000 because no funds are being transferred payload // _callData: the encoded calldata to send ); emit ConnextTransfer(transferId); return bytes32(0); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; import { IDeBridgeGate } from "./interfaces/IDeBridgeGate.sol"; import { ICallProxy } from "./interfaces/ICallProxy.sol"; contract DeBridgeAdapter is BlockHashAdapter, Ownable { string public constant PROVIDER = "debridge"; IDeBridgeGate public immutable deBridgeGate; mapping(uint256 => address) public enabledReporters; error NotProxy(); error UnauthorizedSender(address sender, uint256 chainId); event ReporterSet(uint256 indexed chainId, address indexed reporter); constructor(address _deBridgeGate) { deBridgeGate = IDeBridgeGate(_deBridgeGate); } function setReporterByChainId(uint256 chainId, address reporter) external onlyOwner { enabledReporters[chainId] = reporter; emit ReporterSet(chainId, reporter); } function storeHashes(uint256[] calldata ids, bytes32[] calldata hashes) external { ICallProxy callProxy = ICallProxy(deBridgeGate.callProxy()); if (address(callProxy) != msg.sender) revert NotProxy(); address sender = address(uint160(bytes20(callProxy.submissionNativeSender()))); uint256 chainId = callProxy.submissionChainIdFrom(); if (enabledReporters[chainId] != sender) revert UnauthorizedSender(sender, chainId); _storeHashes(chainId, ids, hashes); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Reporter } from "../Reporter.sol"; import { IDeBridgeGate } from "./interfaces/IDeBridgeGate.sol"; contract DeBridgeReporter is Reporter, Ownable { string public constant PROVIDER = "debridge"; IDeBridgeGate public immutable deBridgeGate; uint256 fee; event FeeSet(uint256 fee); constructor(address headerStorage, address yaho, address deBridgeGate_) Reporter(headerStorage, yaho) { deBridgeGate = IDeBridgeGate(deBridgeGate_); } function setFee(uint256 fee_) external onlyOwner { fee = fee_; emit FeeSet(fee); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { bytes32 submissionId = deBridgeGate.sendMessage{ value: fee }( targetChainId, abi.encodePacked(adapter), abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", ids, hashes) ); return submissionId; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; interface ICallProxy { /// @dev Chain from which the current submission is received function submissionChainIdFrom() external view returns (uint256); /// @dev Native sender of the current submission function submissionNativeSender() external view returns (bytes memory); /// @dev Used for calls where native asset transfer is involved. /// @param _reserveAddress Receiver of the tokens if the call to _receiver fails /// @param _receiver Contract to be called /// @param _data Call data /// @param _flags Flags to change certain behavior of this function, see Flags library for more details /// @param _nativeSender Native sender /// @param _chainIdFrom Id of a chain that originated the request function call( address _reserveAddress, address _receiver, bytes memory _data, uint256 _flags, bytes memory _nativeSender, uint256 _chainIdFrom ) external payable returns (bool); /// @dev Used for calls where ERC20 transfer is involved. /// @param _token Asset address /// @param _reserveAddress Receiver of the tokens if the call to _receiver fails /// @param _receiver Contract to be called /// @param _data Call data /// @param _flags Flags to change certain behavior of this function, see Flags library for more details /// @param _nativeSender Native sender /// @param _chainIdFrom Id of a chain that originated the request function callERC20( address _token, address _reserveAddress, address _receiver, bytes memory _data, uint256 _flags, bytes memory _nativeSender, uint256 _chainIdFrom ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; interface IDeBridgeGate { /* ========== STRUCTS ========== */ struct TokenInfo { uint256 nativeChainId; bytes nativeAddress; } struct DebridgeInfo { uint256 chainId; // native chain id uint256 maxAmount; // maximum amount to transfer uint256 balance; // total locked assets uint256 lockedInStrategies; // total locked assets in strategy (AAVE, Compound, etc) address tokenAddress; // asset address on the current chain uint16 minReservesBps; // minimal hot reserves in basis points (1/10000) bool exist; } struct DebridgeFeeInfo { uint256 collectedFees; // total collected fees uint256 withdrawnFees; // fees that already withdrawn mapping(uint256 => uint256) getChainFee; // whether the chain for the asset is supported } struct ChainSupportInfo { uint256 fixedNativeFee; // transfer fixed fee bool isSupported; // whether the chain for the asset is supported uint16 transferFeeBps; // transfer fee rate nominated in basis points (1/10000) of transferred amount } struct DiscountInfo { uint16 discountFixBps; // fix discount in BPS uint16 discountTransferBps; // transfer % discount in BPS } /// @param executionFee Fee paid to the transaction executor. /// @param fallbackAddress Receiver of the tokens if the call fails. struct SubmissionAutoParamsTo { uint256 executionFee; uint256 flags; bytes fallbackAddress; bytes data; } /// @param executionFee Fee paid to the transaction executor. /// @param fallbackAddress Receiver of the tokens if the call fails. struct SubmissionAutoParamsFrom { uint256 executionFee; uint256 flags; address fallbackAddress; bytes data; bytes nativeSender; } struct FeeParams { uint256 receivedAmount; uint256 fixFee; uint256 transferFee; bool useAssetFee; bool isNativeToken; } /* ========== PUBLIC VARS GETTERS ========== */ /// @dev Returns whether the transfer with the submissionId was claimed. /// submissionId is generated in getSubmissionIdFrom function isSubmissionUsed(bytes32 submissionId) external view returns (bool); /// @dev Returns native token info by wrapped token address function getNativeInfo(address token) external view returns (uint256 nativeChainId, bytes memory nativeAddress); /// @dev Returns address of the proxy to execute user's calls. function callProxy() external view returns (address); /// @dev Fallback fixed fee in native asset, used if a chain fixed fee is set to 0 function globalFixedNativeFee() external view returns (uint256); /// @dev Fallback transfer fee in BPS, used if a chain transfer fee is set to 0 function globalTransferFeeBps() external view returns (uint16); /* ========== FUNCTIONS ========== */ /// @dev Submits the message to the deBridge infrastructure to be broadcasted to another supported blockchain (identified by _dstChainId) /// with the instructions to call the _targetContractAddress contract using the given _targetContractCalldata /// @notice NO ASSETS ARE BROADCASTED ALONG WITH THIS MESSAGE /// @notice DeBridgeGate only accepts submissions with msg.value (native ether) covering a small protocol fee /// (defined in the globalFixedNativeFee property). Any excess amount of ether passed to this function is /// included in the message as the execution fee - the amount deBridgeGate would give as an incentive to /// a third party in return for successful claim transaction execution on the destination chain. /// @notice DeBridgeGate accepts a set of flags that control the behaviour of the execution. This simple method /// sets the default set of flags: REVERT_IF_EXTERNAL_FAIL, PROXY_WITH_SENDER /// @param _dstChainId ID of the destination chain. /// @param _targetContractAddress A contract address to be called on the destination chain /// @param _targetContractCalldata Calldata to execute against the target contract on the destination chain function sendMessage( uint256 _dstChainId, bytes memory _targetContractAddress, bytes memory _targetContractCalldata ) external payable returns (bytes32 submissionId); /// @dev Submits the message to the deBridge infrastructure to be broadcasted to another supported blockchain (identified by _dstChainId) /// with the instructions to call the _targetContractAddress contract using the given _targetContractCalldata /// @notice NO ASSETS ARE BROADCASTED ALONG WITH THIS MESSAGE /// @notice DeBridgeGate only accepts submissions with msg.value (native ether) covering a small protocol fee /// (defined in the globalFixedNativeFee property). Any excess amount of ether passed to this function is /// included in the message as the execution fee - the amount deBridgeGate would give as an incentive to /// a third party in return for successful claim transaction execution on the destination chain. /// @notice DeBridgeGate accepts a set of flags that control the behaviour of the execution. This simple method /// sets the default set of flags: REVERT_IF_EXTERNAL_FAIL, PROXY_WITH_SENDER /// @param _dstChainId ID of the destination chain. /// @param _targetContractAddress A contract address to be called on the destination chain /// @param _targetContractCalldata Calldata to execute against the target contract on the destination chain /// @param _flags A bitmask of toggles listed in the Flags library /// @param _referralCode Referral code to identify this submission function sendMessage( uint256 _dstChainId, bytes memory _targetContractAddress, bytes memory _targetContractCalldata, uint256 _flags, uint32 _referralCode ) external payable returns (bytes32 submissionId); /// @dev This method is used for the transfer of assets [from the native chain](https://docs.debridge.finance/the-core-protocol/transfers#transfer-from-native-chain). /// It locks an asset in the smart contract in the native chain and enables minting of deAsset on the secondary chain. /// @param _tokenAddress Asset identifier. /// @param _amount Amount to be transferred (note: the fee can be applied). /// @param _chainIdTo Chain id of the target chain. /// @param _receiver Receiver address. /// @param _permitEnvelope Permit for approving the spender by signature. bytes (amount + deadline + signature) /// @param _useAssetFee use assets fee for pay protocol fix (work only for specials token) /// @param _referralCode Referral code /// @param _autoParams Auto params for external call in target network function send( address _tokenAddress, uint256 _amount, uint256 _chainIdTo, bytes memory _receiver, bytes memory _permitEnvelope, bool _useAssetFee, uint32 _referralCode, bytes calldata _autoParams ) external payable returns (bytes32 submissionId); /// @dev Is used for transfers [into the native chain](https://docs.debridge.finance/the-core-protocol/transfers#transfer-from-secondary-chain-to-native-chain) /// to unlock the designated amount of asset from collateral and transfer it to the receiver. /// @param _debridgeId Asset identifier. /// @param _amount Amount of the transferred asset (note: the fee can be applied). /// @param _chainIdFrom Chain where submission was sent /// @param _receiver Receiver address. /// @param _nonce Submission id. /// @param _signatures Validators signatures to confirm /// @param _autoParams Auto params for external call function claim( bytes32 _debridgeId, uint256 _amount, uint256 _chainIdFrom, address _receiver, uint256 _nonce, bytes calldata _signatures, bytes calldata _autoParams ) external; /// @dev Withdraw collected fees to feeProxy /// @param _debridgeId Asset identifier. function withdrawFee(bytes32 _debridgeId) external; /// @dev Returns asset fixed fee value for specified debridge and chainId. /// @param _debridgeId Asset identifier. /// @param _chainId Chain id. function getDebridgeChainAssetFixedFee(bytes32 _debridgeId, uint256 _chainId) external view returns (uint256); /* ========== EVENTS ========== */ /// @dev Emitted once the tokens are sent from the original(native) chain to the other chain; the transfer tokens /// are expected to be claimed by the users. event Sent( bytes32 submissionId, bytes32 indexed debridgeId, uint256 amount, bytes receiver, uint256 nonce, uint256 indexed chainIdTo, uint32 referralCode, FeeParams feeParams, bytes autoParams, address nativeSender // bool isNativeToken //added to feeParams ); /// @dev Emitted once the tokens are transferred and withdrawn on a target chain event Claimed( bytes32 submissionId, bytes32 indexed debridgeId, uint256 amount, address indexed receiver, uint256 nonce, uint256 indexed chainIdFrom, bytes autoParams, bool isNativeToken ); /// @dev Emitted when new asset support is added. event PairAdded( bytes32 debridgeId, address tokenAddress, bytes nativeAddress, uint256 indexed nativeChainId, uint256 maxAmount, uint16 minReservesBps ); event MonitoringSendEvent(bytes32 submissionId, uint256 nonce, uint256 lockedOrMintedAmount, uint256 totalSupply); event MonitoringClaimEvent(bytes32 submissionId, uint256 lockedOrMintedAmount, uint256 totalSupply); /// @dev Emitted when the asset is allowed/disallowed to be transferred to the chain. event ChainSupportUpdated(uint256 chainId, bool isSupported, bool isChainFrom); /// @dev Emitted when the supported chains are updated. event ChainsSupportUpdated(uint256 chainIds, ChainSupportInfo chainSupportInfo, bool isChainFrom); /// @dev Emitted when the new call proxy is set. event CallProxyUpdated(address callProxy); /// @dev Emitted when the transfer request is executed. event AutoRequestExecuted(bytes32 submissionId, bool indexed success, address callProxy); /// @dev Emitted when a submission is blocked. event Blocked(bytes32 submissionId); /// @dev Emitted when a submission is unblocked. event Unblocked(bytes32 submissionId); /// @dev Emitted when fee is withdrawn. event WithdrawnFee(bytes32 debridgeId, uint256 fee); /// @dev Emitted when globalFixedNativeFee and globalTransferFeeBps are updated. event FixedNativeFeeUpdated(uint256 globalFixedNativeFee, uint256 globalTransferFeeBps); /// @dev Emitted when globalFixedNativeFee is updated by feeContractUpdater event FixedNativeFeeAutoUpdated(uint256 globalFixedNativeFee); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { ILightClient, LightClientUpdate } from "./interfaces/IDendrETH.sol"; import { SSZ } from "../Telepathy/libraries/SimpleSerialize.sol"; import { Merkle } from "../Electron/lib/Merkle.sol"; import { Receipt } from "../Electron/lib/Receipt.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract DendrETHAdapter is BlockHashAdapter { bytes32 internal constant MESSAGE_DISPATCHED_EVENT_SIG = 0x218247aabc759e65b5bb92ccc074f9d62cd187259f2a0984c3c9cf91f67ff7cf; // keccak256("MessageDispatched(uint256,(uint256,uint256,uint256,address,address,bytes,address[],address[]))"); address public immutable DENDRETH_ADDRESS; address public immutable SOURCE_YAHO; uint256 public immutable SOURCE_CHAIN_ID; error InvalidUpdate(); error BlockHeaderNotAvailable(uint256 slot); error InvalidBlockNumberProof(); error InvalidBlockHashProof(); error InvalidReceiptsRoot(); error ErrorParseReceipt(); error InvalidEventSignature(); error InvalidEventSource(); constructor(address dendrETHAddress, uint256 sourceChainId, address sourceYaho) { DENDRETH_ADDRESS = dendrETHAddress; SOURCE_CHAIN_ID = sourceChainId; SOURCE_YAHO = sourceYaho; } /// @notice Stores the block header for a given block only if it exists // in the DendrETH Light Client for the SOURCE_CHAIN_ID. function storeBlockHeader( uint64 slot, uint256 blockNumber, bytes32[] calldata blockNumberProof, bytes32 blockHash, bytes32[] calldata blockHashProof ) external { ILightClient lightClient = ILightClient(DENDRETH_ADDRESS); bytes32 blockHeaderRoot = lightClient.optimisticHeaders(_getIndex(slot, lightClient)); if (!SSZ.verifyBlockNumber(blockNumber, blockNumberProof, blockHeaderRoot)) { revert InvalidBlockNumberProof(); } if (!SSZ.verifyBlockHash(blockHash, blockHashProof, blockHeaderRoot)) { revert InvalidBlockHashProof(); } _storeHash(SOURCE_CHAIN_ID, blockNumber, blockHash); } /// @notice Updates DendrETH Light client and stores the given block // for the update function storeBlockHeader( uint64 slot, uint256 blockNumber, bytes32[] calldata blockNumberProof, bytes32 blockHash, bytes32[] calldata blockHashProof, LightClientUpdate calldata update ) external { ILightClient lightClient = ILightClient(DENDRETH_ADDRESS); lightClient.light_client_update(update); if (lightClient.optimisticHeaderSlot() != slot) { revert InvalidUpdate(); } bytes32 blockHeaderRoot = lightClient.optimisticHeaderRoot(); if (!SSZ.verifyBlockNumber(blockNumber, blockNumberProof, blockHeaderRoot)) { revert InvalidBlockNumberProof(); } if (!SSZ.verifyBlockHash(blockHash, blockHashProof, blockHeaderRoot)) { revert InvalidBlockHashProof(); } _storeHash(SOURCE_CHAIN_ID, blockNumber, blockHash); } function verifyAndStoreDispatchedMessage( uint64 srcSlot, uint64 txSlot, bytes32[] memory receiptsRootProof, bytes32 receiptsRoot, bytes[] memory receiptProof, bytes memory txIndexRLPEncoded, uint256 logIndex ) external { ILightClient lightClient = ILightClient(DENDRETH_ADDRESS); bytes32 blockHeaderRoot = lightClient.optimisticHeaders(_getIndex(srcSlot, lightClient)); bool isValidReceiptsRoot = Merkle.verifyReceiptsRoot( receiptsRootProof, receiptsRoot, srcSlot, txSlot, blockHeaderRoot ); if (!isValidReceiptsRoot) revert InvalidReceiptsRoot(); Receipt.ParsedReceipt memory parsedReceipt = Receipt.parseReceipt( receiptsRoot, receiptProof, txIndexRLPEncoded, logIndex ); if (!parsedReceipt.isValid) revert ErrorParseReceipt(); if (bytes32(parsedReceipt.topics[0]) != MESSAGE_DISPATCHED_EVENT_SIG) revert InvalidEventSignature(); if (parsedReceipt.eventSource != SOURCE_YAHO) revert InvalidEventSource(); uint256 messageId = uint256(parsedReceipt.topics[1]); bytes32 messageHash = keccak256(parsedReceipt.data); _storeHash(SOURCE_CHAIN_ID, messageId, messageHash); } function _getIndex(uint64 slot, ILightClient lightClient) internal view returns (uint256) { uint256 currentIndex = lightClient.currentIndex(); uint256 i = currentIndex; bool found = false; do { if (slot == lightClient.optimisticSlots(i)) { found = true; break; } if (i == 0) { i = 32; } i--; } while (i != currentIndex); if (!found) { revert BlockHeaderNotAvailable(slot); } return i; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.20; struct LightClientUpdate { bytes32 attestedHeaderRoot; uint256 attestedHeaderSlot; bytes32 finalizedHeaderRoot; bytes32 finalizedExecutionStateRoot; uint256[2] a; uint256[2][2] b; uint256[2] c; } interface ILightClient { function currentIndex() external view returns (uint256); function optimisticHeaders(uint256 index) external view returns (bytes32); function optimisticHeaderRoot() external view returns (bytes32); function optimisticSlots(uint256 index) external view returns (uint256); function optimisticHeaderSlot() external view returns (uint256); function light_client_update(LightClientUpdate calldata update) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; import { ILightClient } from "./interfaces/ILightClient.sol"; import { Merkle } from "./lib/Merkle.sol"; import { Receipt } from "./lib/Receipt.sol"; contract ElectronAdapter is BlockHashAdapter, Ownable { ILightClient public lightClient; bytes32 public constant EVENT_SIG_HASH = 0xf7df17dce0093aedfcbae24b4f04e823f9e863c97986ab1ba6c5267ace49ddea; // HeaderStored(uint256,bytes32) address public eventSource; // HeaderStorage contract address uint256 public chainIdSource; // Chain ID of HeaderStorage error MissingHeaderOnLC(uint256 slot); error InvalidReceiptsRoot(); error ErrorParseReceipt(); error InvalidEventSource(); error InvalidEventSignature(); constructor(address _lightClientAddress, address _eventSourceAddress, uint256 _chainIdSource) Ownable() { lightClient = ILightClient(_lightClientAddress); eventSource = _eventSourceAddress; chainIdSource = _chainIdSource; } function storeHash(bytes calldata _metadata) external { ( bytes memory lcSlotTxSlotPack, bytes32[] memory receiptsRootProof, bytes32 receiptsRoot, bytes[] memory receiptProof, bytes memory txIndexRLP, uint256 logIndex ) = abi.decode(_metadata, (bytes, bytes32[], bytes32, bytes[], bytes, uint256)); (uint64 lcSlot, uint64 txSlot) = abi.decode(lcSlotTxSlotPack, (uint64, uint64)); bytes32 headerRoot = lightClient.headers(lcSlot); if (headerRoot == bytes32(0)) revert MissingHeaderOnLC(lcSlot); bool isValidReceiptsRoot = Merkle.verifyReceiptsRoot( receiptsRootProof, receiptsRoot, lcSlot, txSlot, headerRoot ); if (!isValidReceiptsRoot) revert InvalidReceiptsRoot(); Receipt.ParsedReceipt memory parsedReceipt = Receipt.parseReceipt( receiptsRoot, receiptProof, txIndexRLP, logIndex ); if (!parsedReceipt.isValid) revert ErrorParseReceipt(); // ensure correct event source if (parsedReceipt.eventSource != eventSource) revert InvalidEventSource(); // ensure correct event signature if (bytes32(parsedReceipt.topics[0]) != EVENT_SIG_HASH) revert InvalidEventSignature(); uint256 blockNumber = uint256(parsedReceipt.topics[1]); bytes32 blockHash = parsedReceipt.topics[2]; _storeHash(chainIdSource, blockNumber, blockHash); } function setLightClientAddress(address lightclientAddress) external onlyOwner { lightClient = ILightClient(lightclientAddress); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; interface ILightClient { function headers(uint256 slot) external view returns (bytes32); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; library Merkle { uint256 internal constant SLOTS_PER_HISTORICAL_ROOT = 8192; function restoreMerkleRoot( bytes32[] memory branch, bytes32 leaf, uint256 index ) internal pure returns (bytes32 root) { require(index < 2 ** branch.length, "invalid leaf index"); bytes32 combineHash = leaf; uint256 curIndex = index; for (uint256 i = 0; i < branch.length; ) { if (curIndex % 2 == 0) combineHash = sha256(bytes.concat(combineHash, branch[i])); else combineHash = sha256(bytes.concat(branch[i], combineHash)); curIndex /= 2; unchecked { i++; } } root = combineHash; } function verifyReceiptsRoot( bytes32[] memory receiptsRootBranch, bytes32 receiptsRoot, uint64 lcSlot, uint64 txSlot, bytes32 headerRoot ) internal pure returns (bool) { uint256 index; if (txSlot == lcSlot) { // BeaconBlock -> BeaconBody -> ExecutionPayload -> ReceiptsRoot index = 6435; } else if (lcSlot - txSlot <= SLOTS_PER_HISTORICAL_ROOT) { uint256[] memory historicalRootGindexes = new uint256[](2); historicalRootGindexes[0] = 37; historicalRootGindexes[1] = calculateArrayGindex(txSlot % SLOTS_PER_HISTORICAL_ROOT); uint256[] memory receiptGindexes = new uint256[](3); receiptGindexes[0] = 11; receiptGindexes[1] = concatGindices(historicalRootGindexes); receiptGindexes[2] = 6435; // BeaconBlock -> BeaconState -> HistoricalRoots -> BeaconBlock -> BeaconBody -> ExecutionPayload -> ReceiptsRoot index = concatGindices(receiptGindexes); } else if (lcSlot - txSlot > SLOTS_PER_HISTORICAL_ROOT) { revert("txSlot lags by >8192 blocks. Not supported."); } else { revert("txSlot can't be greater than lightclient slot"); } bytes32 computedRoot = restoreMerkleRoot(receiptsRootBranch, receiptsRoot, calculateIndex(index)); return computedRoot == headerRoot; } function concatGindices(uint256[] memory gindices) public pure returns (uint256) { uint256 result = 1; // Start with binary "1" for (uint i = 0; i < gindices.length; i++) { uint256 gindex = gindices[i]; uint256 gindexWithoutLeadingOne = gindex & ((1 << (bitLength(gindex) - 1)) - 1); result = (result << (bitLength(gindex) - 1)) | gindexWithoutLeadingOne; } return result; } function bitLength(uint256 number) internal pure returns (uint256) { if (number == 0) { return 0; } uint256 length = 0; while (number > 0) { length++; number >>= 1; } return length; } function calculateArrayGindex(uint256 elementIndex) internal pure returns (uint256) { uint256 gindex = 1; uint256 depth = 0; while ((1 << depth) < SLOTS_PER_HISTORICAL_ROOT) { depth++; } for (uint256 d = 0; d < depth; d++) { gindex = (gindex << 1) | ((elementIndex >> (depth - d - 1)) & 1); } return gindex; } function calculateIndex(uint256 gindex) internal pure returns (uint256 index) { uint256 depth = floorLog2(gindex); index = gindex % (2 ** depth); } function floorLog2(uint256 x) internal pure returns (uint256) { require(x > 0, "Input must be greater than zero"); uint256 result = 0; while (x > 1) { x >>= 1; result++; } return result; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import "@polytope-labs/solidity-merkle-trees/src/MerklePatricia.sol"; import { RLPReader } from "@polytope-labs/solidity-merkle-trees/src/trie/ethereum/RLPReader.sol"; library Receipt { error UnsupportedTxType(); struct ParsedReceipt { bool isValid; bytes32[] topics; bytes data; address eventSource; } using RLPReader for RLPReader.RLPItem; function parseReceipt( bytes32 receiptsRoot, bytes[] memory receiptProof, bytes memory txIndexRLP, uint256 logIndex ) internal pure returns (ParsedReceipt memory) { bytes32[] memory eventTopics; bytes memory eventData; address eventSource; ParsedReceipt memory parsedReceipt = ParsedReceipt({ isValid: false, topics: eventTopics, data: eventData, eventSource: eventSource }); // verify the merkle patricia proof for receipt and get the value for key `txIndexRLP` bytes[] memory keys = new bytes[](1); keys[0] = txIndexRLP; bytes memory value = MerklePatricia.VerifyEthereumProof(receiptsRoot, receiptProof, keys)[0].value; // https://eips.ethereum.org/EIPS/eip-2718 // There are 3 possible receipt types: Legacy, 0x01 or 0x02 (More types can be added in future EIPs) uint256 offset; if (value[0] == 0x01 || value[0] == 0x02) { // first byte represents the TransactionType offset = 1; } else if (value[0] >= 0xc0) { // LegacyReceipt offset = 0; } else { revert UnsupportedTxType(); } // memory pointer to the RLP Receipt uint256 memPtr; assembly { memPtr := add(value, add(0x20, mul(0x01, offset))) } // read Receipt as a list of RLPItem RLPReader.RLPItem[] memory receiptItems = RLPReader.RLPItem(value.length - offset, memPtr).toList(); if (receiptItems.length != 4) return parsedReceipt; RLPReader.RLPItem[] memory logs = receiptItems[3].toList(); if (logIndex >= logs.length) return parsedReceipt; RLPReader.RLPItem[] memory targetLog = logs[logIndex].toList(); // extract eventSource address eventSource = targetLog[0].toAddress(); // extract event topics RLPReader.RLPItem[] memory topicsItems = targetLog[1].toList(); bytes32[] memory topics_ = new bytes32[](topicsItems.length); for (uint256 i = 0; i < topicsItems.length; ) { topics_[i] = bytes32(topicsItems[i].toBytes()); unchecked { i++; } } eventTopics = topics_; // extract event data eventData = targetLog[2].toBytes(); return ParsedReceipt({ isValid: true, topics: eventTopics, data: eventData, eventSource: eventSource }); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { BaseIsmpModule, IncomingPostRequest } from "@polytope-labs/ismp-solidity/interfaces/IIsmpModule.sol"; import { StateMachine } from "@polytope-labs/ismp-solidity/interfaces/StateMachine.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract HyperbridgeAdapter is BlockHashAdapter, Ownable, BaseIsmpModule { mapping(bytes32 => bytes32) public enabledReporters; mapping(bytes32 => uint256) public chainIds; error UnauthorizedRequest(); event ReporterSet(uint256 indexed chainId, address indexed reporter); function setReporterByChain(uint256 chainId, address reporter) external onlyOwner { bytes32 stateMachineId = keccak256(StateMachine.evm(chainId)); enabledReporters[stateMachineId] = keccak256(abi.encodePacked(reporter)); chainIds[stateMachineId] = chainId; emit ReporterSet(chainId, reporter); } /// Process incoming requests function onAccept(IncomingPostRequest calldata incoming) external override onlyHost { bytes32 stateMachineId = keccak256(incoming.request.source); if (enabledReporters[stateMachineId] != keccak256(incoming.request.from)) revert UnauthorizedRequest(); uint256 sourceChainId = chainIds[stateMachineId]; (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(incoming.request.body, (uint256[], bytes32[])); _storeHashes(sourceChainId, ids, hashes); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { StateMachine } from "@polytope-labs/ismp-solidity/interfaces/StateMachine.sol"; import { BaseIsmpModule } from "@polytope-labs/ismp-solidity/interfaces/IIsmpModule.sol"; import { IDispatcher, DispatchPost } from "@polytope-labs/ismp-solidity/interfaces/IDispatcher.sol"; import { Reporter } from "../Reporter.sol"; contract HyperbridgeReporter is Reporter, Ownable, BaseIsmpModule { string public constant PROVIDER = "hyperbridge"; constructor(address headerStorage, address yaho) Reporter(headerStorage, yaho) { address host = host(); address feeToken = IDispatcher(host).feeToken(); // approve the host to spend infinitely IERC20(feeToken).approve(host, type(uint256).max); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { bytes memory payload = abi.encode(ids, hashes); DispatchPost memory post = DispatchPost({ // recipient chain dest: StateMachine.evm(targetChainId), // recipient contract to: abi.encodePacked(adapter), // serialized message body: payload, // no timeouts timeout: 0, // requests will be self-relayed fee: 0, // relayer fee refunds payer: address(this) }); // dispatch cross-chain message, returns request id return IDispatcher(host()).dispatch(post); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IMessageRecipient } from "@hyperlane-xyz/core/contracts/interfaces/IMessageRecipient.sol"; import { IInterchainSecurityModule } from "@hyperlane-xyz/core/contracts/interfaces/IInterchainSecurityModule.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract HyperlaneAdapter is BlockHashAdapter, Ownable, IMessageRecipient { string public constant PROVIDER = "hyperlane"; address public immutable HYPERLANE_MAILBOX; mapping(uint32 => bytes32) public enabledReporters; mapping(uint32 => uint256) public chainIds; error UnauthorizedHyperlaneReceive(); event ReporterSet(uint256 indexed chainId, uint32 indexed domain, bytes32 indexed reporter); constructor(address hyperlaneMailbox) { HYPERLANE_MAILBOX = hyperlaneMailbox; } function handle(uint32 origin, bytes32 sender, bytes calldata message) external payable { if (msg.sender != HYPERLANE_MAILBOX || enabledReporters[origin] != sender) revert UnauthorizedHyperlaneReceive(); uint256 sourceChainId = chainIds[origin]; (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(message, (uint256[], bytes32[])); _storeHashes(sourceChainId, ids, hashes); } function setReporterByChain(uint256 chainId, uint32 domain, bytes32 reporter) external onlyOwner { enabledReporters[domain] = reporter; chainIds[domain] = chainId; emit ReporterSet(chainId, domain, reporter); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IMailbox } from "@hyperlane-xyz/core/contracts/interfaces/IMailbox.sol"; import { TypeCasts } from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; import { Reporter } from "../Reporter.sol"; contract HyperlaneReporter is Reporter, Ownable { using TypeCasts for address; string public constant PROVIDER = "hyperlane"; IMailbox public immutable HYPERLANE_MAILBOX; mapping(uint256 => uint32) public domains; error DomainNotAvailable(); event DomainSet(uint256 indexed chainId, uint32 indexed domain); constructor(address headerStorage, address yaho, address hyperlaneMailbox) Reporter(headerStorage, yaho) { HYPERLANE_MAILBOX = IMailbox(hyperlaneMailbox); } function setDomainByChainId(uint256 chainId, uint32 domain) external onlyOwner { domains[chainId] = domain; emit DomainSet(chainId, domain); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { uint32 targetDomain = domains[targetChainId]; if (targetDomain == 0) revert DomainNotAvailable(); bytes memory payload = abi.encode(ids, hashes); HYPERLANE_MAILBOX.dispatch{ value: msg.value }(targetDomain, adapter.addressToBytes32(), payload); return bytes32(0); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; struct MessagingParams { uint32 dstEid; bytes32 receiver; bytes message; bytes options; bool payInLzToken; } struct MessagingReceipt { bytes32 guid; uint64 nonce; MessagingFee fee; } struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; } struct Origin { uint32 srcEid; bytes32 sender; uint64 nonce; } interface ILayerZeroEndpointV2 { event PacketSent(bytes encodedPayload, bytes options, address sendLibrary); event PacketVerified(Origin origin, address receiver, bytes32 payloadHash); event PacketDelivered(Origin origin, address receiver); event LzReceiveAlert( address indexed receiver, address indexed executor, Origin origin, bytes32 guid, uint256 gas, uint256 value, bytes message, bytes extraData, bytes reason ); event LzTokenSet(address token); event DelegateSet(address sender, address delegate); function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory); function send( MessagingParams calldata _params, address _refundAddress ) external payable returns (MessagingReceipt memory); function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external; function verifiable(Origin calldata _origin, address _receiver) external view returns (bool); function initializable(Origin calldata _origin, address _receiver) external view returns (bool); function lzReceive( Origin calldata _origin, address _receiver, bytes32 _guid, bytes calldata _message, bytes calldata _extraData ) external payable; // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external; function setLzToken(address _lzToken) external; function lzToken() external view returns (address); function nativeToken() external view returns (address); function setDelegate(address _delegate) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Origin } from "./ILayerZeroEndpointV2.sol"; interface ILayerZeroReceiver { function lzReceive( Origin calldata _origin, bytes32 _guid, bytes calldata _message, address _executor, bytes calldata _extraData ) external payable; function allowInitializePath(Origin calldata _origin) external view returns (bool); function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { OAppCore } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OAppCore.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; import { ILayerZeroReceiver } from "./interfaces/ILayerZeroReceiver.sol"; import { Origin } from "./interfaces/ILayerZeroEndpointV2.sol"; contract LayerZeroAdapter is BlockHashAdapter, Ownable, ILayerZeroReceiver, OAppCore { string public constant PROVIDER = "layer-zero"; address public immutable LAYER_ZERO_ENDPOINT; mapping(uint32 => address) public enabledReporters; mapping(uint32 => uint256) public chainIds; error UnauthorizedLayerZeroReceive(); event ReporterSet(uint256 indexed chainId, uint32 indexed endpointId, address indexed reporter); constructor(address lzEndpoint, address delegate) OAppCore(lzEndpoint, delegate) { LAYER_ZERO_ENDPOINT = lzEndpoint; } function lzReceive( Origin calldata _origin, bytes32 /* _guid*/, bytes calldata _message, address /* _executor*/, bytes calldata /* _extraData*/ ) external payable { if ( msg.sender != LAYER_ZERO_ENDPOINT || enabledReporters[_origin.srcEid] != address(uint160(uint256(_origin.sender))) ) revert UnauthorizedLayerZeroReceive(); (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(_message, (uint256[], bytes32[])); _storeHashes(chainIds[_origin.srcEid], ids, hashes); } function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public pure override returns (uint64 nonce) { return 0; } function allowInitializePath(Origin calldata origin) public view override returns (bool) { return peers[origin.srcEid] == origin.sender; } function oAppVersion() public pure virtual override returns (uint64 senderVersion, uint64 receiverVersion) { return (1, 1); } function setReporterByChain(uint256 chainId, uint32 endpointId, address reporter) external onlyOwner { enabledReporters[endpointId] = reporter; chainIds[endpointId] = chainId; emit ReporterSet(chainId, endpointId, reporter); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { ILayerZeroEndpointV2, MessagingParams, MessagingFee, MessagingReceipt } from "./interfaces/ILayerZeroEndpointV2.sol"; import { Reporter } from "../Reporter.sol"; import { OptionsBuilder } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/libs/OptionsBuilder.sol"; import { OAppCore } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OAppCore.sol"; contract LayerZeroReporter is Reporter, Ownable, OAppCore { using OptionsBuilder for bytes; string public constant PROVIDER = "layer-zero"; ILayerZeroEndpointV2 public immutable LAYER_ZERO_ENDPOINT; mapping(uint256 => uint32) public endpointIds; uint128 public fee; address refundAddress; error EndpointIdNotAvailable(); event EndpointIdSet(uint256 indexed chainId, uint32 indexed endpointId); event FeeSet(uint256 fee); constructor( address headerStorage, address yaho, address lzEndpoint, address delegate, address refundAddress_, uint128 defaultFee_ ) Reporter(headerStorage, yaho) OAppCore(lzEndpoint, delegate) { refundAddress = refundAddress_; fee = defaultFee_; LAYER_ZERO_ENDPOINT = ILayerZeroEndpointV2(lzEndpoint); } function setEndpointIdByChainId(uint256 chainId, uint32 endpointId) external onlyOwner { endpointIds[chainId] = endpointId; emit EndpointIdSet(chainId, endpointId); } function setFee(uint128 fee_) external onlyOwner { fee = fee_; emit FeeSet(fee); } function setDefaultRefundAddress(address refundAddress_) external onlyOwner { refundAddress = refundAddress_; } function oAppVersion() public pure virtual override returns (uint64 senderVersion, uint64 receiverVersion) { return (1, 1); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { uint32 targetEndpointId = endpointIds[targetChainId]; if (targetEndpointId == 0) revert EndpointIdNotAvailable(); bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(fee, 0); bytes memory message = abi.encode(ids, hashes); MessagingParams memory params = MessagingParams( targetEndpointId, bytes32(abi.encode(adapter)), message, options, false // receiver in lz Token ); // solhint-disable-next-line check-send-result MessagingFee memory msgFee = LAYER_ZERO_ENDPOINT.quote(params, address(this)); MessagingReceipt memory receipt = LAYER_ZERO_ENDPOINT.send{ value: msgFee.nativeFee }(params, refundAddress); return receipt.guid; } receive() external payable {} }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Adapter } from "../Adapter.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract MockAdapter is Adapter, BlockHashAdapter { error LengthMismatch(); function setHashes(uint256 domain, uint256[] memory ids, bytes32[] memory hashes) external { if (ids.length != hashes.length) revert LengthMismatch(); for (uint256 i = 0; i < ids.length; i++) { _storeHash(domain, ids[i], hashes[i]); } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Reporter } from "../Reporter.sol"; import { IAdapter } from "../../interfaces/IAdapter.sol"; contract MockReporter is Reporter { constructor(address headerStorage, address yaho) Reporter(headerStorage, yaho) {} function _dispatch(uint256, address, uint256[] memory, bytes32[] memory) internal override returns (bytes32) {} }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; interface ICrossDomainMessenger { function sendMessage(address _target, bytes calldata _message, uint32 _minGasLimit) external payable; function xDomainMessageSender() external view returns (address); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Reporter } from "../Reporter.sol"; import { IAdapter } from "../../interfaces/IAdapter.sol"; import { ICrossDomainMessenger } from "./interfaces/ICrossDomainMessenger.sol"; contract L1CrossDomainMessengerReporter is Reporter { string public constant PROVIDER = "optimism"; // The first 1.92 million gas on L2 is free. See here: // https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions uint32 internal constant GAS_LIMIT = 1_920_000; ICrossDomainMessenger public immutable L1_CROSS_DOMAIN_MESSENGER; uint256 public immutable TARGET_CHAIN_ID; error InvalidToChainId(uint256 chainId, uint256 expectedChainId); constructor( address headerStorage, address yaho, address l1CrossDomainMessenger, uint256 targetChainId ) Reporter(headerStorage, yaho) { L1_CROSS_DOMAIN_MESSENGER = ICrossDomainMessenger(l1CrossDomainMessenger); TARGET_CHAIN_ID = targetChainId; } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { if (targetChainId != TARGET_CHAIN_ID) revert InvalidToChainId(targetChainId, TARGET_CHAIN_ID); bytes memory message = abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", ids, hashes); L1_CROSS_DOMAIN_MESSENGER.sendMessage(adapter, message, GAS_LIMIT); return bytes32(0); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { ICrossDomainMessenger } from "./interfaces/ICrossDomainMessenger.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract L2CrossDomainMessengerAdapter is BlockHashAdapter { string public constant PROVIDER = "optimism"; ICrossDomainMessenger public immutable L2_CROSS_DOMAIN_MESSENGER; address public immutable REPORTER; uint256 public immutable SOURCE_CHAIN_ID; error ArrayLengthMissmatch(); error UnauthorizedHashReporter(address sender, address expectedSender); error UnauthorizedL2CrossDomainMessenger(address domainMessageSender, address expectedDomainMessageSender); constructor(ICrossDomainMessenger l2CrossDomainMessenger_, address reporter, uint256 sourceChainId) { L2_CROSS_DOMAIN_MESSENGER = ICrossDomainMessenger(l2CrossDomainMessenger_); REPORTER = reporter; SOURCE_CHAIN_ID = sourceChainId; } modifier onlyValid() { if (msg.sender != address(L2_CROSS_DOMAIN_MESSENGER)) revert UnauthorizedL2CrossDomainMessenger(msg.sender, address(L2_CROSS_DOMAIN_MESSENGER)); address xDomainMessageSender = L2_CROSS_DOMAIN_MESSENGER.xDomainMessageSender(); if (xDomainMessageSender != REPORTER) revert UnauthorizedHashReporter(xDomainMessageSender, REPORTER); _; } function storeHashes(uint256[] memory ids, bytes32[] memory hashes) external onlyValid { if (ids.length != hashes.length) revert ArrayLengthMissmatch(); _storeHashes(SOURCE_CHAIN_ID, ids, hashes); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; interface IErc20Vault { function pegIn( uint256 tokenAmount, address tokenAddress, string memory destinationAddress, bytes memory userData, bytes4 destinationChainId ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; interface IPToken { function redeem( uint256 amount, bytes memory userData, string memory underlyingAssetRecipient, bytes4 destinationChainId ) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IERC777Recipient } from "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; import { IERC1820RegistryUpgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract PNetworkAdapter is BlockHashAdapter, Ownable { string public constant PROVIDER = "pnetwork"; address public immutable VAULT; address public immutable TOKEN; IERC1820RegistryUpgradeable private constant ERC1820 = IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); mapping(bytes4 => address) public enabledReporters; mapping(bytes4 => uint256) public chainIds; error InvalidSender(address sender, address expected); error InvalidToken(address token, address expected); error UnauthorizedPNetworkReceive(); event ReporterSet(uint256 indexed chainId, bytes4 indexed networkId, address indexed reporter); modifier onlySupportedToken(address _tokenAddress) { if (_tokenAddress != TOKEN) revert InvalidToken(_tokenAddress, TOKEN); _; } constructor(address pNetworkVault, address pNetworkToken) { VAULT = pNetworkVault; TOKEN = pNetworkToken; ERC1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); } // Implement the ERC777TokensRecipient interface function tokensReceived( address, address from, address, uint256, bytes calldata data, bytes calldata ) external onlySupportedToken(msg.sender) { if (from != VAULT) revert InvalidSender(from, VAULT); (, bytes memory userData, bytes4 networkId, address sender) = abi.decode( data, (bytes1, bytes, bytes4, address) ); if (enabledReporters[networkId] != sender) revert UnauthorizedPNetworkReceive(); (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(userData, (uint256[], bytes32[])); _storeHashes(chainIds[networkId], ids, hashes); } function setReporterByChain(uint256 chainId, bytes4 networkId, address reporter) external onlyOwner { enabledReporters[networkId] = reporter; chainIds[networkId] = chainId; emit ReporterSet(chainId, networkId, reporter); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Reporter } from "../Reporter.sol"; import { IErc20Vault } from "./interfaces/IErc20Vault.sol"; import { IPToken } from "./interfaces/IPToken.sol"; contract PNetworkReporter is Reporter, Ownable { string public constant PROVIDER = "pnetwork"; uint256 private constant SWAP_AMOUNT = 1; address public immutable VAULT; address public immutable TOKEN; mapping(uint256 => bytes4) public networkIds; error NetworkIdNotAvailable(); event NetworkIdSet(uint256 indexed chainId, bytes4 indexed networkId); constructor( address headerStorage, address yaho, address pNetworkVault, address pNetworkToken ) Reporter(headerStorage, yaho) { VAULT = pNetworkVault; TOKEN = pNetworkToken; } function setNetworkIdByChainId(uint256 chainId, bytes4 networkId) external onlyOwner { networkIds[chainId] = networkId; emit NetworkIdSet(chainId, networkId); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { bytes4 targetNetworkId = networkIds[targetChainId]; if (targetNetworkId == 0) revert NetworkIdNotAvailable(); bytes memory payload = abi.encode(ids, hashes); if (VAULT != address(0)) { IERC20(TOKEN).approve(VAULT, SWAP_AMOUNT); IErc20Vault(VAULT).pegIn(SWAP_AMOUNT, TOKEN, _toAsciiString(adapter), payload, targetNetworkId); } else { IPToken(TOKEN).redeem(SWAP_AMOUNT, payload, _toAsciiString(adapter), targetNetworkId); } return bytes32(0); } function _toAsciiString(address x) internal pure returns (string memory) { bytes memory s = new bytes(40); for (uint i = 0; i < 20; i++) { bytes1 b = bytes1(uint8(uint(uint160(x)) / (2 ** (8 * (19 - i))))); bytes1 hi = bytes1(uint8(b) / 16); bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); s[2 * i] = _char(hi); s[2 * i + 1] = _char(lo); } return string(s); } function _char(bytes1 b) internal pure returns (bytes1 c) { if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); else return bytes1(uint8(b) + 0x57); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IHeaderStorage } from "../interfaces/IHeaderStorage.sol"; import { IReporter } from "../interfaces/IReporter.sol"; import { IAdapter } from "../interfaces/IAdapter.sol"; abstract contract Reporter is IReporter { address public immutable HEADER_STORAGE; address public immutable YAHO; modifier onlyYaho() { if (msg.sender != YAHO) revert NotYaho(msg.sender, YAHO); _; } constructor(address headerStorage, address yaho) { HEADER_STORAGE = headerStorage; YAHO = yaho; } /// @inheritdoc IReporter function dispatchBlocks( uint256 targetChainId, IAdapter adapter, uint256[] memory blockNumbers ) external payable returns (bytes32) { bytes32[] memory blockHeaders = IHeaderStorage(HEADER_STORAGE).storeBlockHeaders(blockNumbers); for (uint256 i = 0; i < blockNumbers.length; ) { emit BlockDispatched(targetChainId, adapter, blockNumbers[i], blockHeaders[i]); unchecked { ++i; } } return _dispatch(targetChainId, address(adapter), blockNumbers, blockHeaders); } /// @inheritdoc IReporter function dispatchMessages( uint256 targetChainId, IAdapter adapter, uint256[] memory messageIds, bytes32[] memory messageHashes ) external payable onlyYaho returns (bytes32) { for (uint256 i = 0; i < messageIds.length; ) { emit MessageDispatched(targetChainId, adapter, messageIds[i], messageHashes[i]); unchecked { ++i; } } return _dispatch(targetChainId, address(adapter), messageIds, messageHashes); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal virtual returns (bytes32); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IDapp } from "@routerprotocol/evm-gateway-contracts/contracts/IDapp.sol"; import { IGateway } from "@routerprotocol/evm-gateway-contracts/contracts/IGateway.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract RouterAdapter is BlockHashAdapter, Ownable, IDapp { string public constant PROVIDER = "router"; IGateway public immutable ROUTER_GATEWAY; mapping(bytes32 => bytes32) public enabledReporters; mapping(bytes32 => uint256) public chainIds; error UnauthorizedRouterReceive(); error RouterIAckNotSupported(); event ReporterSet(uint256 indexed chainId, string name, string indexed reporter); constructor(address routerGateway) { ROUTER_GATEWAY = IGateway(routerGateway); } function setReporterByChain( uint256 chainId, string calldata chainIdStr, string calldata reporter ) external onlyOwner { bytes32 chainIdHash = keccak256(bytes(chainIdStr)); enabledReporters[chainIdHash] = keccak256(bytes(reporter)); chainIds[chainIdHash] = chainId; emit ReporterSet(chainId, chainIdStr, reporter); } function iReceive( string calldata requestSender, bytes calldata packet, string calldata srcChainId ) external override returns (bytes memory) { bytes32 chainIdHash = keccak256(bytes(srcChainId)); uint256 sourceChainId = chainIds[chainIdHash]; if ( msg.sender != address(ROUTER_GATEWAY) || enabledReporters[chainIdHash] != keccak256(bytes(requestSender)) || sourceChainId == 0 ) revert UnauthorizedRouterReceive(); (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(packet, (uint256[], bytes32[])); _storeHashes(sourceChainId, ids, hashes); return hex""; } function iAck(uint256, bool, bytes memory) external pure override { revert RouterIAckNotSupported(); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Reporter } from "../Reporter.sol"; import { IGateway } from "@routerprotocol/evm-gateway-contracts/contracts/IGateway.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; interface IRouterGateway is IGateway { function iSendDefaultFee() external view returns (uint256); function currentVersion() external view returns (uint256); } interface IRouterGasStation { function payFee(string memory destChainId, uint256 destGasLimit) external payable returns (uint256); function getNativeFees(string memory destChainId, uint256 destGasLimit) external view returns (uint256); } contract RouterReporter is Reporter, Ownable { using Strings for uint256; string public constant PROVIDER = "router"; bytes32 private constant NULL_STRING = keccak256(""); IRouterGateway public immutable ROUTER_GATEWAY; IRouterGasStation public immutable ROUTER_GAS_STATION; uint256 public immutable CURRENT_GATEWAY_VERSION; string public feePayer; mapping(uint256 => string) public chainIds; error ChainIdNotSupported(uint256 chainId); error InsufficientFeePassed(); event ChainIdSet(uint256 indexed chainId, string indexed chainIdString); event FeePayerSet(string oldFeePayer, string feePayer); constructor( address headerStorage, address yaho, address routerGateway, address routerGasStation, string memory routerFeePayer ) Reporter(headerStorage, yaho) { ROUTER_GATEWAY = IRouterGateway(routerGateway); ROUTER_GAS_STATION = IRouterGasStation(routerGasStation); feePayer = routerFeePayer; CURRENT_GATEWAY_VERSION = ROUTER_GATEWAY.currentVersion(); ROUTER_GATEWAY.setDappMetadata(routerFeePayer); } function setRouterFeePayer(string memory routerFeePayer) external onlyOwner { string memory oldFeePayer = feePayer; feePayer = routerFeePayer; ROUTER_GATEWAY.setDappMetadata(routerFeePayer); emit FeePayerSet(oldFeePayer, routerFeePayer); } function setChainIdStringByChainId(uint256 chainId, string calldata chainIdString) external onlyOwner { chainIds[chainId] = chainIdString; emit ChainIdSet(chainId, chainIdString); } function getRequestMetadata() internal pure returns (bytes memory) { bytes memory requestMetadata = abi.encodePacked( uint64(200_000), uint64(0), uint64(0), uint64(0), uint128(0), uint8(0), false, string("") ); return requestMetadata; } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { string memory targetChainIdStr = chainIds[targetChainId]; if (keccak256(abi.encode(targetChainIdStr)) == NULL_STRING) revert ChainIdNotSupported(targetChainId); bytes memory payload = abi.encode(ids, hashes); string memory stringAdapter = uint256(uint160(adapter)).toHexString(20); bytes memory requestPacket = abi.encode(stringAdapter, payload); bytes memory requestMetadata = getRequestMetadata(); uint256 iSendFee = ROUTER_GATEWAY.iSendDefaultFee(); uint256 crosstalkFee = ROUTER_GAS_STATION.getNativeFees(targetChainIdStr, 200_000); if (address(this).balance < (iSendFee + crosstalkFee)) revert InsufficientFeePassed(); ROUTER_GAS_STATION.payFee{ value: crosstalkFee }(targetChainIdStr, 200_000); ROUTER_GATEWAY.iSend{ value: iSendFee }( CURRENT_GATEWAY_VERSION, 0, string(""), targetChainIdStr, requestMetadata, requestPacket ); return bytes32(0); } receive() external payable {} }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; interface ISpectre { function blockHeaderRoots(uint256 slot) external view returns (bytes32); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; library Merkle { uint256 internal constant SLOTS_PER_HISTORICAL_ROOT = 8192; // BeaconState -> BlockRoots uint256 internal constant BLOCK_ROOTS_GINDEX = 37; // BeaconBlock -> BeaconState uint256 internal constant STATE_ROOT_GINDEX = 11; // BeaconBlock -> BeaconBody -> ExecutionPayload -> ReceiptsRoot uint256 internal constant RECEIPT_ROOT_GINDEX = 6435; function restoreMerkleRoot( bytes32[] memory branch, bytes32 leaf, uint256 index ) internal pure returns (bytes32 root) { require(index < 2 ** branch.length, "invalid leaf index"); bytes32 combineHash = leaf; uint256 curIndex = index; for (uint256 i = 0; i < branch.length; ) { if (curIndex % 2 == 0) combineHash = sha256(bytes.concat(combineHash, branch[i])); else combineHash = sha256(bytes.concat(branch[i], combineHash)); curIndex /= 2; unchecked { i++; } } root = combineHash; } function verifyReceiptsRoot( bytes32[] memory receiptsRootBranch, bytes32 receiptsRoot, uint64 lcSlot, uint64 txSlot, bytes32 headerRoot ) internal pure returns (bool) { uint256 index; if (txSlot == lcSlot) { index = RECEIPT_ROOT_GINDEX; } else if (lcSlot - txSlot <= SLOTS_PER_HISTORICAL_ROOT) { uint256[] memory blockRootsGindex = new uint256[](2); blockRootsGindex[0] = BLOCK_ROOTS_GINDEX; blockRootsGindex[1] = calculateArrayGindex(txSlot % SLOTS_PER_HISTORICAL_ROOT); uint256[] memory receiptGindexes = new uint256[](3); receiptGindexes[0] = STATE_ROOT_GINDEX; receiptGindexes[1] = concatGindices(blockRootsGindex); receiptGindexes[2] = RECEIPT_ROOT_GINDEX; // BeaconBlock -> BeaconState -> HistoricalRoots -> BeaconBlock -> BeaconBody -> ExecutionPayload -> ReceiptsRoot index = concatGindices(receiptGindexes); } else if (lcSlot - txSlot > SLOTS_PER_HISTORICAL_ROOT) { revert("txSlot lags by >8192 blocks. Not supported."); } else { revert("txSlot can't be greater than lightclient slot"); } bytes32 computedRoot = restoreMerkleRoot(receiptsRootBranch, receiptsRoot, calculateIndex(index)); return computedRoot == headerRoot; } function concatGindices(uint256[] memory gindices) public pure returns (uint256) { uint256 result = 1; // Start with binary "1" for (uint i = 0; i < gindices.length; i++) { uint256 gindex = gindices[i]; uint256 gindexWithoutLeadingOne = gindex & ((1 << (bitLength(gindex) - 1)) - 1); result = (result << (bitLength(gindex) - 1)) | gindexWithoutLeadingOne; } return result; } function bitLength(uint256 number) internal pure returns (uint256) { if (number == 0) { return 0; } uint256 length = 0; while (number > 0) { length++; number >>= 1; } return length; } function calculateArrayGindex(uint256 elementIndex) internal pure returns (uint256) { uint256 gindex = 1; uint256 depth = 0; while ((1 << depth) < SLOTS_PER_HISTORICAL_ROOT) { depth++; } for (uint256 d = 0; d < depth; d++) { gindex = (gindex << 1) | ((elementIndex >> (depth - d - 1)) & 1); } return gindex; } function calculateIndex(uint256 gindex) internal pure returns (uint256 index) { uint256 depth = floorLog2(gindex); index = gindex % (2 ** depth); } function floorLog2(uint256 x) internal pure returns (uint256) { require(x > 0, "Input must be greater than zero"); uint256 result = 0; while (x > 1) { x >>= 1; result++; } return result; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; contract MockSpectre { mapping(uint256 => bytes32) public blockHeaderRoots; function setRoot(uint256 slot, bytes32 root) external { blockHeaderRoots[slot] = root; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Merkle } from "./lib/Merkle.sol"; import { Receipt } from "../Electron/lib/Receipt.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; import { ISpectre } from "./interfaces/ISpectre.sol"; contract SpectreAdapter is AccessControl, BlockHashAdapter { string public constant PROVIDER = "spectre"; // keccak256("MessageDispatched(uint256,(uint256,uint256,uint256,address,address,bytes,address[],address[]))") bytes32 internal constant MESSAGE_DISPATCHED_EVENT_SIG = 0x218247aabc759e65b5bb92ccc074f9d62cd187259f2a0984c3c9cf91f67ff7cf; address public immutable SOURCE_YAHO; uint256 public immutable SOURCE_CHAIN_ID; address public spectreAddress; error Unauthorized(); error InvalidEventSource(); error InvalidReceiptsRoot(); error ErrorParseReceipt(); error InvalidEventSignature(); error BlockHeaderRootMissing(); constructor(address initialSpectreAddress, uint256 sourceChainId, address sourceYaho) { _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); SOURCE_CHAIN_ID = sourceChainId; SOURCE_YAHO = sourceYaho; spectreAddress = initialSpectreAddress; } modifier onlyAdmin() { if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert Unauthorized(); _; } function changeSpectreAddress(address newSpectreAddress) external onlyAdmin { spectreAddress = newSpectreAddress; } function verifyAndStoreDispatchedMessage( uint64 srcSlot, uint64 txSlot, bytes32[] memory receiptsRootProof, bytes32 receiptsRoot, bytes[] memory receiptProof, bytes memory txIndexRLPEncoded, uint256 logIndex ) external { bytes32 blockHeaderRoot = ISpectre(spectreAddress).blockHeaderRoots(srcSlot); if (blockHeaderRoot == bytes32(0)) revert BlockHeaderRootMissing(); bool isValidReceiptsRoot = Merkle.verifyReceiptsRoot( receiptsRootProof, receiptsRoot, srcSlot, txSlot, blockHeaderRoot ); if (!isValidReceiptsRoot) revert InvalidReceiptsRoot(); Receipt.ParsedReceipt memory parsedReceipt = Receipt.parseReceipt( receiptsRoot, receiptProof, txIndexRLPEncoded, logIndex ); if (!parsedReceipt.isValid) revert ErrorParseReceipt(); if (bytes32(parsedReceipt.topics[0]) != MESSAGE_DISPATCHED_EVENT_SIG) revert InvalidEventSignature(); if (parsedReceipt.eventSource != SOURCE_YAHO) revert InvalidEventSource(); uint256 messageId = uint256(parsedReceipt.topics[1]); bytes32 messageHash = keccak256(parsedReceipt.data); _storeHash(SOURCE_CHAIN_ID, messageId, messageHash); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; /** @title Interface for Bridge contract. @author ChainSafe Systems. */ interface IBridge { /** @notice Initiates a transfer using a specified handler contract. @notice Only callable when Bridge is not paused. @param destinationDomainID ID of chain deposit will be bridged to. @param resourceID ResourceID used to find address of handler to be used for deposit. @param depositData Additional data to be passed to specified handler. @param feeData Additional data to be passed to the fee handler. @notice Emits {Deposit} event with all necessary parameters. */ function deposit( uint8 destinationDomainID, bytes32 resourceID, bytes calldata depositData, bytes calldata feeData ) external payable returns (uint64 depositNonce, bytes memory handlerResponse); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; interface ISygmaAdapter { function storeHashes(address reporter, uint256[] memory ids, bytes32[] memory _hashes) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; import "../BlockHashAdapter.sol"; contract SygmaAdapter is AccessControl, BlockHashAdapter { string public constant PROVIDER = "sygma"; struct Reporter { uint128 chainID; bool enabled; } address public immutable _handler; mapping(address => Reporter) public reporters; error ArrayLengthMismatch(); error Unauthorized(); error InvalidHandler(address handler); error InvalidReporter(address reporter); event ReporterSet(address reporterAddress, uint256 chainID, bool enabled); constructor(address handler) { _handler = handler; _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); } modifier onlyAdmin() { if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert Unauthorized(); _; } function setReporter(address reporterAddress, uint128 chainID, bool enabled) public onlyAdmin { reporters[reporterAddress] = Reporter(chainID, enabled); emit ReporterSet(reporterAddress, chainID, enabled); } function storeHashes(address reporterAddress, uint256[] calldata ids, bytes32[] calldata hashes) public { if (ids.length != hashes.length) revert ArrayLengthMismatch(); if (msg.sender != _handler) revert InvalidHandler(msg.sender); Reporter memory reporter = reporters[reporterAddress]; if (!reporter.enabled) revert InvalidReporter(reporterAddress); uint256 chainID = uint256(reporter.chainID); _storeHashes(chainID, ids, hashes); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Reporter } from "../Reporter.sol"; import { ISygmaAdapter } from "./interfaces/ISygmaAdapter.sol"; import { IBridge } from "./interfaces/IBridge.sol"; contract SygmaReporter is Reporter, Ownable { string public constant PROVIDER = "sygma"; IBridge public immutable BRIDGE; mapping(uint256 => uint8) public domainIds; mapping(uint256 => bytes32) public resourceIds; error DomainIdNotAvailable(); error ResourceIdNotAvailable(); event DomainIdSet(uint256 indexed chainId, uint8 indexed domainId); event ResourceIdSet(uint256 indexed chainId, bytes32 indexed resourceId); constructor(address headerStorage, address yaho, address bridge) Reporter(headerStorage, yaho) { BRIDGE = IBridge(bridge); } function setDomainIdAndResourceIdByChainId(uint256 chainId, uint8 domainId, bytes32 resourceId) external onlyOwner { domainIds[chainId] = domainId; resourceIds[chainId] = resourceId; emit DomainIdSet(chainId, domainId); emit ResourceIdSet(chainId, resourceId); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { uint8 targetDomainId = domainIds[targetChainId]; if (targetDomainId == 0) revert DomainIdNotAvailable(); bytes32 targetResourceId = resourceIds[targetChainId]; if (targetResourceId == bytes32(0)) revert ResourceIdNotAvailable(); bytes memory depositData = abi.encodePacked( // uint256 maxFee uint256(950000), // uint16 len(executeFuncSignature) uint16(4), // bytes executeFuncSignature ISygmaAdapter(address(0)).storeHashes.selector, // uint8 len(executeContractAddress) uint8(20), // bytes executeContractAddress adapter, // uint8 len(executionDataDepositor) uint8(20), // bytes executionDataDepositor address(this), // bytes executionDataDepositor + executionData prepareDepositData(ids, hashes) ); (uint64 nonce, bytes memory handlerResponse) = BRIDGE.deposit{ value: msg.value }( targetDomainId, targetResourceId, depositData, "" // feeData ); return bytes32(keccak256(abi.encode(nonce, handlerResponse))); } function slice(bytes calldata input, uint256 position) public pure returns (bytes memory) { return input[position:]; } function prepareDepositData( uint256[] memory messageIds, bytes32[] memory hashes ) public view returns (bytes memory) { bytes memory encoded = abi.encode(address(0), messageIds, hashes); return this.slice(encoded, 32); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; interface ILightClient { function consistent() external view returns (bool); function head() external view returns (uint256); function headers(uint256 slot) external view returns (bytes32); function executionStateRoots(uint256 slot) external view returns (bytes32); function timestamps(uint256 slot) external view returns (uint256); } contract TelepathyStorage { mapping(uint32 => ILightClient) public lightClients; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; library SSZ { // G-indicies for the BeaconBlockHeader -> bodyRoot -> executionPayload -> {blockNumber, blockHash} uint256 internal constant EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX = 3222; uint256 internal constant EXECUTION_PAYLOAD_BLOCK_HASH_INDEX = 3228; function toLittleEndian(uint256 _v) internal pure returns (bytes32) { _v = ((_v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | ((_v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8); _v = ((_v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | ((_v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16); _v = ((_v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32) | ((_v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32); _v = ((_v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64) | ((_v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64); _v = (_v >> 128) | (_v << 128); return bytes32(_v); } function restoreMerkleRoot( bytes32 _leaf, uint256 _index, bytes32[] memory _branch ) internal pure returns (bytes32) { require(2 ** (_branch.length + 1) > _index, "incorrect branch length or index size"); bytes32 value = _leaf; uint256 i = 0; while (_index != 1) { if (_index % 2 == 1) { value = sha256(bytes.concat(_branch[i], value)); } else { value = sha256(bytes.concat(value, _branch[i])); } _index /= 2; i++; } return value; } function isValidMerkleBranch( bytes32 _leaf, uint256 _index, bytes32[] memory _branch, bytes32 _root ) internal pure returns (bool) { bytes32 restoredMerkleRoot = restoreMerkleRoot(_leaf, _index, _branch); return _root == restoredMerkleRoot; } function verifyBlockNumber( uint256 _blockNumber, bytes32[] memory _blockNumberProof, bytes32 _headerRoot ) internal pure returns (bool) { return isValidMerkleBranch( toLittleEndian(_blockNumber), EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, _blockNumberProof, _headerRoot ); } function verifyBlockHash( bytes32 _blockHash, bytes32[] memory _blockHashProof, bytes32 _headerRoot ) internal pure returns (bool) { return isValidMerkleBranch(_blockHash, EXECUTION_PAYLOAD_BLOCK_HASH_INDEX, _blockHashProof, _headerRoot); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { ILightClient, TelepathyStorage } from "./interfaces/ITelepathy.sol"; import { SSZ } from "./libraries/SimpleSerialize.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract TelepathyAdapter is BlockHashAdapter { error NoLightClientOnChain(uint32 chainId); error InconsistentLightClient(address lightClient); error BlockHeaderNotAvailable(uint256 slot); error InvalidBlockNumberProof(); error InvalidBlockHashProof(); /// @dev The Telepathy Router contains a mapping of chainIds to Light Clients. address public immutable telepathyRouter; constructor(address _telepathyRouter) { telepathyRouter = _telepathyRouter; } /// @notice Stores the block header for a given block only if it exists in the Telepathy /// Light Client for the chainId. function storeBlockHeader( uint32 _chainId, uint64 _slot, uint256 _blockNumber, bytes32[] calldata _blockNumberProof, bytes32 _blockHash, bytes32[] calldata _blockHashProof ) external { ILightClient lightClient = TelepathyStorage(telepathyRouter).lightClients(_chainId); if (address(lightClient) == address(0)) { revert NoLightClientOnChain(_chainId); } if (!lightClient.consistent()) { revert InconsistentLightClient(address(lightClient)); } bytes32 blockHeaderRoot = lightClient.headers(_slot); if (blockHeaderRoot == bytes32(0)) { revert BlockHeaderNotAvailable(_slot); } if (!SSZ.verifyBlockNumber(_blockNumber, _blockNumberProof, blockHeaderRoot)) { revert InvalidBlockNumberProof(); } if (!SSZ.verifyBlockHash(_blockHash, _blockHashProof, blockHeaderRoot)) { revert InvalidBlockHashProof(); } _storeHash(uint256(_chainId), _blockNumber, _blockHash); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; struct Signature { bytes32 r; bytes32 s; uint8 v; uint8 guardianIndex; } struct VM { uint8 version; uint32 timestamp; uint32 nonce; uint16 emitterChainId; bytes32 emitterAddress; uint64 sequence; uint8 consistencyLevel; bytes payload; uint32 guardianSetIndex; Signature[] signatures; bytes32 hash; } interface IWormhole { /// @dev Publish a message to be attested by the Wormhole network. function publishMessage( uint32 nonce, bytes memory payload, uint8 consistencyLevel ) external payable returns (uint64 sequence); /// @dev parseAndVerifyVM serves to parse an encodedVM and wholy validate it for consumption function parseAndVerifyVM( bytes calldata encodedVM ) external view returns (VM memory vm, bool valid, string memory reason); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; import { IWormhole, VM } from "./interfaces/IWormhole.sol"; contract WormholeAdapter is BlockHashAdapter, Ownable { IWormhole public immutable WORMHOLE; mapping(uint32 => bytes32) public enabledReporters; mapping(uint32 => uint256) public chainIds; error InvalidMessage(VM vm, string reason); error UnauthorizedWormholeReceive(); event ReporterSet(uint256 indexed chainId, uint16 indexed endpointId, address indexed reporter); constructor(address wormhole) { WORMHOLE = IWormhole(wormhole); } function setReporterByChain(uint256 chainId, uint16 wormholeChainId, address reporter) external onlyOwner { enabledReporters[wormholeChainId] = bytes32(uint256(uint160(reporter))); chainIds[wormholeChainId] = wormholeChainId; emit ReporterSet(chainId, wormholeChainId, reporter); } function storeHashesByEncodedVM(bytes calldata encodedVM) external { (VM memory vm, bool valid, string memory reason) = WORMHOLE.parseAndVerifyVM(encodedVM); if (!valid) revert InvalidMessage(vm, reason); if (enabledReporters[vm.emitterChainId] != vm.emitterAddress) revert UnauthorizedWormholeReceive(); (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(vm.payload, (uint256[], bytes32[])); _storeHashes(chainIds[vm.emitterChainId], ids, hashes); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Reporter } from "../Reporter.sol"; import { IAdapter } from "../../interfaces/IAdapter.sol"; import { IWormhole } from "./interfaces/IWormhole.sol"; contract WormholeReporter is Reporter { string public constant PROVIDER = "wormhole"; IWormhole public immutable WOMRHOLE; constructor(address headerStorage, address yaho, address wormhole) Reporter(headerStorage, yaho) { WOMRHOLE = IWormhole(wormhole); } function _dispatch( uint256, address, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { bytes memory payload = abi.encode(ids, hashes); uint32 nonce = 0; uint8 consistencyLevel = 201; uint64 sequence = WOMRHOLE.publishMessage(nonce, payload, consistencyLevel); return bytes32(abi.encode(sequence)); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; interface ZetaInterfaces { /** * @dev Use SendInput to interact with the Connector: connector.send(SendInput) */ struct SendInput { /// @dev Chain id of the destination chain. More about chain ids https://docs.zetachain.com/learn/glossary#chain-id uint256 destinationChainId; /// @dev Address receiving the message on the destination chain (expressed in bytes since it can be non-EVM) bytes destinationAddress; /// @dev Gas limit for the destination chain's transaction uint256 destinationGasLimit; /// @dev An encoded, arbitrary message to be parsed by the destination contract bytes message; /// @dev ZETA to be sent cross-chain + ZetaChain gas fees + destination chain gas fees (expressed in ZETA) uint256 zetaValueAndGas; /// @dev Optional parameters for the ZetaChain protocol bytes zetaParams; } /** * @dev Our Connector calls onZetaMessage with this struct as argument */ struct ZetaMessage { bytes zetaTxSenderAddress; uint256 sourceChainId; address destinationAddress; /// @dev Remaining ZETA from zetaValueAndGas after subtracting ZetaChain gas fees and destination gas fees uint256 zetaValue; bytes message; } } interface ZetaConnector { /** * @dev Sending value and data cross-chain is as easy as calling connector.send(SendInput) */ function send(ZetaInterfaces.SendInput calldata input) external; } interface ZetaReceiver { /** * @dev onZetaMessage is called when a cross-chain message reaches a contract */ function onZetaMessage(ZetaInterfaces.ZetaMessage calldata zetaMessage) external; } /** * @dev ZetaTokenConsumer makes it easier to handle the following situations: * - Getting Zeta using native coin (to pay for destination gas while using `connector.send`) * - Getting Zeta using a token (to pay for destination gas while using `connector.send`) * - Getting native coin using Zeta (to return unused destination gas when `onZetaRevert` is executed) * - Getting a token using Zeta (to return unused destination gas when `onZetaRevert` is executed) * @dev The interface can be implemented using different strategies, like UniswapV2, UniswapV3, etc */ interface ZetaTokenConsumer { event EthExchangedForZeta(uint256 amountIn, uint256 amountOut); event TokenExchangedForZeta(address token, uint256 amountIn, uint256 amountOut); event ZetaExchangedForEth(uint256 amountIn, uint256 amountOut); event ZetaExchangedForToken(address token, uint256 amountIn, uint256 amountOut); function getZetaFromEth(address destinationAddress, uint256 minAmountOut) external payable returns (uint256); function getZetaFromToken( address destinationAddress, uint256 minAmountOut, address inputToken, uint256 inputTokenAmount ) external returns (uint256); function getEthFromZeta( address destinationAddress, uint256 minAmountOut, uint256 zetaTokenAmount ) external returns (uint256); function getTokenFromZeta( address destinationAddress, uint256 minAmountOut, address outputToken, uint256 zetaTokenAmount ) external returns (uint256); function hasZetaLiquidity() external view returns (bool); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { ZetaReceiver, ZetaInterfaces } from "./interfaces/ZetaInterfaces.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; contract ZetaAdapter is BlockHashAdapter, Ownable, ZetaReceiver { string public constant PROVIDER = "zeta"; address public immutable ZETA_CONNECTOR; mapping(uint256 => bytes32) public enabledReporters; error UnauthorizedZetaChainReceive(); event ReporterSet(uint256 indexed chainId, address indexed reporter); constructor(address zetaConnector) { ZETA_CONNECTOR = zetaConnector; } function onZetaMessage(ZetaInterfaces.ZetaMessage calldata zetaMessage) external { // NOTE: auth adapted from "ZetaInteractor" contract's "isValidMessageCall" modifier if ( msg.sender != ZETA_CONNECTOR || enabledReporters[zetaMessage.sourceChainId] != keccak256(zetaMessage.zetaTxSenderAddress) ) revert UnauthorizedZetaChainReceive(); (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(zetaMessage.message, (uint256[], bytes32[])); _storeHashes(zetaMessage.sourceChainId, ids, hashes); } function setReporterByChainId(uint256 chainId, address reporter) external onlyOwner { enabledReporters[chainId] = keccak256(abi.encodePacked(reporter)); emit ReporterSet(chainId, reporter); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { ZetaConnector, ZetaTokenConsumer, ZetaInterfaces } from "./interfaces/ZetaInterfaces.sol"; import { Reporter } from "../Reporter.sol"; contract ZetaReporter is Reporter, Ownable { using SafeERC20 for IERC20; string public constant PROVIDER = "zeta"; ZetaConnector public immutable ZETA_CONNECTOR; address public immutable ZETA_TOKEN; ZetaTokenConsumer public immutable ZETA_CONSUMER; constructor( address headerStorage, address yaho, address zetaConnector, address zetaToken, address zetaConsumer ) Reporter(headerStorage, yaho) { ZETA_CONNECTOR = ZetaConnector(zetaConnector); ZETA_TOKEN = zetaToken; ZETA_CONSUMER = ZetaTokenConsumer(zetaConsumer); } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { bytes memory payload = abi.encode(ids, hashes); uint256 zetaAmount = ZETA_CONSUMER.getZetaFromEth{ value: msg.value }(address(this), 0); IERC20(ZETA_TOKEN).safeApprove(address(ZETA_CONNECTOR), zetaAmount); // solhint-disable-next-line check-send-result ZETA_CONNECTOR.send( ZetaInterfaces.SendInput({ destinationChainId: targetChainId, destinationAddress: abi.encodePacked(adapter), destinationGasLimit: 200_000, message: payload, zetaValueAndGas: zetaAmount, zetaParams: abi.encode("") }) ); return bytes32(0); } }
/* ███▄▄▄ ,▄▄███▄ ████▀` ,╓▄▄▄████████████▄ ███▌ ,╓▄▄▄▄█████████▀▀▀▀▀▀╙└` ███▌ ▀▀▀▀▀▀▀▀▀▀╙└└- ████L ███▌ ████` ╓██▄ ███▌ ╓▄ ╓╓╓╓╓╓╓╓╓╓╓████▄╓╓╓╓╓╓╓╓╓╓╓╓╓╓▄███████▄ ███▌ ▄█████▄ ▀▀▀▀▀▀▀▀▀▀████▀▀▀▀▀▀██▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ███████████████████████_ ▄███▀ ██µ ▐███▌ ,███▀ ▀██µ ████▌ ▄███▌, ▄████▄ ▐████▌ ▄██▀████▀▀▀▀▀▀▀▀▀▀█████▀███▄ ,█████▌ ,▄██▀_ ▓███ ▐███_ ▀████▄▄ ██████▌, ▄██▀_ ▓███ ▐███_ ▀███████▄- ███▀███▌▀███▄ ╙" ▓███▄▄▄▄▄▄▄▄▄▄▄███_ `▀███└ ▄██^ ███▌ ^████▄ ▓███▀▀▀▀▀▀▀▀▀▀▀███_ ` ▄██_ ███▌ ╙███ ▓██▀ └▀▀_ ▄, ██▀ ███▌ ▀└ ▐███▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄████▄µ ██^ ███▌ ▐███▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀██████▀ ╓█▀ ███▌ ▐███⌐ µ ╓ ▐███ ▀ ███▌ ▐███⌐ ███▄▄▄▄▄▄▄████▄ ▐███ ███▌ ▐███⌐ ████▀▀▀▀▀▀▀████▀ ▐███ ███▌ ▐███⌐ ███▌ J███M ▐███ ███▌ ▐███⌐ ███▌ J███M ▐███ ███▌ ▐███⌐ ████▄▄▄▄▄▄████M ▐███ ███▌ ▐███⌐ ███▌ ▐███M ▐███ ███▌ ▐███⌐ ███▌ ▀▀_ ████ ███▌ ▐███⌐ ▀▀_ ▀▀▀███████ ███^ ▐███_ ▐██▀▀ Made with ❤️ by Gnosis Guild & the Cross-chain Alliance */ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IAdapter } from "./interfaces/IAdapter.sol"; import { IHashi } from "./interfaces/IHashi.sol"; contract Hashi is IHashi { /// @inheritdoc IHashi function checkHashWithThresholdFromAdapters( uint256 domain, uint256 id, uint256 threshold, IAdapter[] calldata adapters ) external view returns (bool) { if (threshold > adapters.length || threshold == 0) revert InvalidThreshold(threshold, adapters.length); bytes32[] memory hashes = getHashesFromAdapters(domain, id, adapters); for (uint256 i = 0; i < hashes.length; ) { if (i > hashes.length - threshold) break; bytes32 baseHash = hashes[i]; if (baseHash == bytes32(0)) { unchecked { ++i; } continue; } uint256 num = 0; for (uint256 j = i; j < hashes.length; ) { if (baseHash == hashes[j]) { unchecked { ++num; } if (num == threshold) return true; } unchecked { ++j; } } unchecked { ++i; } } return false; } /// @inheritdoc IHashi function getHashFromAdapter(uint256 domain, uint256 id, IAdapter adapter) external view returns (bytes32) { return adapter.getHash(domain, id); } /// @inheritdoc IHashi function getHashesFromAdapters( uint256 domain, uint256 id, IAdapter[] calldata adapters ) public view returns (bytes32[] memory) { if (adapters.length == 0) revert NoAdaptersGiven(); bytes32[] memory hashes = new bytes32[](adapters.length); for (uint256 i = 0; i < adapters.length; ) { hashes[i] = adapters[i].getHash(domain, id); unchecked { ++i; } } return hashes; } /// @inheritdoc IHashi function getHash(uint256 domain, uint256 id, IAdapter[] calldata adapters) external view returns (bytes32 hash) { if (adapters.length == 0) revert NoAdaptersGiven(); bytes32[] memory hashes = getHashesFromAdapters(domain, id, adapters); hash = hashes[0]; if (hash == bytes32(0)) revert HashNotAvailableInAdapter(adapters[0]); for (uint256 i = 1; i < hashes.length; ) { if (hashes[i] == bytes32(0)) revert HashNotAvailableInAdapter(adapters[i]); if (hash != hashes[i]) revert AdaptersDisagree(adapters[i - 1], adapters[i]); unchecked { ++i; } } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; /** * @title IAdapter */ interface IAdapter { error ConflictingBlockHeader(uint256 blockNumber, bytes32 blockHash, bytes32 storedBlockHash); error InvalidBlockHeaderRLP(); /** * @dev Emitted when a hash is stored. * @param id - The ID of the stored hash. * @param hash - The stored hash as bytes32 values. */ event HashStored(uint256 indexed id, bytes32 indexed hash); /** * @dev Returns the hash for a given ID. * @param domain - Identifier for the domain to query. * @param id - Identifier for the ID to query. * @return hash Bytes32 hash for the given ID on the given domain. * @notice MUST return bytes32(0) if the hash is not present. */ function getHash(uint256 domain, uint256 id) external view returns (bytes32 hash); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IAdapter } from "./IAdapter.sol"; /** * @title IBlockHashAdapter */ interface IBlockHashAdapter is IAdapter { /** * @dev Proves and stores valid ancestral block hashes for a given chain ID. * @param chainId - The ID of the chain to prove block hashes for. * @param blockHeaders - The RLP encoded block headers to prove the hashes for. * @notice Block headers should be ordered by descending block number and should start with a known block header. */ function proveAncestralBlockHashes(uint256 chainId, bytes[] memory blockHeaders) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IAdapter } from "./IAdapter.sol"; import { IHashi } from "./IHashi.sol"; import { IShuSho } from "./IShuSho.sol"; /** * @title IGiriGiriBashi */ interface IGiriGiriBashi is IShuSho { struct Challenge { address payable challenger; // account that raised the challenge. uint256 timestamp; // timestamp when the challenge was created. uint256 bond; // bond paid by the challenger. } struct Settings { bool quarantined; // whether or not the adapter has been quarantined. uint256 minimumBond; // amount that must be bonded alongside a challenge. uint256 startId; // earliest id that the adapter could have stored. uint256 idDepth; // how far behind the current head can this adapter safely report. 0 equals infinite. uint256 timeout; // grace period in which the adapter must report on an in-range id after being challenged. } error AdaptersCannotContainChallengedAdapter(IAdapter[] adapters, IAdapter adapter); error AdapterHasNotYetTimedOut(IAdapter adapter); error AdapterNotQuarantined(IAdapter adapter); error AlreadyQuarantined(IAdapter adapter); error CannotProveNoConfidence(uint256 domain, uint256 id, IAdapter[] adapters); error ChallengeNotFound(bytes32 challengeId, uint256 domain, uint256 id, IAdapter adapter); error ChallengeRangeAlreadySet(uint256 domain); error CountMustBeZero(uint256 domain); error DuplicateChallenge(bytes32 challengeId, uint256 domain, uint256 id, IAdapter adapter); error NoConfidenceRequired(); error NotEnoughValue(IAdapter adapter, uint256 value); error OutOfRange(IAdapter adapter, uint256 id); error UnequalArrayLengths(); /** * @dev Emitted when the bond recipient address is set. * @param bondRecipient - The new bond recipient address as an Ethereum address. */ event BondRecipientSet(address payable bondRecipient); /** * @dev Emitted when a challenge is created. * @param challengeId - The unique identifier for the challenge. * @param domain - The domain associated with the challenge. * @param id - The identifier associated with the challenge. * @param adapter - The adapter address associated with the challenge. * @param challenger - The address of the challenger. * @param timestamp - The timestamp when the challenge was created. * @param bond - The bond amount associated with the challenge. */ event ChallengeCreated( bytes32 challengeId, uint256 indexed domain, uint256 id, IAdapter indexed adapter, address indexed challenger, uint256 timestamp, uint256 bond ); /** * @dev Emitted when the challenge range is updated. * @param domain - The domain associated with the updated challenge range. * @param range - The new challenge range as a Uint256 identifier. */ event ChallengeRangeUpdated(uint256 domain, uint256 range); /** * @dev Emitted when a challenge is resolved. * @param challengeId - The unique identifier for the resolved challenge. * @param domain - The domain associated with the resolved challenge. * @param id - The identifier associated with the resolved challenge. * @param adapter - The adapter address associated with the resolved challenge. * @param challenger - The address of the challenger. * @param bond - The bond amount associated with the resolved challenge. * @param challengeSuccessful - A boolean indicating whether the challenge was successful. */ event ChallengeResolved( bytes32 challengeId, uint256 indexed domain, uint256 id, IAdapter indexed adapter, address indexed challenger, uint256 bond, bool challengeSuccessful ); /** * @dev Emitted when a new head is updated. * @param domain - The domain associated with the new head. * @param head - The new head as a Uint256 identifier. */ event NewHead(uint256 domain, uint256 head); /** * @dev Emitted when a declaration of no confidence is made for a specific domain. * @param domain - The domain associated with the declaration. */ event NoConfidenceDeclared(uint256 domain); /** * @dev Emitted when settings are initialized for a specific domain and adapter. * @param domain - The domain associated with the initialized settings. * @param adapter - The adapter address associated with the initialized settings. * @param settings - The initialized settings object. */ event SettingsInitialized(uint256 domain, IAdapter adapter, Settings settings); /** * @dev Challenges the adapter to provide a response. If the adapter fails, it can be quarantined. * @param domain - The Uint256 identifier for the domain. * @param id - The Uint256 identifier for the challenge. * @param adapter - The address of the adapter to challenge. * @notice Caller must pay a minimum bond to issue the challenge. This bond should be high enough to cover the gas costs for successfully completing the challenge. */ function challengeAdapter(uint256 domain, uint256 id, IAdapter adapter) external payable; /** * @dev Show that enough adapters disagree that they could not make a threshold if the remainder all agree with one. * @param domain - The Uint256 identifier for the domain. * @param id - The Uint256 identifier. * @param adapters - An array of adapter instances. */ function declareNoConfidence(uint256 domain, uint256 id, IAdapter[] memory adapters) external; /** * @dev Disables a set of adapters for a given domain. * @param domain - The Uint256 identifier for the domain. * @param adapters - An array of adapter instances to be disabled. */ function disableAdapters(uint256 domain, IAdapter[] memory adapters) external; /** * @dev Enables a set of adapters for a given domain with specific settings. * @param domain - The Uint256 identifier for the domain. * @param adapters - An array of adapter instances. * @param settings - An array of settings, corresponding to each adapter. */ function enableAdapters(uint256 domain, IAdapter[] memory adapters, Settings[] memory settings) external; /** * @dev Get the current challenge given a challengeId. * @param challengeId - The Bytes32 identifier for the challenge. * @return challenge - Challenge indicating the challenge parameters. */ function getChallenge(bytes32 challengeId) external view returns (Challenge memory); /** * @dev Gets the challenge ID for a given domain, ID, and adapter. * @param domain - The Uint256 identifier for the domain. * @param id - The Uint256 identifier. * @param adapter - The adapter instance. * @return The computed challenge ID as a bytes32 hash. */ function getChallengeId(uint256 domain, uint256 id, IAdapter adapter) external pure returns (bytes32); /** * @dev Get how far beyond the current highestId can be challenged. * @param domain - The Uint256 identifier for the domain. * @return range - Uint256 indicating the challenge range. */ function getChallengeRange(uint256 domain) external view returns (uint256); /** * @dev Returns the hash agreed upon by a threshold of the enabled adapters. * @param domain - Uint256 identifier for the domain to query. * @param id - Uint256 identifier to query. * @return hash - Bytes32 hash agreed upon by a threshold of the adapters for the given domain. * @notice Reverts if no threshold is not reached. * @notice Reverts if no adapters are set for the given domain. */ function getThresholdHash(uint256 domain, uint256 id) external returns (bytes32); /** * @dev Returns the hash unanimously agreed upon by ALL of the enabled adapters. * @param domain - Uint256 identifier for the domain to query. * @param id - Uint256 identifier to query. * @return hash - Bytes32 hash agreed upon by the adapters for the given domain. * @notice Reverts if adapters disagree. * @notice Revert if the adapters do not yet have the hash for the given ID. * @notice Reverts if no adapters are set for the given domain. */ function getUnanimousHash(uint256 domain, uint256 id) external returns (bytes32); /** * @dev Returns the hash unanimously agreed upon by all of the given adapters. * @param domain - Uint256 identifier for the domain to query. * @param adapters - Array of adapter addresses to query. * @param id - Uint256 identifier to query. * @return hash - Bytes32 hash agreed upon by the adapters for the given domain. * @notice Adapters must be in numerical order from smallest to largest and contain no duplicates. * @notice Reverts if adapters are out of order or contain duplicates. * @notice Reverts if adapters disagree. * @notice Revert if the adapters do not yet have the hash for the given ID. * @notice Reverts if no adapters are set for the given domain. */ function getHash(uint256 domain, uint256 id, IAdapter[] memory adapters) external returns (bytes32); /** * @dev Returns the highest id reported for a given id * @param domain - Uint256 identifier for the domain to query. * @return id - Uint256 indicating the highest id reported. */ function getHead(uint256 domain) external view returns (uint256); /** * @dev Get the current settings for a given adapter. * @param domain - Uint256 identifier for the domain to query. * @param adapter - The adapter. * @return settings - The Settings for the given adapter. */ function getSettings(uint256 domain, IAdapter adapter) external view returns (Settings memory); /** * @dev Replaces the quarantined adapters for a given domain with new adapters and settings. * @param domain - The Uint256 identifier for the domain. * @param currentAdapters - An array of current adapter instances to be replaced. * @param newAdapters - An array of new adapter instances to replace the current ones. * @param settings - An array of settings corresponding to the new adapters. */ function replaceQuarantinedAdapters( uint256 domain, IAdapter[] memory currentAdapters, IAdapter[] memory newAdapters, Settings[] memory settings ) external; /** * @dev Resolves a challenge by comparing results from a specific adapter with others. * @param domain - The Uint256 identifier for the domain. * @param id - The Uint256 identifier. * @param adapter - The adapter instance for comparison. * @param adapters - An array of adapter instances for comparison. * @return A boolean indicating the success of the challenge resolution. */ function resolveChallenge( uint256 domain, uint256 id, IAdapter adapter, IAdapter[] memory adapters ) external returns (bool); /** * @dev Sets the bond recipient address for payments. * @param bondRecipient - The address where bond payments should be sent. */ function setBondRecipient(address payable bondRecipient) external; /** * @dev Sets the challenge range for a specific domain. * @param domain - The Uint256 identifier for the domain. * @param range - The Uint256 range to set for the given domain. */ function setChallengeRange(uint256 domain, uint256 range) external; /** * @dev Sets the threshold for a specific domain. * @param domain - The Uint256 identifier for the domain. * @param threshold - The Uint256 threshold to set for the given domain. */ function setThreshold(uint256 domain, uint256 threshold) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IAdapter } from "./IAdapter.sol"; /** * @title IHashi */ interface IHashi { error AdaptersDisagree(IAdapter adapterOne, IAdapter adapterTwo); error HashNotAvailableInAdapter(IAdapter adapter); error InvalidThreshold(uint256 threshold, uint256 maxThreshold); error NoAdaptersGiven(); /** * @dev Checks whether the threshold is reached for a message given a set of adapters. * @param domain - ID of the domain to query. * @param id - ID for which to return hash. * @param threshold - Threshold to use. * @param adapters - Array of addresses for the adapters to query. * @notice If the threshold is 1, it will always return true. * @return result A boolean indicating if a threshold for a given message has been reached. */ function checkHashWithThresholdFromAdapters( uint256 domain, uint256 id, uint256 threshold, IAdapter[] calldata adapters ) external view returns (bool); /** * @dev Returns the hash stored by a given adapter for a given ID. * @param domain - ID of the domain to query. * @param id - ID for which to return a hash. * @param adapter - Address of the adapter to query. * @return hash stored by the given adapter for the given ID. */ function getHashFromAdapter(uint256 domain, uint256 id, IAdapter adapter) external view returns (bytes32); /** * @dev Returns the hashes for a given ID stored by a given set of adapters. * @param domain - The ID of the domain to query. * @param id - The ID for which to return hashes. * @param adapters - An array of addresses for the adapters to query. * @return hashes An array of hashes stored by the given adapters for the specified ID. */ function getHashesFromAdapters( uint256 domain, uint256 id, IAdapter[] calldata adapters ) external view returns (bytes32[] memory); /** * @dev Returns the hash unanimously agreed upon by a given set of adapters. * @param domain - The ID of the domain to query. * @param id - The ID for which to return a hash. * @param adapters - An array of addresses for the adapters to query. * @return hash agreed on by the given set of adapters. * @notice MUST revert if adapters disagree on the hash or if an adapter does not report. */ function getHash(uint256 domain, uint256 id, IAdapter[] calldata adapters) external view returns (bytes32); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; /** * @title IHashiProver */ interface IHashiProver { struct AccountAndStorageProof { uint256 chainId; uint256 blockNumber; bytes blockHeader; uint256 ancestralBlockNumber; bytes[] ancestralBlockHeaders; address account; bytes accountProof; bytes32 storageHash; bytes32[] storageKeys; bytes[] storageProof; } error AncestralBlockHeadersLengthReached(); error BlockHeaderNotFound(); error ConflictingBlockHeader(uint256 blockNumber, bytes32 ancestralBlockHeaderHash, bytes32 blockHeaderHash); error InvalidAccount(); error InvalidBlockHeader(); error InvalidBlockHeaderLength(); error InvalidStorageHash(); error InvalidStorageProofParams(); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; /** * @title IHeaderStorage */ interface IHeaderStorage { error HeaderOutOfRange(uint256 blockNumber); /** * @dev Emitted when a block header is stored. * @param blockNumber - The block number associated with the stored header. * @param blockHeader - The stored block header as a bytes32 value. */ event HeaderStored(uint256 indexed blockNumber, bytes32 indexed blockHeader); /** * @dev Retrieves the stored block header for a specific block number. * @param blockNumber - The block number as a uint256 value. * @return The block header as a bytes32 value. */ function headers(uint256 blockNumber) external view returns (bytes32); /** * @dev Stores and returns the header for the given block. * @param blockNumber - Block number. * @return blockHeader - Block header stored. * @notice Reverts if the given block header was not previously stored and is now out of range. */ function storeBlockHeader(uint256 blockNumber) external returns (bytes32); /** * @dev Stores and returns the header for an array of given blocks. * @param blockNumbers - Array of block numbers. * @return blockHeaders - Array of block headers stored. * @notice Reverts if the given block header was not previously stored and is now out of range. */ function storeBlockHeaders(uint256[] memory blockNumbers) external returns (bytes32[] memory); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IAdapter } from "./IAdapter.sol"; /** * @title IJushin */ interface IJushin { /** * @dev Handles the incoming message from a specified chain. * @param messageId - The unique identifier of the message. * @param sourceChainId - The ID of the origin chain from which the message originates. * @param sender - The address of the sender of the message on the origin chain. * @param threshold - The minimum number of adapters required to have stored the same message. * @param data - The data contained in the message, in bytes. * @param adapters - An array of `IAdapter` contracts. * @return result bytes at the user's choice */ function onMessage( uint256 messageId, uint256 sourceChainId, address sender, uint256 threshold, IAdapter[] calldata adapters, bytes calldata data ) external returns (bytes memory); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IReporter } from "./IReporter.sol"; import { IAdapter } from "./IAdapter.sol"; struct Message { uint256 nonce; uint256 targetChainId; uint256 threshold; address sender; address receiver; bytes data; IReporter[] reporters; IAdapter[] adapters; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { Message } from "./IMessage.sol"; /** * @title IMessageHashCalculator */ interface IMessageHashCalculator { /** * @dev Calculates and returns the hash of a given message. * @param message - The `Message` structure containing various fields to be hashed. * @return hash The keccak256 hash of the message, represented as a 32-byte hexadecimal string. */ function calculateMessageHash(Message memory message) external pure returns (bytes32); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; /** * @title IMessageIdCalculator */ interface IMessageIdCalculator { /** * @dev Calculates and returns a unique identifier (ID) for a message. * * @param sourceChainId - The ID of the chain from which the message originates. * @param dispatcherAddress - The address of the dispatcher sending the message. * @param messageHash - The keccak256 hash of the message, represented as a 32-byte hexadecimal string. * @return messageId The unique identifier for the message, calculated based on the input parameters. */ function calculateMessageId( uint256 sourceChainId, address dispatcherAddress, bytes32 messageHash ) external pure returns (uint256); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IAdapter } from "./IAdapter.sol"; interface IReporter { error NotYaho(address sender, address expectedYaho); /** * @dev Emitted when a block is dispatched to another chain. * @param targetChainId - The target chain's identifier associated with the dispatched block. * @param adapter - The adapter address associated with the dispatched block. * @param blockNumber - The block number associated with the dispatched block. * @param blockHeader - The dispatched block header as a bytes32 value. */ event BlockDispatched( uint256 indexed targetChainId, IAdapter adapter, uint256 indexed blockNumber, bytes32 blockHeader ); /** * @dev Emitted when a message is dispatched to another chain. * @param targetChainId - The target chain's identifier associated with the dispatched message. * @param adapter - The adapter address associated with the dispatched message. * @param messageId - The message identifier associated with the dispatched message. * @param messageHash - The dispatched message hash as a bytes32 value. */ event MessageDispatched( uint256 indexed targetChainId, IAdapter adapter, uint256 indexed messageId, bytes32 messageHash ); /** * @dev Dispatches blocks to a given adapter on the target chaib. * @param targetChainId - The target chain's Uint256 identifier. * @param adapter - The adapter instance to use. * @param blockNumbers - An array of Uint256 block numbers to dispatch. * @notice blockNumbers must include block numbers that are greater than or equal to (currentBlock - 256) due to EVM limitations. * @return result - The result returned by the adapter as bytes. */ function dispatchBlocks( uint256 targetChainId, IAdapter adapter, uint256[] memory blockNumbers ) external payable returns (bytes32); /** * @dev Dispatches messages to a target chain using the specified adapter. * @param targetChainId - The target chain's Uint256 identifier. * @param adapter - The adapter instance to use. * @param messageIds - An array of Uint256 message identifiers. * @param messageHashes - An array of bytes32 message hashes. * @notice This function can be called only by Yaho * @return result - The result returned by the adapter as bytes. */ function dispatchMessages( uint256 targetChainId, IAdapter adapter, uint256[] memory messageIds, bytes32[] memory messageHashes ) external payable returns (bytes32); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IHashi } from "./IHashi.sol"; import { IAdapter } from "./IAdapter.sol"; import { IShuSho } from "./IShuSho.sol"; /** * @title IShoyuBashi */ interface IShoyuBashi is IShuSho { /** * @dev Disables the given adapters for a given domain. * @param domain - Uint256 identifier for the domain for which to set adapters. * @param adapters - Array of adapter addresses. * @notice Only callable by the owner of this contract. * @notice Reverts if adapters are out of order or contain duplicates. */ function disableAdapters(uint256 domain, IAdapter[] memory adapters) external; /** * @dev Enables the given adapters for a given domain. * @param domain - Uint256 identifier for the domain for which to set adapters. * @param adapters - Array of adapter addresses. * @param threshold - Uint256 threshold to set for the given domain. * @notice Only callable by the owner of this contract. * @notice Reverts if adapters are out of order, contain duplicates or if the threshold is not higher than half the count of the adapters */ function enableAdapters(uint256 domain, IAdapter[] memory adapters, uint256 threshold) external; /** * @dev Returns the hash unanimously agreed upon by ALL of the enabled adapters. * @param domain - Uint256 identifier for the domain to query. * @param id - Uint256 identifier to query. * @return Bytes32 hash agreed upon by the adapters for the given domain. * @notice Revert if the adapters do not yet have the hash for the given ID. * @notice Reverts if adapters disagree. * @notice Reverts if no adapters are set for the given domain. */ function getUnanimousHash(uint256 domain, uint256 id) external view returns (bytes32); /** * @dev Returns the hash agreed upon by a threshold of the enabled adapters. * @param domain - Uint256 identifier for the domain to query. * @param id - Uint256 identifier to query. * @return Bytes32 hash agreed upon by a threshold of the adapters for the given domain. * @notice Reverts if the threshold is not reached. * @notice Reverts if no adapters are set for the given domain. */ function getThresholdHash(uint256 domain, uint256 id) external view returns (bytes32); /** * @dev Returns the hash unanimously agreed upon by all of the given adapters. * @param domain - Uint256 identifier for the domain to query. * @param adapters - Array of adapter addresses to query. * @param id - Uint256 identifier to query. * @return Bytes32 hash agreed upon by the adapters for the given domain. * @notice adapters must be in numerical order from smallest to largest and contain no duplicates. * @notice Reverts if adapters are out of order or contain duplicates. * @notice Reverts if adapters disagree. * @notice Revert if the adapters do not yet have the hash for the given ID. * @notice Reverts if no adapters are set for the given domain. */ function getHash(uint256 domain, uint256 id, IAdapter[] memory adapters) external view returns (bytes32); /** * @dev Sets the threshold of adapters required for a given domain. * @param domain - Uint256 identifier for the domain for which to set the threshold. * @param threshold - Uint256 threshold to set for the given domain. * @notice Only callable by the owner of this contract. * @notice Reverts if the threshold is already set to the given value. */ function setThreshold(uint256 domain, uint256 threshold) external; /** * @dev Sets the address of the IHashi contract. * @param hashi - Address of the hashi contract. * @notice Only callable by the owner of this contract. */ function setHashi(IHashi hashi) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IHashi } from "./IHashi.sol"; import { IAdapter } from "./IAdapter.sol"; /** * @title IShuSho */ interface IShuSho { struct Domain { uint256 threshold; uint256 count; } struct Link { IAdapter previous; IAdapter next; } error AdapterNotEnabled(IAdapter adapter); error AdapterAlreadyEnabled(IAdapter adapter); error CountCannotBeZero(); error DuplicateHashiAddress(IHashi hashi); error DuplicateOrOutOfOrderAdapters(IAdapter adapterOne, IAdapter adapterTwo); error DuplicateThreshold(uint256 threshold); error InvalidAdapter(IAdapter adapter); error InvalidThreshold(uint256 threshold); error NoAdaptersEnabled(uint256 domain); error NoAdaptersGiven(); error ThresholdNotMet(); /** * @dev Emitted when adapters are disabled for a specific domain. * @param domain - The domain associated with the disabled adapters. * @param adapters - An array of disabled adapter addresses associated with this event. */ event AdaptersDisabled(uint256 indexed domain, IAdapter[] adapters); /** * @dev Emitted when adapters are enabled for a specific domain. * @param domain - The domain associated with the enabled adapters. * @param adapters - An array of enabled adapter addresses associated with this event. */ event AdaptersEnabled(uint256 indexed domain, IAdapter[] adapters); /** * @dev Emitted when the address of the IHashi contract is set. * @param hashi - The address of the IHashi contract associated with this event. */ event HashiSet(IHashi indexed hashi); /** * @dev Emitted when initialization occurs with the owner's address and the IHashi contract address. * @param owner - The address of the owner associated with this event. * @param hashi - The address of the IHashi contract associated with this event. */ event Init(address indexed owner, IHashi indexed hashi); /** * @dev Emitted when the threshold is set for a specific domain. * @param domain - The domain associated with the set threshold. * @param threshold - The new threshold value associated with this event. */ event ThresholdSet(uint256 domain, uint256 threshold); /** * @dev Checks the order and validity of adapters for a given domain. * @param domain - The Uint256 identifier for the domain. * @param _adapters - An array of adapter instances. */ function checkAdapterOrderAndValidity(uint256 domain, IAdapter[] memory _adapters) external view; /** * @dev Get the previous and the next adapter given a domain and an adapter. * @param domain - Uint256 identifier for the domain. * @param adapter - IAdapter value for the adapter. * @return link - The Link struct containing the previous and the next adapter. */ function getAdapterLink(uint256 domain, IAdapter adapter) external view returns (Link memory); /** * @dev Returns an array of enabled adapters for a given domain. * @param domain - Uint256 identifier for the domain for which to list adapters. * @return adapters - The adapters for a given domain. */ function getAdapters(uint256 domain) external view returns (IAdapter[] memory); /** * @dev Get the current configuration for a given domain. * @param domain - Uint256 identifier for the domain. * @return domain - The Domain struct containing the current configuration for a given domain. */ function getDomain(uint256 domain) external view returns (Domain memory); /** * @dev Returns the threshold and count for a given domain. * @param domain - Uint256 identifier for the domain. * @return threshold - Uint256 adapters threshold for the given domain. * @return count - Uint256 adapters count for the given domain. * @notice If the threshold for a domain has not been set, or is explicitly set to 0, this function will return a threshold equal to the adapters count for the given domain. */ function getThresholdAndCount(uint256 domain) external view returns (uint256, uint256); /** * @dev Returns the address of the specified Hashi. * @return hashi - The Hashi address. */ function hashi() external view returns (IHashi); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { IMessageHashCalculator } from "./IMessageHashCalculator.sol"; import { IMessageIdCalculator } from "./IMessageIdCalculator.sol"; import { Message } from "./IMessage.sol"; import { IReporter } from "./IReporter.sol"; import { IAdapter } from "./IAdapter.sol"; /** * @title IYaho */ interface IYaho is IMessageHashCalculator, IMessageIdCalculator { error NoMessagesGiven(); error NoMessageIdsGiven(); error NoReportersGiven(); error NoAdaptersGiven(); error UnequalArrayLengths(uint256 arrayOne, uint256 arrayTwo); error MessageHashNotFound(uint256 messageId); error InvalidMessage(Message message); error InvalidThreshold(uint256 threshold, uint256 maxThreshold); /** * @dev Emitted when a message is dispatched with its associated message ID and message struct. * @param messageId - The message ID associated with the dispatched message. * @param message - The message struct associated with this event. */ event MessageDispatched(uint256 indexed messageId, Message message); /** * @dev Dispatches a message to a specified chain with a set of validation parameters without calling the reporters. It just write in storage a commitment of message. In order to dispatch it to the reporters, you must then invoke `relayMessagesToAdapters` * @param targetChainId - The ID of the target chain to which the message is being sent. * @param threshold - The minimum number of adapters required to have stored the same message. * @param receiver - The address of the receiver on the target chain. * @param data - The data being sent in the message, represented as a byte array. * @param reporters - An array of `IReporter` contracts (not actively used in this step). * @param adapters - An array of `IAdapter` contracts (for later validation use). * @return messageId A unique identifier for the dispatched message, used for tracking and subsequent validation. * @notice If you plan to use an Adapter that does not have a corresponding Reporter (such as an adapter that uses a light client), you need to specify only the adapter and use address(0) as the reporter, since the adapter will verify the MessageDispatched event emitted by Yaho. * */ function dispatchMessage( uint256 targetChainId, uint256 threshold, address receiver, bytes calldata data, IReporter[] calldata reporters, IAdapter[] calldata adapters ) external returns (uint256); /** * @dev Dispatches a message to a specified chain with a set of validation parameters and calls the reporters. * @param targetChainId - The ID of the target chain to which the message is being sent. * @param threshold - The minimum number of adapters required to have stored the same message. * @param receiver - The address of the receiver on the target chain. * @param data - The data being sent in the message, represented as a byte array. * @param reporters - An array of `IReporter` contracts (not actively used in this step). * @param adapters - An array of `IAdapter` contracts (for later validation use). * @return (messageId, result) A unique identifier for the dispatched message and an array of byte arrays, where each element is the result of dispatching a respective message to the corresponding Reporter. * @notice If you plan to use an Adapter that does not have a corresponding Reporter (such as an adapter that uses a light client), you need to specify only the adapter and use address(0) as the reporter, since the adapter will verify the MessageDispatched event emitted by Yaho. */ function dispatchMessageToAdapters( uint256 targetChainId, uint256 threshold, address receiver, bytes calldata data, IReporter[] calldata reporters, IAdapter[] calldata adapters ) external payable returns (uint256, bytes32[] memory); /** * @dev Dispatches an array of messages to specified chains and calls the reporters. * @param targetChainId - The ID of the target chain for all messages. * @param thresholds - An array of minimum validation thresholds required for each message. * @param receivers - An array of addresses for the receivers on the target chain, one for each message. * @param data - An array of data payloads for each message, represented as byte arrays. * @param reporters - An array of `IReporter` contracts for reporting the status of each message. * @param adapters - An array of `IAdapter` contracts used for the validation of each message. * @return (messageIds, result) An array of unique identifiers for the dispatched messages and an array of bytes32 arrays, where each element is the result of dispatching a respective message to the corresponding Reporter. * @notice If you plan to use an Adapter that does not have a corresponding Reporter (such as an adapter that uses a light client), you need to specify only the adapter and use address(0) as the reporter, since the adapter will verify the MessageDispatched event emitted by Yaho. */ function dispatchMessagesToAdapters( uint256 targetChainId, uint256[] calldata thresholds, address[] calldata receivers, bytes[] calldata data, IReporter[] calldata reporters, IAdapter[] calldata adapters ) external payable returns (uint256[] memory, bytes32[] memory); /** * @dev Retrieves the hash of a pending message that was dispatched via `dispatchMessage` but has not yet been relayed to adapters using `relayingMessagesToAdapters`. * @param messageId - The unique identifier of the message for which the hash is being retrieved. * @return messageHash The hash of the pending message if it exists. */ function getPendingMessageHash(uint256 messageId) external view returns (bytes32); /** * @dev Relays an array of messages to their respective adapters. In order to be able to aggregate messages within the reporter, it's mandatory that all messages have the same targetChainId, reporters and adapters. * @param messages - An array of `Message` structures to be relayed to the adapters. * @return result An array of bytes32 arrays, where each element is the result of dispatching a respective all messages to the corresponding Reporter. */ function relayMessagesToAdapters(Message[] calldata messages) external payable returns (bytes32[] memory); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; import { Message } from "./IMessage.sol"; import { IMessageHashCalculator } from "./IMessageHashCalculator.sol"; import { IMessageIdCalculator } from "./IMessageIdCalculator.sol"; /** * @title IYaru */ interface IYaru is IMessageHashCalculator, IMessageIdCalculator { error CallFailed(); error InvalidToChainId(uint256 chainId, uint256 expectedChainId); error MessageIdAlreadyExecuted(uint256 messageId); error ThresholdNotMet(); /** * @dev Emitted when a message is executed with its associated message ID and message object. * @param messageId - The message ID associated with the executed message. * @param message - The message object associated with this event. */ event MessageExecuted(uint256 indexed messageId, Message message); /** * @dev Executes a batch of messages and returns the results if the threshold for a single message has been reached * @param messages - An array of `Message` structures * @return result An array of byte arrays, where each byte array is the result of executing a respective message from the input. */ function executeMessages(Message[] calldata messages) external returns (bytes[] memory); }
pragma solidity ^0.8.20; /* solhint-disable */ import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol"; // Copied from here: https://github.com/defi-wonderland/safe-liveness/blob/dev/solidity/libraries/MerklePatriciaProofVerifier.sol library MerklePatriciaProofVerifier { using RLPReader for RLPReader.RLPItem; using RLPReader for bytes; /// @dev Validates a Merkle-Patricia-Trie proof. /// If the proof proves the inclusion of some key-value pair in the /// trie, the value is returned. Otherwise, i.e. if the proof proves /// the exclusion of a key from the trie, an empty byte array is /// returned. /// @param rootHash is the Keccak-256 hash of the root node of the MPT. /// @param path is the key of the node whose inclusion/exclusion we are /// proving. /// @param stack is the stack of MPT nodes (starting with the root) that /// need to be traversed during verification. /// @return value whose inclusion is proved or an empty byte array for /// a proof of exclusion function extractProofValue( bytes32 rootHash, bytes memory path, RLPReader.RLPItem[] memory stack ) internal pure returns (bytes memory value) { bytes memory mptKey = _decodeNibbles(path, 0); uint256 mptKeyOffset = 0; bytes32 nodeHashHash; RLPReader.RLPItem[] memory node; RLPReader.RLPItem memory rlpValue; if (stack.length == 0) { // Root hash of empty Merkle-Patricia-Trie require(rootHash == 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421); return new bytes(0); } // Traverse stack of nodes starting at root. for (uint256 i = 0; i < stack.length; i++) { // We use the fact that an rlp encoded list consists of some // encoding of its length plus the concatenation of its // *rlp-encoded* items. // The root node is hashed with Keccak-256 ... if (i == 0 && rootHash != stack[i].rlpBytesKeccak256()) { revert(); } // ... whereas all other nodes are hashed with the MPT // hash function. if (i != 0 && nodeHashHash != _mptHashHash(stack[i])) { revert(); } // We verified that stack[i] has the correct hash, so we // may safely decode it. node = stack[i].toList(); if (node.length == 2) { // Extension or Leaf node bool isLeaf; bytes memory nodeKey; (isLeaf, nodeKey) = _merklePatriciaCompactDecode(node[0].toBytes()); uint256 prefixLength = _sharedPrefixLength(mptKeyOffset, mptKey, nodeKey); mptKeyOffset += prefixLength; if (prefixLength < nodeKey.length) { // Proof claims divergent extension or leaf. (Only // relevant for proofs of exclusion.) // An Extension/Leaf node is divergent iff it 'skips' over // the point at which a Branch node should have been had the // excluded key been included in the trie. // Example: Imagine a proof of exclusion for path [1, 4], // where the current node is a Leaf node with // path [1, 3, 3, 7]. For [1, 4] to be included, there // should have been a Branch node at [1] with a child // at 3 and a child at 4. // Sanity check if (i < stack.length - 1) { // divergent node must come last in proof revert(); } return new bytes(0); } if (isLeaf) { // Sanity check if (i < stack.length - 1) { // leaf node must come last in proof revert(); } if (mptKeyOffset < mptKey.length) { return new bytes(0); } rlpValue = node[1]; return rlpValue.toBytes(); } else { // extension // Sanity check if (i == stack.length - 1) { // shouldn't be at last level revert(); } if (!node[1].isList()) { // rlp(child) was at least 32 bytes. node[1] contains // Keccak256(rlp(child)). nodeHashHash = node[1].payloadKeccak256(); } else { // rlp(child) was less than 32 bytes. node[1] contains // rlp(child). nodeHashHash = node[1].rlpBytesKeccak256(); } } } else if (node.length == 17) { // Branch node if (mptKeyOffset != mptKey.length) { // we haven't consumed the entire path, so we need to look at a child uint8 nibble = uint8(mptKey[mptKeyOffset]); mptKeyOffset += 1; if (nibble >= 16) { // each element of the path has to be a nibble revert(); } if (_isEmptyBytesequence(node[nibble])) { // Sanity if (i != stack.length - 1) { // leaf node should be at last level revert(); } return new bytes(0); } else if (!node[nibble].isList()) { nodeHashHash = node[nibble].payloadKeccak256(); } else { nodeHashHash = node[nibble].rlpBytesKeccak256(); } } else { // we have consumed the entire mptKey, so we need to look at what's contained in this node. // Sanity if (i != stack.length - 1) { // should be at last level revert(); } return node[16].toBytes(); } } } } /// @dev Computes the hash of the Merkle-Patricia-Trie hash of the RLP item. /// Merkle-Patricia-Tries use a weird 'hash function' that outputs /// *variable-length* hashes: If the item is shorter than 32 bytes, /// the MPT hash is the item. Otherwise, the MPT hash is the /// Keccak-256 hash of the item. /// The easiest way to compare variable-length byte sequences is /// to compare their Keccak-256 hashes. /// @param item The RLP item to be hashed. /// @return Keccak-256(MPT-hash(item)) function _mptHashHash(RLPReader.RLPItem memory item) private pure returns (bytes32) { if (item.len < 32) { return item.rlpBytesKeccak256(); } else { return keccak256(abi.encodePacked(item.rlpBytesKeccak256())); } } function _isEmptyBytesequence(RLPReader.RLPItem memory item) private pure returns (bool) { if (item.len != 1) { return false; } uint8 b; uint256 memPtr = item.memPtr; assembly { b := byte(0, mload(memPtr)) } return b == 0x80; /* empty byte string */ } function _merklePatriciaCompactDecode( bytes memory compact ) private pure returns (bool isLeaf, bytes memory nibbles) { require(compact.length > 0); uint256 first_nibble = (uint8(compact[0]) >> 4) & 0xF; uint256 skipNibbles; if (first_nibble == 0) { skipNibbles = 2; isLeaf = false; } else if (first_nibble == 1) { skipNibbles = 1; isLeaf = false; } else if (first_nibble == 2) { skipNibbles = 2; isLeaf = true; } else if (first_nibble == 3) { skipNibbles = 1; isLeaf = true; } else { // Not supposed to happen! revert(); } return (isLeaf, _decodeNibbles(compact, skipNibbles)); } function _decodeNibbles(bytes memory compact, uint256 skipNibbles) private pure returns (bytes memory nibbles) { require(compact.length > 0); uint256 length = compact.length * 2; require(skipNibbles <= length); length -= skipNibbles; nibbles = new bytes(length); uint256 nibblesLength = 0; for (uint256 i = skipNibbles; i < skipNibbles + length; i += 1) { if (i % 2 == 0) { nibbles[nibblesLength] = bytes1((uint8(compact[i / 2]) >> 4) & 0xF); } else { nibbles[nibblesLength] = bytes1((uint8(compact[i / 2]) >> 0) & 0xF); } nibblesLength += 1; } assert(nibblesLength == nibbles.length); } function _sharedPrefixLength(uint256 xsOffset, bytes memory xs, bytes memory ys) private pure returns (uint256) { uint256 i; for (i = 0; i + xsOffset < xs.length && i < ys.length; i++) { if (xs[i + xsOffset] != ys[i]) { return i; } } return i; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { ShuSo } from "./ShuSo.sol"; import { IAdapter } from "../interfaces/IAdapter.sol"; import { IShoyuBashi } from "../interfaces/IShoyuBashi.sol"; import { IHashi } from "../interfaces/IHashi.sol"; contract ShoyuBashi is IShoyuBashi, ShuSo { constructor(address _owner, address _hashi) ShuSo(_owner, _hashi) {} // solhint-disable no-empty-blocks /// @inheritdoc IShoyuBashi function setThreshold(uint256 domain, uint256 threshold) external { _setThreshold(domain, threshold); } /// @inheritdoc IShoyuBashi function enableAdapters(uint256 domain, IAdapter[] memory adapters, uint256 threshold) external { _enableAdapters(domain, adapters, threshold); } /// @inheritdoc IShoyuBashi function disableAdapters(uint256 domain, IAdapter[] memory adapters) external { _disableAdapters(domain, adapters); } /// @inheritdoc IShoyuBashi function getUnanimousHash(uint256 domain, uint256 id) external view returns (bytes32) { return _getUnanimousHash(domain, id); } /// @inheritdoc IShoyuBashi function getThresholdHash(uint256 domain, uint256 id) external view returns (bytes32) { return _getThresholdHash(domain, id); } /// @inheritdoc IShoyuBashi function getHash(uint256 domain, uint256 id, IAdapter[] memory adapters) external view returns (bytes32) { return _getHash(domain, id, adapters); } /// @inheritdoc IShoyuBashi function setHashi(IHashi _hashi) external { _setHashi(_hashi); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { IAdapter } from "../interfaces/IAdapter.sol"; import { IHashi } from "../interfaces/IHashi.sol"; import { IShuSho } from "../interfaces/IShuSho.sol"; abstract contract ShuSo is IShuSho, OwnableUpgradeable { IAdapter internal constant LIST_END = IAdapter(address(0x1)); IHashi public hashi; mapping(uint256 => mapping(IAdapter => Link)) private _adapters; mapping(uint256 => Domain) private _domains; constructor(address _owner, address _hashi) { bytes memory initParams = abi.encode(_owner, _hashi); init(initParams); } function init(bytes memory initParams) public initializer { (address _owner, IHashi _hashi) = abi.decode(initParams, (address, IHashi)); __Ownable_init(); _setHashi(_hashi); transferOwnership(_owner); emit Init(_owner, _hashi); } /// @inheritdoc IShuSho function checkAdapterOrderAndValidity(uint256 domain, IAdapter[] memory adapters) public view { for (uint256 i = 0; i < adapters.length; i++) { IAdapter adapter = adapters[i]; if (i > 0 && adapter <= adapters[i - 1]) revert DuplicateOrOutOfOrderAdapters(adapter, adapters[i - 1]); if (_adapters[domain][adapter].next == IAdapter(address(0))) revert InvalidAdapter(adapter); } } /// @inheritdoc IShuSho function getAdapterLink(uint256 domain, IAdapter adapter) external view returns (Link memory) { return _adapters[domain][adapter]; } /// @inheritdoc IShuSho function getAdapters(uint256 domain) public view returns (IAdapter[] memory) { IAdapter[] memory adapters = new IAdapter[](_domains[domain].count); IAdapter currentAdapter = _adapters[domain][LIST_END].next; for (uint256 i = 0; i < adapters.length; i++) { adapters[i] = currentAdapter; currentAdapter = _adapters[domain][currentAdapter].next; } return adapters; } /// @inheritdoc IShuSho function getDomain(uint256 domain) external view returns (Domain memory) { return _domains[domain]; } /// @inheritdoc IShuSho function getThresholdAndCount(uint256 domain_) public view returns (uint256, uint256) { Domain storage domain = _domains[domain_]; uint256 threshold = domain.threshold; uint256 count = domain.count; if (threshold == 0) threshold = count; return (threshold, count); } /** * @dev Disables the given adapters for a given domain. * @param domain - Uint256 identifier for the domain for which to set adapters. * @param adapters - Array of adapter addresses. * @notice Reverts if adapters are out of order or contain duplicates. * @notice Only callable by the owner of this contract. */ function _disableAdapters(uint256 domain, IAdapter[] memory adapters) internal onlyOwner { if (_domains[domain].count == 0) revert NoAdaptersEnabled(domain); if (adapters.length == 0) revert NoAdaptersGiven(); for (uint256 i = 0; i < adapters.length; i++) { IAdapter adapter = adapters[i]; if (adapter == IAdapter(address(0)) || adapter == LIST_END) revert InvalidAdapter(adapter); Link memory current = _adapters[domain][adapter]; if (current.next == IAdapter(address(0))) revert AdapterNotEnabled(adapter); IAdapter next = current.next; IAdapter previous = current.previous; _adapters[domain][next].previous = previous; _adapters[domain][previous].next = next; delete _adapters[domain][adapter].next; delete _adapters[domain][adapter].previous; _domains[domain].count--; } emit AdaptersDisabled(domain, adapters); } /** * @dev Enables the given adapters for a given domain. * @param domain - Uint256 identifier for the domain for which to set adapters. * @param adapters - Array of adapter addresses. * @param threshold - Uint256 threshold to set for the given domain. * @notice Reverts if adapters are out of order, contain duplicates or if the threshold is not higher than half the count of the adapters * @notice Only callable by the owner of this contract. */ function _enableAdapters(uint256 domain, IAdapter[] memory adapters, uint256 threshold) internal onlyOwner { if (_adapters[domain][LIST_END].next == IAdapter(address(0))) { _adapters[domain][LIST_END].next = LIST_END; _adapters[domain][LIST_END].previous = LIST_END; } if (adapters.length == 0) revert NoAdaptersGiven(); for (uint256 i = 0; i < adapters.length; i++) { IAdapter adapter = adapters[i]; if (adapter == IAdapter(address(0)) || adapter == LIST_END) revert InvalidAdapter(adapter); if (_adapters[domain][adapter].next != IAdapter(address(0))) revert AdapterAlreadyEnabled(adapter); IAdapter previous = _adapters[domain][LIST_END].previous; _adapters[domain][previous].next = adapter; _adapters[domain][adapter].previous = previous; _adapters[domain][LIST_END].previous = adapter; _adapters[domain][adapter].next = LIST_END; _domains[domain].count++; } if (threshold < (_domains[domain].count / 2) + 1) revert InvalidThreshold(threshold); _domains[domain].threshold = threshold; emit ThresholdSet(domain, threshold); emit AdaptersEnabled(domain, adapters); } /** * @dev Returns the hash unanimously agreed upon by all of the given adapters. * @param domain - Uint256 identifier for the domain to query. * @param id - Uint256 identifier to query. * @param adapters - Array of adapter addresses to query. * @return hash - Bytes32 hash agreed upon by the adapters for the given domain. * @notice adapters must be in numerical order from smallest to largest and contain no duplicates. * @notice Reverts if adapters are out of order or contain duplicates. * @notice Reverts if adapters disagree. * @notice Revert if the adapters do not yet have the hash for the given ID. * @notice Reverts if no adapters are set for the given domain. */ function _getHash(uint256 domain, uint256 id, IAdapter[] memory adapters) internal view returns (bytes32) { (uint256 threshold, uint256 count) = getThresholdAndCount(domain); if (adapters.length == 0) revert NoAdaptersGiven(); if (count == 0) revert NoAdaptersEnabled(domain); if (adapters.length < threshold) revert ThresholdNotMet(); checkAdapterOrderAndValidity(domain, adapters); return hashi.getHash(domain, id, adapters); } /** * @dev Returns the hash agreed upon by a threshold of the enabled adapters. * @param domain - Uint256 identifier for the domain to query. * @param id - Uint256 identifier to query. * @return hash - Bytes32 hash agreed upon by a threshold of the adapters for the given domain. * @notice If the threshold is set to 1, the function will return the hash of the first adapter in the list. * @notice Reverts if no threshold is not reached. * @notice Reverts if no adapters are set for the given domain. */ function _getThresholdHash(uint256 domain, uint256 id) internal view returns (bytes32 hash) { IAdapter[] memory adapters = getAdapters(domain); (uint256 threshold, uint256 count) = getThresholdAndCount(domain); if (count == 0) revert NoAdaptersEnabled(domain); if (adapters.length < threshold) revert ThresholdNotMet(); bytes32[] memory hashes = new bytes32[](adapters.length); for (uint256 i = 0; i < adapters.length; i++) { hashes[i] = adapters[i].getHash(domain, id); } for (uint256 i = 0; i < hashes.length; i++) { if (i > hashes.length - threshold) break; bytes32 baseHash = hashes[i]; if (baseHash == bytes32(0)) continue; uint256 num = 0; for (uint256 j = i; j < hashes.length; j++) { if (baseHash == hashes[j]) { num++; if (num == threshold) return hashes[i]; } } } revert ThresholdNotMet(); } /** * @dev Returns the hash unanimously agreed upon by ALL of the enabled adapters. * @param domain - Uint256 identifier for the domain to query. * @param id - Uint256 identifier to query. * @return hash - Bytes32 hash agreed upon by the adapters for the given domain. * @notice Reverts if adapters disagree. * @notice Revert if the adapters do not yet have the hash for the given ID. * @notice Reverts if no adapters are set for the given domain. */ function _getUnanimousHash(uint256 domain, uint256 id) internal view returns (bytes32 hash) { IAdapter[] memory adapters = getAdapters(domain); (uint256 threshold, uint256 count) = getThresholdAndCount(domain); if (count == 0) revert NoAdaptersEnabled(domain); if (adapters.length < threshold) revert ThresholdNotMet(); return hashi.getHash(domain, id, adapters); } /** * @dev Sets the address of the IHashi contract. * @param _hashi - Address of the hashi contract. * @notice Only callable by the owner of this contract. */ function _setHashi(IHashi _hashi) internal onlyOwner { if (hashi == _hashi) revert DuplicateHashiAddress(_hashi); hashi = _hashi; emit HashiSet(hashi); } /** * @dev Sets the threshold of adapters required for a given domain. * @param domain - Uint256 identifier for the domain for which to set the threshold. * @param threshold - Uint256 threshold to set for the given domain. * @notice Only callable by the owner of this contract. * @notice Reverts if threshold is already set to the given value. */ function _setThreshold(uint256 domain, uint256 threshold) internal onlyOwner { uint256 count = _domains[domain].count; if (count == 0) revert CountCannotBeZero(); if (threshold < (count / 2) + 1) revert InvalidThreshold(threshold); if (_domains[domain].threshold == threshold) revert DuplicateThreshold(threshold); _domains[domain].threshold = threshold; emit ThresholdSet(domain, threshold); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol"; import { MerklePatriciaProofVerifier } from "../libraries/MerklePatriciaProofVerifier.sol"; import { IHashiProver } from "../interfaces/IHashiProver.sol"; import { IShoyuBashi } from "../interfaces/IShoyuBashi.sol"; contract HashiProver is IHashiProver { using RLPReader for RLPReader.RLPItem; using RLPReader for bytes; address public immutable SHOYU_BASHI; constructor(address shoyuBashi) { SHOYU_BASHI = shoyuBashi; } function verifyForeignStorage(AccountAndStorageProof calldata proof) internal view returns (bytes[] memory) { bytes memory blockHeader = _checkBlockHeaderAgainstHashi( proof.chainId, proof.blockNumber, proof.blockHeader, proof.ancestralBlockNumber, proof.ancestralBlockHeaders ); RLPReader.RLPItem[] memory blockHeaderFields = blockHeader.toRlpItem().toList(); bytes32 stateRoot = bytes32(blockHeaderFields[3].toUint()); (, , bytes32 expectedStorageHash, ) = _verifyAccountProof(proof.account, stateRoot, proof.accountProof); if (proof.storageHash != expectedStorageHash) revert InvalidStorageHash(); return _verifyStorageProof(proof.storageHash, proof.storageKeys, proof.storageProof); } function _checkBlockHeaderAgainstHashi( uint256 chainId, uint256 blockNumber, bytes memory blockHeader, uint256 ancestralBlockNumber, bytes[] memory ancestralBlockHeaders ) private view returns (bytes memory) { bytes32 blockHeaderHash = keccak256(blockHeader); bytes32 currentBlockHeaderHash = IShoyuBashi(SHOYU_BASHI).getThresholdHash(chainId, blockNumber); if (currentBlockHeaderHash == blockHeaderHash && ancestralBlockHeaders.length == 0) return blockHeader; for (uint256 i = 0; i < ancestralBlockHeaders.length; i++) { RLPReader.RLPItem memory ancestralBlockHeaderRLP = RLPReader.toRlpItem(ancestralBlockHeaders[i]); RLPReader.RLPItem[] memory ancestralBlockHeaderContent = ancestralBlockHeaderRLP.toList(); bytes32 blockParentHash = bytes32(ancestralBlockHeaderContent[0].toUint()); uint256 currentAncestralBlockNumber = uint256(ancestralBlockHeaderContent[8].toUint()); bytes32 ancestralBlockHeaderHash = keccak256(ancestralBlockHeaders[i]); if (ancestralBlockHeaderHash != currentBlockHeaderHash) revert ConflictingBlockHeader( currentAncestralBlockNumber, ancestralBlockHeaderHash, currentBlockHeaderHash ); if (ancestralBlockNumber == currentAncestralBlockNumber && i == ancestralBlockHeaders.length - 1) { return ancestralBlockHeaders[i]; } else if (i == ancestralBlockHeaders.length - 1) { revert AncestralBlockHeadersLengthReached(); } else { currentBlockHeaderHash = blockParentHash; } } revert BlockHeaderNotFound(); } function _verifyAccountProof( address account, bytes32 stateRoot, bytes memory proof ) private pure returns (uint256, uint256, bytes32, bytes32) { bytes memory accountRlp = MerklePatriciaProofVerifier.extractProofValue( stateRoot, abi.encodePacked(keccak256(abi.encodePacked(account))), proof.toRlpItem().toList() ); bytes32 accountStorageRoot = bytes32(accountRlp.toRlpItem().toList()[2].toUint()); if (accountStorageRoot.length == 0) revert InvalidStorageHash(); RLPReader.RLPItem[] memory accountFields = accountRlp.toRlpItem().toList(); if (accountFields.length != 4) revert InvalidAccount(); // [nonce, balance, storageHash, codeHash] return ( accountFields[0].toUint(), accountFields[1].toUint(), bytes32(accountFields[2].toUint()), bytes32(accountFields[3].toUint()) ); } function _verifyStorageProof( bytes32 storageHash, bytes32[] memory storageKeys, bytes[] memory proof ) private pure returns (bytes[] memory) { bytes[] memory results = new bytes[](proof.length); if (storageKeys.length == 0 || proof.length == 0 || storageKeys.length != proof.length) revert InvalidStorageProofParams(); for (uint256 i = 0; i < proof.length; ) { RLPReader.RLPItem memory item = RLPReader.toRlpItem( MerklePatriciaProofVerifier.extractProofValue( storageHash, abi.encodePacked(keccak256(abi.encode(storageKeys[i]))), proof[i].toRlpItem().toList() ) ); results[i] = item.toBytes(); unchecked { ++i; } } return results; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { HashiProver } from "../prover/HashiProver.sol"; contract HashiProverTest is HashiProver { constructor(address shoyuBashi) HashiProver(shoyuBashi) {} function getValue(HashiProver.AccountAndStorageProof calldata proof) external view returns (bytes[] memory) { return verifyForeignStorage(proof); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IJushin } from "../interfaces/IJushin.sol"; import { IAdapter } from "../interfaces/IAdapter.sol"; contract PingPong is IJushin { uint256 public count; event Pong(uint256 count); function ping() external { count++; emit Pong(count); } function onMessage( uint256, uint256, address, uint256, IAdapter[] calldata, bytes calldata ) external returns (bytes memory) { count++; emit Pong(count); return abi.encode(0); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IJushin } from "../interfaces/IJushin.sol"; import { IAdapter } from "../interfaces/IAdapter.sol"; import { IReporter } from "../interfaces/IReporter.sol"; import { IYaho } from "../interfaces/IYaho.sol"; contract PingPongOriginal is IJushin { uint256 public count; event Pong(uint256 count); IYaho yaho; uint256 targetChainId; address peerPingPong; constructor(address yaho_, uint256 targetChainId_) { yaho = IYaho(yaho_); targetChainId = targetChainId_; } function setPeerPingPong(address pingpong) public { peerPingPong = pingpong; } function ping(address[] calldata reporter, address[] calldata adapter) external { IReporter[] memory reporters = new IReporter[](reporter.length); IAdapter[] memory adapters = new IAdapter[](adapter.length); for (uint256 i = 0; i < reporter.length; i++) { reporters[i] = IReporter(reporter[i]); adapters[i] = IAdapter(adapter[i]); } count++; yaho.dispatchMessageToAdapters(targetChainId, 1, peerPingPong, hex"00", reporters, adapters); emit Pong(count); } function onMessage( uint256, uint256, address, uint256, IAdapter[] calldata, bytes calldata ) external returns (bytes memory) { count++; emit Pong(count); return abi.encode(0); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IJushin } from "../interfaces/IJushin.sol"; import { IAdapter } from "../interfaces/IAdapter.sol"; import { IReporter } from "../interfaces/IReporter.sol"; import { IYaho } from "../interfaces/IYaho.sol"; contract PingPong is IJushin { uint256 public count; event Pong(uint256 count); IYaho yaho; uint256 targetChainId; address peerPingPong; constructor(address yaho_, uint256 targetChainId_) { yaho = IYaho(yaho_); targetChainId = targetChainId_; } function setPeerPingPong(address pingpong) public { peerPingPong = pingpong; } function ping(address[] calldata reporter, address[] calldata adapter) external { IReporter[] memory reporters = new IReporter[](reporter.length); IAdapter[] memory adapters = new IAdapter[](adapter.length); for (uint256 i = 0; i < reporter.length; i++) { reporters[i] = IReporter(reporter[i]); adapters[i] = IAdapter(adapter[i]); } count++; yaho.dispatchMessageToAdapters(targetChainId, 1, peerPingPong, hex"00", reporters, adapters); emit Pong(count); } function onMessage( uint256, uint256, address, uint256, IAdapter[] calldata, bytes calldata ) external returns (bytes memory) { count++; emit Pong(count); return abi.encode(0); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IMessageHashCalculator } from "../interfaces/IMessageHashCalculator.sol"; import { Message } from "../interfaces/IMessage.sol"; contract MessageHashCalculator is IMessageHashCalculator { /// @inheritdoc IMessageHashCalculator function calculateMessageHash(Message memory message) public pure returns (bytes32) { return keccak256(abi.encode(message)); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { IMessageIdCalculator } from "../interfaces/IMessageIdCalculator.sol"; contract MessageIdCalculator is IMessageIdCalculator { /// @inheritdoc IMessageIdCalculator function calculateMessageId( uint256 sourceChainId, address dispatcherAddress, bytes32 messageHash ) public pure returns (uint256) { return uint256(keccak256(abi.encode(sourceChainId, dispatcherAddress, messageHash))); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import { IYaru } from "./interfaces/IYaru.sol"; import { IHashi, IAdapter } from "./interfaces/IHashi.sol"; import { Message } from "./interfaces/IMessage.sol"; import { MessageIdCalculator } from "./utils/MessageIdCalculator.sol"; import { MessageHashCalculator } from "./utils/MessageHashCalculator.sol"; import { IJushin } from "./interfaces/IJushin.sol"; contract Yaru is IYaru, MessageIdCalculator, MessageHashCalculator, ReentrancyGuard { address public immutable HASHI; address public immutable YAHO; uint256 public immutable SOURCE_CHAIN_ID; mapping(uint256 => bool) public executed; constructor(address hashi, address yaho_, uint256 sourceChainId) { HASHI = hashi; YAHO = yaho_; SOURCE_CHAIN_ID = sourceChainId; } /// @inheritdoc IYaru function executeMessages(Message[] calldata messages) external nonReentrant returns (bytes[] memory) { bytes[] memory returnDatas = new bytes[](messages.length); for (uint256 i = 0; i < messages.length; ) { Message memory message = messages[i]; bytes32 messageHash = calculateMessageHash(message); uint256 messageId = calculateMessageId(SOURCE_CHAIN_ID, YAHO, messageHash); if (message.targetChainId != block.chainid) revert InvalidToChainId(message.targetChainId, block.chainid); if (executed[messageId]) revert MessageIdAlreadyExecuted(messageId); executed[messageId] = true; if ( !IHashi(HASHI).checkHashWithThresholdFromAdapters( SOURCE_CHAIN_ID, messageId, message.threshold, message.adapters ) ) revert ThresholdNotMet(); try IJushin(message.receiver).onMessage( messageId, SOURCE_CHAIN_ID, message.sender, message.threshold, message.adapters, message.data ) returns (bytes memory returnData) { returnDatas[i] = returnData; } catch { revert CallFailed(); } emit MessageExecuted(messageId, message); unchecked { ++i; } } return returnDatas; } }
// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity >=0.8.0 <0.9.0; library BytesLib { function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1 , "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equal_nonAligned(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let endMinusWord := add(_preBytes, length) let mc := add(_preBytes, 0x20) let cc := add(_postBytes, 0x20) for { // the next line is the loop condition: // while(uint256(mc < endWord) + cb == 2) } eq(add(lt(mc, endMinusWord), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } // Only if still successful // For <1 word tail bytes if gt(success, 0) { // Get the remainder of length/32 // length % 32 = AND(length, 32 - 1) let numTailBytes := and(length, 0x1f) let mcRem := mload(mc) let ccRem := mload(cc) for { let i := 0 // the next line is the loop condition: // while(uint256(i < numTailBytes) + cb == 2) } eq(add(lt(i, numTailBytes), cb), 2) { i := add(i, 1) } { if iszero(eq(byte(i, mcRem), byte(i, ccRem))) { // unsuccess: success := 0 cb := 0 } } } } default { // unsuccess: success := 0 } } return success; } function equalStorage( bytes storage _preBytes, bytes memory _postBytes ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
// SPDX-License-Identifier: Apache-2.0 /* * @author Hamdi Allam [email protected] * Please reach out with any questions or concerns */ pragma solidity >=0.5.10 <0.9.0; library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint256 len; uint256 memPtr; } struct Iterator { RLPItem item; // Item that's being iterated over. uint256 nextPtr; // Position of the next item in the list. } /* * @dev Returns the next element in the iteration. Reverts if it has not next element. * @param self The iterator. * @return The next element in the iteration. */ function next(Iterator memory self) internal pure returns (RLPItem memory) { require(hasNext(self)); uint256 ptr = self.nextPtr; uint256 itemLength = _itemLength(ptr); self.nextPtr = ptr + itemLength; return RLPItem(itemLength, ptr); } /* * @dev Returns true if the iteration has more elements. * @param self The iterator. * @return true if the iteration has more elements. */ function hasNext(Iterator memory self) internal pure returns (bool) { RLPItem memory item = self.item; return self.nextPtr < item.memPtr + item.len; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint256 memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @dev Create an iterator. Reverts if item is not a list. * @param self The RLP item. * @return An 'Iterator' over the item. */ function iterator(RLPItem memory self) internal pure returns (Iterator memory) { require(isList(self)); uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); return Iterator(self, ptr); } /* * @param the RLP item. */ function rlpLen(RLPItem memory item) internal pure returns (uint256) { return item.len; } /* * @param the RLP item. * @return (memPtr, len) pair: location of the item's payload in memory. */ function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { uint256 offset = _payloadOffset(item.memPtr); uint256 memPtr = item.memPtr + offset; uint256 len = item.len - offset; // data length return (memPtr, len); } /* * @param the RLP item. */ function payloadLen(RLPItem memory item) internal pure returns (uint256) { (, uint256 len) = payloadLocation(item); return len; } /* * @param the RLP item containing the encoded list. */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint256 items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 dataLen; for (uint256 i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint256 memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /* * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. * @return keccak256 hash of RLP encoded bytes. */ function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { uint256 ptr = item.memPtr; uint256 len = item.len; bytes32 result; assembly { result := keccak256(ptr, len) } return result; } /* * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. * @return keccak256 hash of the item payload. */ function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { (uint256 memPtr, uint256 len) = payloadLocation(item); bytes32 result; assembly { result := keccak256(memPtr, len) } return result; } /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); if (result.length == 0) return result; uint256 ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } // any non-zero byte except "0x80" is considered true function toBoolean(RLPItem memory item) internal pure returns (bool) { require(item.len == 1); uint256 result; uint256 memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } // SEE Github Issue #5. // Summary: Most commonly used RLP libraries (i.e Geth) will encode // "0" as "0x80" instead of as "0". We handle this edge case explicitly // here. if (result == 0 || result == STRING_SHORT_START) { return false; } else { return true; } } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix require(item.len == 21); return address(uint160(toUint(item))); } function toUint(RLPItem memory item) internal pure returns (uint256) { require(item.len > 0 && item.len <= 33); (uint256 memPtr, uint256 len) = payloadLocation(item); uint256 result; assembly { result := mload(memPtr) // shift to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } // enforces 32 byte length function toUintStrict(RLPItem memory item) internal pure returns (uint256) { // one byte prefix require(item.len == 33); uint256 result; uint256 memPtr = item.memPtr + 1; assembly { result := mload(memPtr) } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); (uint256 memPtr, uint256 len) = payloadLocation(item); bytes memory result = new bytes(len); uint256 destPtr; assembly { destPtr := add(0x20, result) } copy(memPtr, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint256) { if (item.len == 0) return 0; uint256 count = 0; uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint256 memPtr) private pure returns (uint256) { uint256 itemLen; uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) { itemLen = 1; } else if (byte0 < STRING_LONG_START) { itemLen = byte0 - STRING_SHORT_START + 1; } else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint256 memPtr) private pure returns (uint256) { uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) { return 0; } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) { return 1; } else if (byte0 < LIST_SHORT_START) { // being explicit return byte0 - (STRING_LONG_START - 1) + 1; } else { return byte0 - (LIST_LONG_START - 1) + 1; } } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy(uint256 src, uint256 dest, uint256 len) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } if (len > 0) { // left over bytes. Mask is used to remove unwanted bytes from the word uint256 mask = 256**(WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 10000 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract ABI
API[{"inputs":[{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"targetChainId","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"contract IReporter[]","name":"reporters","type":"address[]"},{"internalType":"contract IAdapter[]","name":"adapters","type":"address[]"}],"internalType":"struct Message","name":"message","type":"tuple"}],"name":"InvalidMessage","type":"error"},{"inputs":[{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"uint256","name":"maxThreshold","type":"uint256"}],"name":"InvalidThreshold","type":"error"},{"inputs":[{"internalType":"uint256","name":"messageId","type":"uint256"}],"name":"MessageHashNotFound","type":"error"},{"inputs":[],"name":"NoAdaptersGiven","type":"error"},{"inputs":[],"name":"NoMessageIdsGiven","type":"error"},{"inputs":[],"name":"NoMessagesGiven","type":"error"},{"inputs":[],"name":"NoReportersGiven","type":"error"},{"inputs":[{"internalType":"uint256","name":"arrayOne","type":"uint256"},{"internalType":"uint256","name":"arrayTwo","type":"uint256"}],"name":"UnequalArrayLengths","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"messageId","type":"uint256"},{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"targetChainId","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"contract IReporter[]","name":"reporters","type":"address[]"},{"internalType":"contract IAdapter[]","name":"adapters","type":"address[]"}],"indexed":false,"internalType":"struct Message","name":"message","type":"tuple"}],"name":"MessageDispatched","type":"event"},{"inputs":[{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"targetChainId","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"contract IReporter[]","name":"reporters","type":"address[]"},{"internalType":"contract IAdapter[]","name":"adapters","type":"address[]"}],"internalType":"struct Message","name":"message","type":"tuple"}],"name":"calculateMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"sourceChainId","type":"uint256"},{"internalType":"address","name":"dispatcherAddress","type":"address"},{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"calculateMessageId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"currentNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"targetChainId","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"contract IReporter[]","name":"reporters","type":"address[]"},{"internalType":"contract IAdapter[]","name":"adapters","type":"address[]"}],"name":"dispatchMessage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"targetChainId","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"contract IReporter[]","name":"reporters","type":"address[]"},{"internalType":"contract IAdapter[]","name":"adapters","type":"address[]"}],"name":"dispatchMessageToAdapters","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"targetChainId","type":"uint256"},{"internalType":"uint256[]","name":"thresholds","type":"uint256[]"},{"internalType":"address[]","name":"receivers","type":"address[]"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"contract IReporter[]","name":"reporters","type":"address[]"},{"internalType":"contract IAdapter[]","name":"adapters","type":"address[]"}],"name":"dispatchMessagesToAdapters","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"messageId","type":"uint256"}],"name":"getPendingMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"targetChainId","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"contract IReporter[]","name":"reporters","type":"address[]"},{"internalType":"contract IAdapter[]","name":"adapters","type":"address[]"}],"internalType":"struct Message[]","name":"messages","type":"tuple[]"}],"name":"relayMessagesToAdapters","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"payable","type":"function"}]
Contract Creation Code
6080806040523461001657611305908161001c8239f35b600080fdfe610140604052600436101561001357600080fd5b60003560e01c806331fa321b14610ab657806339d0bf63146109df5780633db59eeb146108a1578063543836b11461085e578063553c19dd1461049d578063adb610a31461047f578063b38fda7c146100a65763feed46df1461007557600080fd5b346100a15760206003193601126100a15760043560005260006020526020604060002054604051908152f35b600080fd5b60c06003193601126100a15760243567ffffffffffffffff81116100a1576100d2903690600401610b1a565b9060443567ffffffffffffffff81116100a1576100f3903690600401610b1a565b9260643567ffffffffffffffff81116100a157610114903690600401610b1a565b94909160843567ffffffffffffffff81116100a157610137903690600401610b1a565b60a05260805260a43567ffffffffffffffff81116100a15761015d903690600401610b1a565b60c05260e052818103610449578581036104125761017a82610e52565b6101005261018782610e52565b6101205260005b828110610202576101a060a051610e52565b506101fe6101d66101b63660a051608051610d11565b6101c53660c05160e051610d11565b90610120516101005160043561112c565b604051918291604083526101f06040840161010051610bf3565b908382036020850152610bf3565b0390f35b61020d818388610e83565b3560a051156103e85760c051156103be5760c05160a051036103845760a0518111801561037c575b6103435750610245818388610e83565b3590610252818588610e83565b359173ffffffffffffffffffffffffffffffffffffffff831683036100a15788821015610314578160051b8601357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1873603018112156100a15786019267ffffffffffffffff8435116100a1578335360360208501136100a1576001936102f29260c0519260e0519260a05192608051926020833593019160043561105f565b6102ff8361012051610ea0565b5261030d8261010051610ea0565b520161018e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604490604051907f7b7a98f1000000000000000000000000000000000000000000000000000000008252600482015260a0516024820152fd5b508015610235565b60446040517fdf9ccef500000000000000000000000000000000000000000000000000000000815260a051600482015260c0516024820152fd5b60046040517fc77f9c81000000000000000000000000000000000000000000000000000000008152fd5b60046040517f66ab7afe000000000000000000000000000000000000000000000000000000008152fd5b60449086604051917fdf9ccef500000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b604491604051917fdf9ccef500000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b346100a15760006003193601126100a1576020600154604051908152f35b60206003193601126100a15760043567ffffffffffffffff81116100a1576104c9903690600401610b1a565b80156108345760206104db8284610eb4565b01356104f46104ea8385610eb4565b60c0810190610eed565b61050b6105018587610eb4565b60e0810190610eed565b919060405193828593602085019760808601908952606060408701525260a08401919060005b8181106107f75750505080601f19846020930301606085015284815201909260005b8181106107bc57505061056f925003601f198101835282610c8f565b51902061057b82610e52565b9061058583610e52565b9060005b8481106105f6576101fe6105e2868887876105dc60206105a98686610eb4565b0135946105d46105ca6105016105c26104ea858a610eb4565b949098610eb4565b9690923691610d11565b943691610d11565b9361112c565b604051918291602083526020830190610bf3565b8060051b8601357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01873603018112156100a1576106369036908801610d67565b8115158061075a575b6107205761064f61065691611264565b304661127f565b80600052600060205260406000205480156106ef576106758387610ea0565b52806106818386610ea0565b526000526000602052600060408120557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106c057600101610589565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602482604051907f92e4a25e0000000000000000000000000000000000000000000000000000000082526004820152fd5b610756906040519182917f69b2c1a400000000000000000000000000000000000000000000000000000000835260048301610f8b565b0390fd5b5060208101516107916107b160c08401516107a360e086015160405194859360208501978852606060408601526080850190610f41565b90601f19848303016060850152610f41565b03601f198101835282610c8f565b51902083141561063f565b9160019193506020809173ffffffffffffffffffffffffffffffffffffffff6107e488610af9565b1681520194019101918492939193610553565b91939594509160208060019273ffffffffffffffffffffffffffffffffffffffff61082188610af9565b1681520194019101918694959392610531565b60046040517f6832f637000000000000000000000000000000000000000000000000000000008152fd5b346100a15760206003193601126100a15760043567ffffffffffffffff81116100a1576108996108946020923690600401610d67565b611264565b604051908152f35b6108aa36610b4b565b949092939196979587156103e85785156103be578588036109a85787891180156109a0575b610969578584898761090197966108f9966108ef9661094c9e9f8e61105f565b9490983691610d11565b933691610d11565b926040519061090f82610c73565b60018252602036818401376040519261092784610c73565b60018452602036818601378761093c84610e93565b5261094684610e93565b5261112c565b906101fe6040519283928352604060208401526040830190610bf3565b60448989604051917f7b7a98f100000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5088156108cf565b60448887604051917fdf9ccef500000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b346100a1576109ed36610b4b565b97829692979197959395156103e85788156103be57888703610a7f578682118015610a77575b610a405791610a2a9795939160209997959361105f565b8160005260008352604060002055604051908152f35b60448288604051917f7b7a98f100000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b508115610a13565b6044878a604051917fdf9ccef500000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b346100a15760606003193601126100a15760243573ffffffffffffffffffffffffffffffffffffffff811681036100a1576108996020916044359060043561127f565b359073ffffffffffffffffffffffffffffffffffffffff821682036100a157565b9181601f840112156100a15782359167ffffffffffffffff83116100a1576020808501948460051b0101116100a157565b60c06003198201126100a157600435916024359160443573ffffffffffffffffffffffffffffffffffffffff811681036100a157916064359167ffffffffffffffff908184116100a157806023850112156100a1578360040135938285116100a15781602486830101116100a15760240193926084358381116100a15782610bd591600401610b1a565b9390939260a4359182116100a157610bef91600401610b1a565b9091565b90815180825260208080930193019160005b828110610c13575050505090565b835185529381019392810192600101610c05565b610100810190811067ffffffffffffffff821117610c4457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff821117610c4457604052565b90601f601f19910116810190811067ffffffffffffffff821117610c4457604052565b92919267ffffffffffffffff8211610c445760405191610cdc6020601f19601f8401160184610c8f565b8294818452818301116100a1578281602093846000960137010152565b67ffffffffffffffff8111610c445760051b60200190565b9291610d1c82610cf9565b91610d2a6040519384610c8f565b829481845260208094019160051b81019283116100a157905b828210610d505750505050565b838091610d5c84610af9565b815201910190610d43565b9190610100838203126100a15760405190610d8182610c27565b8193803583526020810135602084015260408101356040840152610da760608201610af9565b6060840152610db860808201610af9565b608084015267ffffffffffffffff9060a08101358281116100a157810183601f820112156100a15783816020610df093359101610cb2565b60a085015260c08101358281116100a157810183601f820112156100a15783816020610e1e93359101610d11565b60c085015260e08101359182116100a1570181601f820112156100a15760e091816020610e4d93359101610d11565b910152565b90610e5c82610cf9565b610e696040519182610c8f565b828152601f19610e798294610cf9565b0190602036910137565b91908110156103145760051b0190565b8051156103145760200190565b80518210156103145760209160051b010190565b9015610314578035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01813603018212156100a1570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156100a1570180359067ffffffffffffffff82116100a157602001918160051b360383136100a157565b90815180825260208080930193019160005b828110610f61575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610f53565b60208082528251818301528083015160408301526040830151606083015273ffffffffffffffffffffffffffffffffffffffff80606085015116608084015260808401511660a083015260a0830151610100918260c085015281519061012092828487015260005b83811061104a575050509160e0611037611047969593601f95610140906000828289010152601f199788910116860160c08901519287820301858801520190610f41565b9401519282850301910152610f41565b90565b81810183015187820161014001528201610ff3565b96959291989794939098600154996040519861107a8a610c27565b8b8a5260208a0152604089015233606089015273ffffffffffffffffffffffffffffffffffffffff16608088015236906110b392610cb2565b60a086015236906110c392610d11565b60c084015236906110d392610d11565b60e08201526110e181611264565b926110ed84304661127f565b916001839201600155604051611104819282610f8b565b037f218247aabc759e65b5bb92ccc074f9d62cd187259f2a0984c3c9cf91f67ff7cf91a29190565b919493909461113b8451610e52565b9360005b815181101561125a5773ffffffffffffffffffffffffffffffffffffffff90816111698285610ea0565b51168061117c575b50600191500161113f565b6111f29261118a8388610ea0565b51166040918251917fb4a90ff7000000000000000000000000000000000000000000000000000000008352896004840152602483015260806044830152816111d68d6084830190610bf3565b91600319828403016064830152816000816020998a968d610bf3565b03925af1918215611250575060009161121c575b50600192506112158289610ea0565b5238611171565b919282813d8311611249575b6112328183610c8f565b810103126112465750906001915138611206565b80fd5b503d611228565b513d6000823e3d90fd5b5093955050505050565b604051611279816107a3602082019485610f8b565b51902090565b9173ffffffffffffffffffffffffffffffffffffffff60405192602084019485521660408301526060820152606081526080810181811067ffffffffffffffff821117610c44576040525190209056fea2646970667358221220158d2361e138404a854dd77553b246db43425c5e243638561379c29cf6f3a03c64736f6c63430008140033
Deployed Bytecode
0x610140604052600436101561001357600080fd5b60003560e01c806331fa321b14610ab657806339d0bf63146109df5780633db59eeb146108a1578063543836b11461085e578063553c19dd1461049d578063adb610a31461047f578063b38fda7c146100a65763feed46df1461007557600080fd5b346100a15760206003193601126100a15760043560005260006020526020604060002054604051908152f35b600080fd5b60c06003193601126100a15760243567ffffffffffffffff81116100a1576100d2903690600401610b1a565b9060443567ffffffffffffffff81116100a1576100f3903690600401610b1a565b9260643567ffffffffffffffff81116100a157610114903690600401610b1a565b94909160843567ffffffffffffffff81116100a157610137903690600401610b1a565b60a05260805260a43567ffffffffffffffff81116100a15761015d903690600401610b1a565b60c05260e052818103610449578581036104125761017a82610e52565b6101005261018782610e52565b6101205260005b828110610202576101a060a051610e52565b506101fe6101d66101b63660a051608051610d11565b6101c53660c05160e051610d11565b90610120516101005160043561112c565b604051918291604083526101f06040840161010051610bf3565b908382036020850152610bf3565b0390f35b61020d818388610e83565b3560a051156103e85760c051156103be5760c05160a051036103845760a0518111801561037c575b6103435750610245818388610e83565b3590610252818588610e83565b359173ffffffffffffffffffffffffffffffffffffffff831683036100a15788821015610314578160051b8601357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1873603018112156100a15786019267ffffffffffffffff8435116100a1578335360360208501136100a1576001936102f29260c0519260e0519260a05192608051926020833593019160043561105f565b6102ff8361012051610ea0565b5261030d8261010051610ea0565b520161018e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604490604051907f7b7a98f1000000000000000000000000000000000000000000000000000000008252600482015260a0516024820152fd5b508015610235565b60446040517fdf9ccef500000000000000000000000000000000000000000000000000000000815260a051600482015260c0516024820152fd5b60046040517fc77f9c81000000000000000000000000000000000000000000000000000000008152fd5b60046040517f66ab7afe000000000000000000000000000000000000000000000000000000008152fd5b60449086604051917fdf9ccef500000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b604491604051917fdf9ccef500000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b346100a15760006003193601126100a1576020600154604051908152f35b60206003193601126100a15760043567ffffffffffffffff81116100a1576104c9903690600401610b1a565b80156108345760206104db8284610eb4565b01356104f46104ea8385610eb4565b60c0810190610eed565b61050b6105018587610eb4565b60e0810190610eed565b919060405193828593602085019760808601908952606060408701525260a08401919060005b8181106107f75750505080601f19846020930301606085015284815201909260005b8181106107bc57505061056f925003601f198101835282610c8f565b51902061057b82610e52565b9061058583610e52565b9060005b8481106105f6576101fe6105e2868887876105dc60206105a98686610eb4565b0135946105d46105ca6105016105c26104ea858a610eb4565b949098610eb4565b9690923691610d11565b943691610d11565b9361112c565b604051918291602083526020830190610bf3565b8060051b8601357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01873603018112156100a1576106369036908801610d67565b8115158061075a575b6107205761064f61065691611264565b304661127f565b80600052600060205260406000205480156106ef576106758387610ea0565b52806106818386610ea0565b526000526000602052600060408120557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106c057600101610589565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602482604051907f92e4a25e0000000000000000000000000000000000000000000000000000000082526004820152fd5b610756906040519182917f69b2c1a400000000000000000000000000000000000000000000000000000000835260048301610f8b565b0390fd5b5060208101516107916107b160c08401516107a360e086015160405194859360208501978852606060408601526080850190610f41565b90601f19848303016060850152610f41565b03601f198101835282610c8f565b51902083141561063f565b9160019193506020809173ffffffffffffffffffffffffffffffffffffffff6107e488610af9565b1681520194019101918492939193610553565b91939594509160208060019273ffffffffffffffffffffffffffffffffffffffff61082188610af9565b1681520194019101918694959392610531565b60046040517f6832f637000000000000000000000000000000000000000000000000000000008152fd5b346100a15760206003193601126100a15760043567ffffffffffffffff81116100a1576108996108946020923690600401610d67565b611264565b604051908152f35b6108aa36610b4b565b949092939196979587156103e85785156103be578588036109a85787891180156109a0575b610969578584898761090197966108f9966108ef9661094c9e9f8e61105f565b9490983691610d11565b933691610d11565b926040519061090f82610c73565b60018252602036818401376040519261092784610c73565b60018452602036818601378761093c84610e93565b5261094684610e93565b5261112c565b906101fe6040519283928352604060208401526040830190610bf3565b60448989604051917f7b7a98f100000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5088156108cf565b60448887604051917fdf9ccef500000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b346100a1576109ed36610b4b565b97829692979197959395156103e85788156103be57888703610a7f578682118015610a77575b610a405791610a2a9795939160209997959361105f565b8160005260008352604060002055604051908152f35b60448288604051917f7b7a98f100000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b508115610a13565b6044878a604051917fdf9ccef500000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b346100a15760606003193601126100a15760243573ffffffffffffffffffffffffffffffffffffffff811681036100a1576108996020916044359060043561127f565b359073ffffffffffffffffffffffffffffffffffffffff821682036100a157565b9181601f840112156100a15782359167ffffffffffffffff83116100a1576020808501948460051b0101116100a157565b60c06003198201126100a157600435916024359160443573ffffffffffffffffffffffffffffffffffffffff811681036100a157916064359167ffffffffffffffff908184116100a157806023850112156100a1578360040135938285116100a15781602486830101116100a15760240193926084358381116100a15782610bd591600401610b1a565b9390939260a4359182116100a157610bef91600401610b1a565b9091565b90815180825260208080930193019160005b828110610c13575050505090565b835185529381019392810192600101610c05565b610100810190811067ffffffffffffffff821117610c4457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff821117610c4457604052565b90601f601f19910116810190811067ffffffffffffffff821117610c4457604052565b92919267ffffffffffffffff8211610c445760405191610cdc6020601f19601f8401160184610c8f565b8294818452818301116100a1578281602093846000960137010152565b67ffffffffffffffff8111610c445760051b60200190565b9291610d1c82610cf9565b91610d2a6040519384610c8f565b829481845260208094019160051b81019283116100a157905b828210610d505750505050565b838091610d5c84610af9565b815201910190610d43565b9190610100838203126100a15760405190610d8182610c27565b8193803583526020810135602084015260408101356040840152610da760608201610af9565b6060840152610db860808201610af9565b608084015267ffffffffffffffff9060a08101358281116100a157810183601f820112156100a15783816020610df093359101610cb2565b60a085015260c08101358281116100a157810183601f820112156100a15783816020610e1e93359101610d11565b60c085015260e08101359182116100a1570181601f820112156100a15760e091816020610e4d93359101610d11565b910152565b90610e5c82610cf9565b610e696040519182610c8f565b828152601f19610e798294610cf9565b0190602036910137565b91908110156103145760051b0190565b8051156103145760200190565b80518210156103145760209160051b010190565b9015610314578035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01813603018212156100a1570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156100a1570180359067ffffffffffffffff82116100a157602001918160051b360383136100a157565b90815180825260208080930193019160005b828110610f61575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610f53565b60208082528251818301528083015160408301526040830151606083015273ffffffffffffffffffffffffffffffffffffffff80606085015116608084015260808401511660a083015260a0830151610100918260c085015281519061012092828487015260005b83811061104a575050509160e0611037611047969593601f95610140906000828289010152601f199788910116860160c08901519287820301858801520190610f41565b9401519282850301910152610f41565b90565b81810183015187820161014001528201610ff3565b96959291989794939098600154996040519861107a8a610c27565b8b8a5260208a0152604089015233606089015273ffffffffffffffffffffffffffffffffffffffff16608088015236906110b392610cb2565b60a086015236906110c392610d11565b60c084015236906110d392610d11565b60e08201526110e181611264565b926110ed84304661127f565b916001839201600155604051611104819282610f8b565b037f218247aabc759e65b5bb92ccc074f9d62cd187259f2a0984c3c9cf91f67ff7cf91a29190565b919493909461113b8451610e52565b9360005b815181101561125a5773ffffffffffffffffffffffffffffffffffffffff90816111698285610ea0565b51168061117c575b50600191500161113f565b6111f29261118a8388610ea0565b51166040918251917fb4a90ff7000000000000000000000000000000000000000000000000000000008352896004840152602483015260806044830152816111d68d6084830190610bf3565b91600319828403016064830152816000816020998a968d610bf3565b03925af1918215611250575060009161121c575b50600192506112158289610ea0565b5238611171565b919282813d8311611249575b6112328183610c8f565b810103126112465750906001915138611206565b80fd5b503d611228565b513d6000823e3d90fd5b5093955050505050565b604051611279816107a3602082019485610f8b565b51902090565b9173ffffffffffffffffffffffffffffffffffffffff60405192602084019485521660408301526060820152606081526080810181811067ffffffffffffffff821117610c44576040525190209056fea2646970667358221220158d2361e138404a854dd77553b246db43425c5e243638561379c29cf6f3a03c64736f6c63430008140033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.