Contract Name:
HubVotePool
Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.23;
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {HubEvmSpokeVoteDecoder} from "src/HubEvmSpokeVoteDecoder.sol";
import {IWormhole} from "wormhole-sdk/interfaces/IWormhole.sol";
import {QueryResponse, ParsedQueryResponse} from "wormhole-sdk/QueryResponse.sol";
import {Checkpoints} from "src/lib/Checkpoints.sol";
import {ISpokeVoteDecoder} from "src/interfaces/ISpokeVoteDecoder.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
/// @title HubVotePool
/// @author [ScopeLift](https://scopelift.co)
/// @notice A contract that parses a specific wormhole query type from the `SpokeVoteAggregator`.
contract HubVotePool is QueryResponse, Ownable {
using Checkpoints for Checkpoints.Trace256;
using ERC165Checker for address;
/// @notice The governor where cross chain votes are submitted.
IGovernor public hubGovernor;
/// @notice A necessary param which is ignored when submitting a vote.
uint8 private constant UNUSED_SUPPORT_PARAM = 1;
/// @notice Thrown when the submitted spoke aggregator vote has a vote that is inconsistent with the previously
/// submitted vote.
error InvalidProposalVote();
/// @notice Thrown if a query vote implementation is set to an address that does not support the
/// `ISpokeVoteDecoder` interface.
error InvalidQueryVoteImpl();
/// @notice Thrown if a vote query is submitted with an unsupported query type.
error UnsupportedQueryType();
/// @notice Emitted when the Governor is updated.
event HubGovernorUpdated(address oldGovernor, address newGovernor);
/// @notice Emitted when a new query type is registered.
event QueryTypeRegistered(uint8 indexed queryType, address oldQueryTypeImpl, address newQueryTypeImpl);
/// @notice Emitted when a vote is recorded from a registered spoke vote aggregator.
event SpokeVoteCast(
uint16 indexed emitterChainId, uint256 proposalId, uint256 voteAgainst, uint256 voteFor, uint256 voteAbstain
);
/// @notice Emitted whtn a new spoke vote address is registered.
event SpokeRegistered(uint16 indexed targetChain, bytes32 oldSpokeVoteAddress, bytes32 newSpokeVoteAddress);
/// @dev Contains the distribution of a proposal vote.
struct ProposalVote {
uint256 againstVotes;
uint256 forVotes;
uint256 abstainVotes;
}
/// @dev Contains the information to register a spoke.
struct SpokeVoteAggregator {
uint16 wormholeChainId;
bytes32 wormholeAddress;
}
/// @notice A mapping of a chain and emitter address that determines valid spokes and addresses for receiving votes.
// Add 256 checkpoints for registry
mapping(uint16 emitterChain => Checkpoints.Trace256 emitterAddress) internal emitterRegistry;
mapping(bytes32 spokeProposalId => ProposalVote proposalVotes) public spokeProposalVotes;
mapping(uint8 queryType => ISpokeVoteDecoder voteImpl) public voteTypeDecoder;
constructor(address _core, address _hubGovernor, address _owner) QueryResponse(_core) Ownable(_owner) {
HubEvmSpokeVoteDecoder evmDecoder = new HubEvmSpokeVoteDecoder(_core, address(this));
_registerQueryType(address(evmDecoder), QueryResponse.QT_ETH_CALL_WITH_FINALITY);
_setGovernor(_hubGovernor);
}
function getSpoke(uint16 _emitterChainId, uint256 _timepoint) external view returns (bytes32) {
return bytes32(emitterRegistry[_emitterChainId].upperLookup(_timepoint));
}
/// @notice Registers or unregisters a query type implementation.
/// @dev Can only be called by the contract owner. Unregisters if the implementation address is zero.
/// @param _queryType The type of query to register.
/// @param _implementation The address of the implementation contract for the query type.
function registerQueryType(uint8 _queryType, address _implementation) external {
_checkOwner();
_registerQueryType(_implementation, _queryType);
}
/// @notice Registers a new spoke chain and its vote aggregator address.
/// @dev Can only be called by the contract owner.
/// @param _targetChain The Wormhole chain ID of the spoke chain.
/// @param _spokeVoteAddress The address of the vote aggregator on the spoke chain.
function registerSpoke(uint16 _targetChain, bytes32 _spokeVoteAddress) external {
_checkOwner();
_registerSpoke(_targetChain, _spokeVoteAddress);
}
/// @notice Registers multiple spoke chains with their corresponding vote aggregator in a single call.
/// @param _spokes An an array of spoke vote aggregators to be registered.
function registerSpokes(SpokeVoteAggregator[] memory _spokes) external {
_checkOwner();
for (uint256 i = 0; i < _spokes.length; i++) {
SpokeVoteAggregator memory _aggregator = _spokes[i];
_registerSpoke(_aggregator.wormholeChainId, _aggregator.wormholeAddress);
}
}
/// @notice Updates the address of the hub governor.
/// @dev Can only be called by the contract owner.
/// @param _newGovernor The address of the new hub governor.
function setGovernor(address _newGovernor) external {
_checkOwner();
_setGovernor(_newGovernor);
}
/// @notice Processes cross chain votes from the spokes. Parses and verifies the Wormhole query response, then casts
/// votes on the hub governor.
/// @param _queryResponseRaw The raw bytes of the query response from Wormhole.
/// @param _signatures The signatures verifying the Wormhole message.
function crossChainVote(bytes memory _queryResponseRaw, IWormhole.Signature[] memory _signatures) external {
ParsedQueryResponse memory _queryResponse = parseAndVerifyQueryResponse(_queryResponseRaw, _signatures);
for (uint256 i = 0; i < _queryResponse.responses.length; i++) {
ISpokeVoteDecoder _voteQueryImpl = voteTypeDecoder[_queryResponse.responses[i].queryType];
if (address(_voteQueryImpl) == address(0)) revert UnsupportedQueryType();
ISpokeVoteDecoder.QueryVote memory _voteQuery = _voteQueryImpl.decode(_queryResponse.responses[i], hubGovernor);
ISpokeVoteDecoder.ProposalVote memory _proposalVote = _voteQuery.proposalVote;
ProposalVote memory _existingSpokeVote = spokeProposalVotes[_voteQuery.spokeProposalId];
if (
_existingSpokeVote.againstVotes > _proposalVote.againstVotes
|| _existingSpokeVote.forVotes > _proposalVote.forVotes
|| _existingSpokeVote.abstainVotes > _proposalVote.abstainVotes
) revert InvalidProposalVote();
spokeProposalVotes[_voteQuery.spokeProposalId] =
ProposalVote(_proposalVote.againstVotes, _proposalVote.forVotes, _proposalVote.abstainVotes);
_castVote(
_voteQuery.proposalId,
ProposalVote(
_proposalVote.againstVotes - _existingSpokeVote.againstVotes,
_proposalVote.forVotes - _existingSpokeVote.forVotes,
_proposalVote.abstainVotes - _existingSpokeVote.abstainVotes
),
_voteQuery.chainId
);
}
}
function _castVote(uint256 _proposalId, ProposalVote memory _vote, uint16 _emitterChainId) internal {
bytes memory _votes = abi.encodePacked(
SafeCast.toUint128(_vote.againstVotes), SafeCast.toUint128(_vote.forVotes), SafeCast.toUint128(_vote.abstainVotes)
);
hubGovernor.castVoteWithReasonAndParams(
_proposalId, UNUSED_SUPPORT_PARAM, "rolled-up vote from governance spoke token holders", _votes
);
emit SpokeVoteCast(_emitterChainId, _proposalId, _vote.againstVotes, _vote.forVotes, _vote.abstainVotes);
}
function _registerSpoke(uint16 _targetChain, bytes32 _spokeVoteAddress) internal {
Checkpoints.Trace256 storage registeredAddressCheckpoint = emitterRegistry[_targetChain];
emit SpokeRegistered(
_targetChain, bytes32(registeredAddressCheckpoint.upperLookup(block.timestamp)), _spokeVoteAddress
);
registeredAddressCheckpoint.push(block.timestamp, uint256(_spokeVoteAddress));
}
function _registerQueryType(address _implementation, uint8 _queryType) internal {
emit QueryTypeRegistered(_queryType, address(voteTypeDecoder[_queryType]), _implementation);
if (_implementation == address(0)) {
delete voteTypeDecoder[_queryType];
return;
}
bool _isValid = _implementation.supportsInterface(type(ISpokeVoteDecoder).interfaceId);
if (!_isValid) revert InvalidQueryVoteImpl();
voteTypeDecoder[_queryType] = ISpokeVoteDecoder(_implementation);
}
function _setGovernor(address _newGovernor) internal {
emit HubGovernorUpdated(address(hubGovernor), _newGovernor);
hubGovernor = IGovernor(_newGovernor);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165Checker.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;
/**
* @dev Returns true if `account` supports the {IERC165} interface.
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
!supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
}
/**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*/
function getSupportedInterfaces(
address account,
bytes4[] memory interfaceIds
) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
// prepare call
bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId));
// perform static call
bool success;
uint256 returnSize;
uint256 returnValue;
assembly {
success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
returnSize := returndatasize()
returnValue := mload(0x00)
}
return success && returnSize >= 0x20 && returnValue > 0;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/IGovernor.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../interfaces/IERC165.sol";
import {IERC6372} from "../interfaces/IERC6372.sol";
/**
* @dev Interface of the {Governor} core.
*/
interface IGovernor is IERC165, IERC6372 {
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Queued,
Expired,
Executed
}
/**
* @dev Empty proposal or a mismatch between the parameters length for a proposal call.
*/
error GovernorInvalidProposalLength(uint256 targets, uint256 calldatas, uint256 values);
/**
* @dev The vote was already cast.
*/
error GovernorAlreadyCastVote(address voter);
/**
* @dev Token deposits are disabled in this contract.
*/
error GovernorDisabledDeposit();
/**
* @dev The `account` is not a proposer.
*/
error GovernorOnlyProposer(address account);
/**
* @dev The `account` is not the governance executor.
*/
error GovernorOnlyExecutor(address account);
/**
* @dev The `proposalId` doesn't exist.
*/
error GovernorNonexistentProposal(uint256 proposalId);
/**
* @dev The current state of a proposal is not the required for performing an operation.
* The `expectedStates` is a bitmap with the bits enabled for each ProposalState enum position
* counting from right to left.
*
* NOTE: If `expectedState` is `bytes32(0)`, the proposal is expected to not be in any state (i.e. not exist).
* This is the case when a proposal that is expected to be unset is already initiated (the proposal is duplicated).
*
* See {Governor-_encodeStateBitmap}.
*/
error GovernorUnexpectedProposalState(uint256 proposalId, ProposalState current, bytes32 expectedStates);
/**
* @dev The voting period set is not a valid period.
*/
error GovernorInvalidVotingPeriod(uint256 votingPeriod);
/**
* @dev The `proposer` does not have the required votes to create a proposal.
*/
error GovernorInsufficientProposerVotes(address proposer, uint256 votes, uint256 threshold);
/**
* @dev The `proposer` is not allowed to create a proposal.
*/
error GovernorRestrictedProposer(address proposer);
/**
* @dev The vote type used is not valid for the corresponding counting module.
*/
error GovernorInvalidVoteType();
/**
* @dev Queue operation is not implemented for this governor. Execute should be called directly.
*/
error GovernorQueueNotImplemented();
/**
* @dev The proposal hasn't been queued yet.
*/
error GovernorNotQueuedProposal(uint256 proposalId);
/**
* @dev The proposal has already been queued.
*/
error GovernorAlreadyQueuedProposal(uint256 proposalId);
/**
* @dev The provided signature is not valid for the expected `voter`.
* If the `voter` is a contract, the signature is not valid using {IERC1271-isValidSignature}.
*/
error GovernorInvalidSignature(address voter);
/**
* @dev Emitted when a proposal is created.
*/
event ProposalCreated(
uint256 proposalId,
address proposer,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
uint256 voteStart,
uint256 voteEnd,
string description
);
/**
* @dev Emitted when a proposal is queued.
*/
event ProposalQueued(uint256 proposalId, uint256 etaSeconds);
/**
* @dev Emitted when a proposal is executed.
*/
event ProposalExecuted(uint256 proposalId);
/**
* @dev Emitted when a proposal is canceled.
*/
event ProposalCanceled(uint256 proposalId);
/**
* @dev Emitted when a vote is cast without params.
*
* Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
*/
event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
/**
* @dev Emitted when a vote is cast with params.
*
* Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
* `params` are additional encoded parameters. Their interpepretation also depends on the voting module used.
*/
event VoteCastWithParams(
address indexed voter,
uint256 proposalId,
uint8 support,
uint256 weight,
string reason,
bytes params
);
/**
* @notice module:core
* @dev Name of the governor instance (used in building the ERC712 domain separator).
*/
function name() external view returns (string memory);
/**
* @notice module:core
* @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1"
*/
function version() external view returns (string memory);
/**
* @notice module:voting
* @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to
* be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of
* key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`.
*
* There are 2 standard keys: `support` and `quorum`.
*
* - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`.
* - `quorum=bravo` means that only For votes are counted towards quorum.
* - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum.
*
* If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique
* name that describes the behavior. For example:
*
* - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain.
* - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote.
*
* NOTE: The string can be decoded by the standard
* https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`]
* JavaScript class.
*/
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() external view returns (string memory);
/**
* @notice module:core
* @dev Hashing function used to (re)build the proposal id from the proposal details..
*/
function hashProposal(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external pure returns (uint256);
/**
* @notice module:core
* @dev Current state of a proposal, following Compound's convention
*/
function state(uint256 proposalId) external view returns (ProposalState);
/**
* @notice module:core
* @dev The number of votes required in order for a voter to become a proposer.
*/
function proposalThreshold() external view returns (uint256);
/**
* @notice module:core
* @dev Timepoint used to retrieve user's votes and quorum. If using block number (as per Compound's Comp), the
* snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the
* following block.
*/
function proposalSnapshot(uint256 proposalId) external view returns (uint256);
/**
* @notice module:core
* @dev Timepoint at which votes close. If using block number, votes close at the end of this block, so it is
* possible to cast a vote during this block.
*/
function proposalDeadline(uint256 proposalId) external view returns (uint256);
/**
* @notice module:core
* @dev The account that created a proposal.
*/
function proposalProposer(uint256 proposalId) external view returns (address);
/**
* @notice module:core
* @dev The time when a queued proposal becomes executable ("ETA"). Unlike {proposalSnapshot} and
* {proposalDeadline}, this doesn't use the governor clock, and instead relies on the executor's clock which may be
* different. In most cases this will be a timestamp.
*/
function proposalEta(uint256 proposalId) external view returns (uint256);
/**
* @notice module:core
* @dev Whether a proposal needs to be queued before execution.
*/
function proposalNeedsQueuing(uint256 proposalId) external view returns (bool);
/**
* @notice module:user-config
* @dev Delay, between the proposal is created and the vote starts. The unit this duration is expressed in depends
* on the clock (see EIP-6372) this contract uses.
*
* This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a
* proposal starts.
*
* NOTE: While this interface returns a uint256, timepoints are stored as uint48 following the ERC-6372 clock type.
* Consequently this value must fit in a uint48 (when added to the current clock). See {IERC6372-clock}.
*/
function votingDelay() external view returns (uint256);
/**
* @notice module:user-config
* @dev Delay between the vote start and vote end. The unit this duration is expressed in depends on the clock
* (see EIP-6372) this contract uses.
*
* NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting
* duration compared to the voting delay.
*
* NOTE: This value is stored when the proposal is submitted so that possible changes to the value do not affect
* proposals that have already been submitted. The type used to save it is a uint32. Consequently, while this
* interface returns a uint256, the value it returns should fit in a uint32.
*/
function votingPeriod() external view returns (uint256);
/**
* @notice module:user-config
* @dev Minimum number of cast voted required for a proposal to be successful.
*
* NOTE: The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows to scale the
* quorum depending on values such as the totalSupply of a token at this timepoint (see {ERC20Votes}).
*/
function quorum(uint256 timepoint) external view returns (uint256);
/**
* @notice module:reputation
* @dev Voting power of an `account` at a specific `timepoint`.
*
* Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or
* multiple), {ERC20Votes} tokens.
*/
function getVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @notice module:reputation
* @dev Voting power of an `account` at a specific `timepoint` given additional encoded parameters.
*/
function getVotesWithParams(
address account,
uint256 timepoint,
bytes memory params
) external view returns (uint256);
/**
* @notice module:voting
* @dev Returns whether `account` has cast a vote on `proposalId`.
*/
function hasVoted(uint256 proposalId, address account) external view returns (bool);
/**
* @dev Create a new proposal. Vote start after a delay specified by {IGovernor-votingDelay} and lasts for a
* duration specified by {IGovernor-votingPeriod}.
*
* Emits a {ProposalCreated} event.
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) external returns (uint256 proposalId);
/**
* @dev Queue a proposal. Some governors require this step to be performed before execution can happen. If queuing
* is not necessary, this function may revert.
* Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached.
*
* Emits a {ProposalQueued} event.
*/
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256 proposalId);
/**
* @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the
* deadline to be reached. Depending on the governor it might also be required that the proposal was queued and
* that some delay passed.
*
* Emits a {ProposalExecuted} event.
*
* NOTE: Some modules can modify the requirements for execution, for example by adding an additional timelock.
*/
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external payable returns (uint256 proposalId);
/**
* @dev Cancel a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e.
* before the vote starts.
*
* Emits a {ProposalCanceled} event.
*/
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256 proposalId);
/**
* @dev Cast a vote
*
* Emits a {VoteCast} event.
*/
function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);
/**
* @dev Cast a vote with a reason
*
* Emits a {VoteCast} event.
*/
function castVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) external returns (uint256 balance);
/**
* @dev Cast a vote with a reason and additional encoded parameters
*
* Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
*/
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) external returns (uint256 balance);
/**
* @dev Cast a vote using the voter's signature, including ERC-1271 signature support.
*
* Emits a {VoteCast} event.
*/
function castVoteBySig(
uint256 proposalId,
uint8 support,
address voter,
bytes memory signature
) external returns (uint256 balance);
/**
* @dev Cast a vote with a reason and additional encoded parameters using the voter's signature,
* including ERC-1271 signature support.
*
* Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
*/
function castVoteWithReasonAndParamsBySig(
uint256 proposalId,
uint8 support,
address voter,
string calldata reason,
bytes memory params,
bytes memory signature
) external returns (uint256 balance);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.23;
import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {
QueryResponse,
ParsedPerChainQueryResponse,
EthCallData,
EthCallWithFinalityQueryResponse,
InvalidContractAddress,
InvalidFunctionSignature
} from "wormhole-sdk/QueryResponse.sol";
import {fromWormholeFormat} from "wormhole-sdk/Utils.sol";
import {HubVotePool} from "src/HubVotePool.sol";
import {ISpokeVoteDecoder} from "src/interfaces/ISpokeVoteDecoder.sol";
import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol";
/// @title HubEvmSpokeVoteDecoder
/// @author [ScopeLift](https://scopelift.co)
/// @notice A contract that parses a specific wormhole query type from the `SpokeVoteAggregator`.
contract HubEvmSpokeVoteDecoder is ISpokeVoteDecoder, QueryResponse, ERC165 {
using BytesParsing for bytes;
/// @notice The hub vote pool used to validate message emitter.
HubVotePool public immutable HUB_VOTE_POOL;
/// @notice The expected finality for an EVM query.
bytes9 constant REQUEST_FINALITY = bytes9("finalized");
/// @param _core The Wormhole core contract for the hub chain.
/// @param _hubVotePool The address for the hub vote pool.
constructor(address _core, address _hubVotePool) QueryResponse(_core) {
HUB_VOTE_POOL = HubVotePool(_hubVotePool);
}
/// @notice Decodes a parsed per chain query respone for an eth call with finality query containing a spoke vote.
/// @param _perChainResp The parsed per chain response.
/// @param _governor The governor used to fetch a registered spoke.
/// @return The parsed query vote.
function decode(ParsedPerChainQueryResponse memory _perChainResp, IGovernor _governor)
external
view
returns (QueryVote memory)
{
EthCallWithFinalityQueryResponse memory _ethCalls = parseEthCallWithFinalityQueryResponse(_perChainResp);
// verify contract and chain is correct
if (_ethCalls.result.length != 1) revert TooManyEthCallResults(_ethCalls.result.length);
_validateEthCallData(_ethCalls.result[0]);
_ethCalls.requestFinality.checkLength(9);
if (bytes9(_ethCalls.requestFinality) != REQUEST_FINALITY) revert InvalidQueryBlock(_ethCalls.requestBlockId);
_ethCalls.result[0].result.checkLength(128);
(uint256 _proposalId, uint256 _againstVotes, uint256 _forVotes, uint256 _abstainVotes) =
abi.decode(_ethCalls.result[0].result, (uint256, uint256, uint256, uint256));
uint256 _voteStart = _governor.proposalSnapshot(_proposalId);
bytes32 _registeredAddress = HUB_VOTE_POOL.getSpoke(_perChainResp.chainId, _voteStart);
if (
_registeredAddress == bytes32("") || _ethCalls.result[0].contractAddress != fromWormholeFormat(_registeredAddress)
) revert InvalidContractAddress();
bytes32 _spokeProposalId = keccak256(abi.encode(_perChainResp.chainId, _proposalId));
return (
QueryVote({
proposalId: _proposalId,
spokeProposalId: _spokeProposalId,
proposalVote: ProposalVote(_againstVotes, _forVotes, _abstainVotes),
chainId: _perChainResp.chainId
})
);
}
/// @notice An ERC165 compatible method that validates the various interfaces this contract supports.
/// @param _interfaceId The id of the interface that is checked.
/// @return Whether the interface id is supported.
function supportsInterface(bytes4 _interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return _interfaceId == type(ISpokeVoteDecoder).interfaceId || ERC165.supportsInterface(_interfaceId);
}
/// @notice Validate the query eth calldata was from the expected spoke contract and contains the expected function
/// signature.
/// @param _r The Eth calldata of the query.
function _validateEthCallData(EthCallData memory _r) internal pure {
(bytes4 funcSig,) = _r.callData.asBytes4Unchecked(0);
// The function signature should be bytes4(keccak256(bytes("proposalVotes(uint256)")))
if (funcSig != bytes4(hex"544ffc9c")) revert InvalidFunctionSignature();
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// contracts/Messages.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
interface IWormhole {
struct GuardianSet {
address[] keys;
uint32 expirationTime;
}
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;
}
struct ContractUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
address newContract;
}
struct GuardianSetUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
GuardianSet newGuardianSet;
uint32 newGuardianSetIndex;
}
struct SetMessageFee {
bytes32 module;
uint8 action;
uint16 chain;
uint256 messageFee;
}
struct TransferFees {
bytes32 module;
uint8 action;
uint16 chain;
uint256 amount;
bytes32 recipient;
}
struct RecoverChainId {
bytes32 module;
uint8 action;
uint256 evmChainId;
uint16 newChainId;
}
event LogMessagePublished(
address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel
);
event ContractUpgraded(address indexed oldContract, address indexed newContract);
event GuardianSetAdded(uint32 indexed index);
function publishMessage(uint32 nonce, bytes memory payload, uint8 consistencyLevel)
external
payable
returns (uint64 sequence);
function initialize() external;
function parseAndVerifyVM(bytes calldata encodedVM)
external
view
returns (VM memory vm, bool valid, string memory reason);
function verifyVM(VM memory vm) external view returns (bool valid, string memory reason);
function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet)
external
pure
returns (bool valid, string memory reason);
function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);
function quorum(uint256 numGuardians) external pure returns (uint256 numSignaturesRequiredForQuorum);
function getGuardianSet(uint32 index) external view returns (GuardianSet memory);
function getCurrentGuardianSetIndex() external view returns (uint32);
function getGuardianSetExpiry() external view returns (uint32);
function governanceActionIsConsumed(bytes32 hash) external view returns (bool);
function isInitialized(address impl) external view returns (bool);
function chainId() external view returns (uint16);
function isFork() external view returns (bool);
function governanceChainId() external view returns (uint16);
function governanceContract() external view returns (bytes32);
function messageFee() external view returns (uint256);
function evmChainId() external view returns (uint256);
function nextSequence(address emitter) external view returns (uint64);
function parseContractUpgrade(bytes memory encodedUpgrade) external pure returns (ContractUpgrade memory cu);
function parseGuardianSetUpgrade(bytes memory encodedUpgrade)
external
pure
returns (GuardianSetUpgrade memory gsu);
function parseSetMessageFee(bytes memory encodedSetMessageFee) external pure returns (SetMessageFee memory smf);
function parseTransferFees(bytes memory encodedTransferFees) external pure returns (TransferFees memory tf);
function parseRecoverChainId(bytes memory encodedRecoverChainId)
external
pure
returns (RecoverChainId memory rci);
function submitContractUpgrade(bytes memory _vm) external;
function submitSetMessageFee(bytes memory _vm) external;
function submitNewGuardianSet(bytes memory _vm) external;
function submitTransferFees(bytes memory _vm) external;
function submitRecoverChainId(bytes memory _vm) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.4;
import {BytesParsing} from "./libraries/BytesParsing.sol";
import "./interfaces/IWormhole.sol";
// @dev ParsedQueryResponse is returned by QueryResponse.parseAndVerifyQueryResponse().
struct ParsedQueryResponse {
uint8 version;
uint16 senderChainId;
uint32 nonce;
bytes requestId; // 65 byte sig for off-chain, 32 byte vaaHash for on-chain
ParsedPerChainQueryResponse [] responses;
}
// @dev ParsedPerChainQueryResponse describes a single per-chain response.
struct ParsedPerChainQueryResponse {
uint16 chainId;
uint8 queryType;
bytes request;
bytes response;
}
// @dev EthCallQueryResponse describes the response to an ETH call per-chain query.
struct EthCallQueryResponse {
bytes requestBlockId;
uint64 blockNum;
uint64 blockTime;
bytes32 blockHash;
EthCallData [] result;
}
// @dev EthCallByTimestampQueryResponse describes the response to an ETH call by timestamp per-chain query.
struct EthCallByTimestampQueryResponse {
bytes requestTargetBlockIdHint;
bytes requestFollowingBlockIdHint;
uint64 requestTargetTimestamp;
uint64 targetBlockNum;
uint64 targetBlockTime;
uint64 followingBlockNum;
bytes32 targetBlockHash;
bytes32 followingBlockHash;
uint64 followingBlockTime;
EthCallData [] result;
}
// @dev EthCallWithFinalityQueryResponse describes the response to an ETH call with finality per-chain query.
struct EthCallWithFinalityQueryResponse {
bytes requestBlockId;
bytes requestFinality;
uint64 blockNum;
uint64 blockTime;
bytes32 blockHash;
EthCallData [] result;
}
// @dev EthCallData describes a single ETH call query / response pair.
struct EthCallData {
address contractAddress;
bytes callData;
bytes result;
}
// @dev SolanaAccountQueryResponse describes the response to a Solana Account query per-chain query.
struct SolanaAccountQueryResponse {
bytes requestCommitment;
uint64 requestMinContextSlot;
uint64 requestDataSliceOffset;
uint64 requestDataSliceLength;
uint64 slotNumber;
uint64 blockTime;
bytes32 blockHash;
SolanaAccountResult [] results;
}
// @dev SolanaAccountResult describes a single Solana Account query result.
struct SolanaAccountResult {
bytes32 account;
uint64 lamports;
uint64 rentEpoch;
bool executable;
bytes32 owner;
bytes data;
}
// @dev SolanaPdaQueryResponse describes the response to a Solana PDA (Program Derived Address) query per-chain query.
struct SolanaPdaQueryResponse {
bytes requestCommitment;
uint64 requestMinContextSlot;
uint64 requestDataSliceOffset;
uint64 requestDataSliceLength;
uint64 slotNumber;
uint64 blockTime;
bytes32 blockHash;
SolanaPdaResult [] results;
}
// @dev SolanaPdaResult describes a single Solana PDA (Program Derived Address) query result.
struct SolanaPdaResult {
bytes32 programId;
bytes[] seeds;
bytes32 account;
uint64 lamports;
uint64 rentEpoch;
bool executable;
bytes32 owner;
bytes data;
uint8 bump;
}
// Custom errors
error EmptyWormholeAddress();
error InvalidResponseVersion();
error VersionMismatch();
error ZeroQueries();
error NumberOfResponsesMismatch();
error ChainIdMismatch();
error RequestTypeMismatch();
error UnsupportedQueryType(uint8 received);
error WrongQueryType(uint8 received, uint8 expected);
error UnexpectedNumberOfResults();
error InvalidPayloadLength(uint256 received, uint256 expected);
error InvalidContractAddress();
error InvalidFunctionSignature();
error InvalidChainId();
error StaleBlockNum();
error StaleBlockTime();
// @dev QueryResponse is a library that implements the parsing and verification of Cross Chain Query (CCQ) responses.
// For a detailed discussion of these query responses, please see the white paper:
// https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0013_ccq.md
abstract contract QueryResponse {
using BytesParsing for bytes;
IWormhole public immutable wormhole;
bytes public constant responsePrefix = bytes("query_response_0000000000000000000|");
uint8 public constant VERSION = 1;
// TODO: Consider changing these to an enum.
uint8 public constant QT_ETH_CALL = 1;
uint8 public constant QT_ETH_CALL_BY_TIMESTAMP = 2;
uint8 public constant QT_ETH_CALL_WITH_FINALITY = 3;
uint8 public constant QT_SOL_ACCOUNT = 4;
uint8 public constant QT_SOL_PDA = 5;
uint8 public constant QT_MAX = 6; // Keep this last
constructor(address _wormhole) {
if (_wormhole == address(0)) {
revert EmptyWormholeAddress();
}
wormhole = IWormhole(_wormhole);
}
/// @dev getResponseHash computes the hash of the specified query response.
function getResponseHash(bytes memory response) public pure returns (bytes32) {
return keccak256(response);
}
/// @dev getResponseDigest computes the digest of the specified query response.
function getResponseDigest(bytes memory response) public pure returns (bytes32) {
return keccak256(abi.encodePacked(responsePrefix,getResponseHash(response)));
}
/// @dev parseAndVerifyQueryResponse verifies the query response and returns the parsed response.
function parseAndVerifyQueryResponse(bytes memory response, IWormhole.Signature[] memory signatures) public view returns (ParsedQueryResponse memory r) {
verifyQueryResponseSignatures(response, signatures);
uint index;
(r.version, index) = response.asUint8Unchecked(index);
if (r.version != VERSION) {
revert InvalidResponseVersion();
}
(r.senderChainId, index) = response.asUint16Unchecked(index);
// For off chain requests (chainID zero), the requestId is the 65 byte signature. For on chain requests, it is the 32 byte VAA hash.
if (r.senderChainId == 0) {
(r.requestId, index) = response.sliceUnchecked(index, 65);
} else {
(r.requestId, index) = response.sliceUnchecked(index, 32);
}
uint32 len;
(len, index) = response.asUint32Unchecked(index); // query_request_len
uint reqIdx = index;
// Scope to avoid stack-too-deep error
{
uint8 version;
(version, reqIdx) = response.asUint8Unchecked(reqIdx);
if (version != r.version) {
revert VersionMismatch();
}
}
(r.nonce, reqIdx) = response.asUint32Unchecked(reqIdx);
uint8 numPerChainQueries;
(numPerChainQueries, reqIdx) = response.asUint8Unchecked(reqIdx);
// A valid query request has at least one per chain query
if (numPerChainQueries == 0) {
revert ZeroQueries();
}
// The response starts after the request.
uint respIdx = index + len;
uint startOfResponse = respIdx;
uint8 respNumPerChainQueries;
(respNumPerChainQueries, respIdx) = response.asUint8Unchecked(respIdx);
if (respNumPerChainQueries != numPerChainQueries) {
revert NumberOfResponsesMismatch();
}
r.responses = new ParsedPerChainQueryResponse[](numPerChainQueries);
// Walk through the requests and responses in lock step.
for (uint idx; idx < numPerChainQueries;) {
(r.responses[idx].chainId, reqIdx) = response.asUint16Unchecked(reqIdx);
uint16 respChainId;
(respChainId, respIdx) = response.asUint16Unchecked(respIdx);
if (respChainId != r.responses[idx].chainId) {
revert ChainIdMismatch();
}
(r.responses[idx].queryType, reqIdx) = response.asUint8Unchecked(reqIdx);
uint8 respQueryType;
(respQueryType, respIdx) = response.asUint8Unchecked(respIdx);
if (respQueryType != r.responses[idx].queryType) {
revert RequestTypeMismatch();
}
if (r.responses[idx].queryType < QT_ETH_CALL || r.responses[idx].queryType >= QT_MAX) {
revert UnsupportedQueryType(r.responses[idx].queryType);
}
(len, reqIdx) = response.asUint32Unchecked(reqIdx);
(r.responses[idx].request, reqIdx) = response.sliceUnchecked(reqIdx, len);
(len, respIdx) = response.asUint32Unchecked(respIdx);
(r.responses[idx].response, respIdx) = response.sliceUnchecked(respIdx, len);
unchecked { ++idx; }
}
// End of request body should align with start of response body
if (startOfResponse != reqIdx) {
revert InvalidPayloadLength(startOfResponse, reqIdx);
}
checkLength(response, respIdx);
return r;
}
/// @dev parseEthCallQueryResponse parses a ParsedPerChainQueryResponse for an ETH call per-chain query.
function parseEthCallQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (EthCallQueryResponse memory r) {
if (pcr.queryType != QT_ETH_CALL) {
revert WrongQueryType(pcr.queryType, QT_ETH_CALL);
}
uint reqIdx;
uint respIdx;
uint32 len;
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // block_id_len
(r.requestBlockId, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len);
uint8 numBatchCallData;
(numBatchCallData, reqIdx) = pcr.request.asUint8Unchecked(reqIdx);
(r.blockNum, respIdx) = pcr.response.asUint64Unchecked(respIdx);
(r.blockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx);
(r.blockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx);
uint8 respNumResults;
(respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx);
if (respNumResults != numBatchCallData) {
revert UnexpectedNumberOfResults();
}
r.result = new EthCallData[](numBatchCallData);
// Walk through the call data and results in lock step.
for (uint idx; idx < numBatchCallData;) {
(r.result[idx].contractAddress, reqIdx) = pcr.request.asAddressUnchecked(reqIdx);
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // call_data_len
(r.result[idx].callData, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len);
(len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len
(r.result[idx].result, respIdx) = pcr.response.sliceUnchecked(respIdx, len);
unchecked { ++idx; }
}
checkLength(pcr.request, reqIdx);
checkLength(pcr.response, respIdx);
return r;
}
/// @dev parseEthCallByTimestampQueryResponse parses a ParsedPerChainQueryResponse for an ETH call per-chain query.
function parseEthCallByTimestampQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (EthCallByTimestampQueryResponse memory r) {
if (pcr.queryType != QT_ETH_CALL_BY_TIMESTAMP) {
revert WrongQueryType(pcr.queryType, QT_ETH_CALL_BY_TIMESTAMP);
}
uint reqIdx;
uint respIdx;
uint32 len;
(r.requestTargetTimestamp, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request target_time_us
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request target_block_id_hint_len
(r.requestTargetBlockIdHint, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request target_block_id_hint
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // following_block_id_hint_len
(r.requestFollowingBlockIdHint, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request following_block_id_hint
uint8 numBatchCallData;
(numBatchCallData, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); // Request num_batch_call_data
(r.targetBlockNum, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response target_block_number
(r.targetBlockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response target_block_hash
(r.targetBlockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response target_block_time_us
(r.followingBlockNum, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response following_block_number
(r.followingBlockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response following_block_hash
(r.followingBlockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response following_block_time_us
uint8 respNumResults;
(respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response num_results
if (respNumResults != numBatchCallData) {
revert UnexpectedNumberOfResults();
}
r.result = new EthCallData[](numBatchCallData);
// Walk through the call data and results in lock step.
for (uint idx; idx < numBatchCallData;) {
(r.result[idx].contractAddress, reqIdx) = pcr.request.asAddressUnchecked(reqIdx);
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // call_data_len
(r.result[idx].callData, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len);
(len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len
(r.result[idx].result, respIdx) = pcr.response.sliceUnchecked(respIdx, len);
unchecked { ++idx; }
}
checkLength(pcr.request, reqIdx);
checkLength(pcr.response, respIdx);
}
/// @dev parseEthCallWithFinalityQueryResponse parses a ParsedPerChainQueryResponse for an ETH call per-chain query.
function parseEthCallWithFinalityQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (EthCallWithFinalityQueryResponse memory r) {
if (pcr.queryType != QT_ETH_CALL_WITH_FINALITY) {
revert WrongQueryType(pcr.queryType, QT_ETH_CALL_WITH_FINALITY);
}
uint reqIdx;
uint respIdx;
uint32 len;
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request block_id_len
(r.requestBlockId, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request block_id
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request finality_len
(r.requestFinality, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request finality
uint8 numBatchCallData;
(numBatchCallData, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); // Request num_batch_call_data
(r.blockNum, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response block_number
(r.blockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response block_hash
(r.blockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response block_time_us
uint8 respNumResults;
(respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response num_results
if (respNumResults != numBatchCallData) {
revert UnexpectedNumberOfResults();
}
r.result = new EthCallData[](numBatchCallData);
// Walk through the call data and results in lock step.
for (uint idx; idx < numBatchCallData;) {
(r.result[idx].contractAddress, reqIdx) = pcr.request.asAddressUnchecked(reqIdx);
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // call_data_len
(r.result[idx].callData, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len);
(len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len
(r.result[idx].result, respIdx) = pcr.response.sliceUnchecked(respIdx, len);
unchecked { ++idx; }
}
checkLength(pcr.request, reqIdx);
checkLength(pcr.response, respIdx);
}
/// @dev parseSolanaAccountQueryResponse parses a ParsedPerChainQueryResponse for a Solana Account per-chain query.
function parseSolanaAccountQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (SolanaAccountQueryResponse memory r) {
if (pcr.queryType != QT_SOL_ACCOUNT) {
revert WrongQueryType(pcr.queryType, QT_SOL_ACCOUNT);
}
uint reqIdx;
uint respIdx;
uint32 len;
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request commitment_len
(r.requestCommitment, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request commitment
(r.requestMinContextSlot, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request min_context_slot
(r.requestDataSliceOffset, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request data_slice_offset
(r.requestDataSliceLength, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request data_slice_length
uint8 numAccounts;
(numAccounts, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); // Request num_accounts
(r.slotNumber, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response slot_number
(r.blockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response block_time_us
(r.blockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response block_hash
uint8 respNumResults;
(respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response num_results
if (respNumResults != numAccounts) {
revert UnexpectedNumberOfResults();
}
r.results = new SolanaAccountResult[](numAccounts);
// Walk through the call data and results in lock step.
for (uint idx; idx < numAccounts;) {
(r.results[idx].account, reqIdx) = pcr.request.asBytes32Unchecked(reqIdx); // Request account
(r.results[idx].lamports, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response lamports
(r.results[idx].rentEpoch, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response rent_epoch
(r.results[idx].executable, respIdx) = pcr.response.asBoolUnchecked(respIdx); // Response executable
(r.results[idx].owner, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response owner
(len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len
(r.results[idx].data, respIdx) = pcr.response.sliceUnchecked(respIdx, len);
unchecked { ++idx; }
}
checkLength(pcr.request, reqIdx);
checkLength(pcr.response, respIdx);
}
/// @dev parseSolanaPdaQueryResponse parses a ParsedPerChainQueryResponse for a Solana Pda per-chain query.
function parseSolanaPdaQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (SolanaPdaQueryResponse memory r) {
if (pcr.queryType != QT_SOL_PDA) {
revert WrongQueryType(pcr.queryType, QT_SOL_PDA);
}
uint reqIdx;
uint respIdx;
uint32 len;
(len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request commitment_len
(r.requestCommitment, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request commitment
(r.requestMinContextSlot, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request min_context_slot
(r.requestDataSliceOffset, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request data_slice_offset
(r.requestDataSliceLength, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request data_slice_length
uint8 numPdas;
(numPdas, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); // Request num_Pdas
(r.slotNumber, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response slot_number
(r.blockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response block_time_us
(r.blockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response block_hash
uint8 respNumResults;
(respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response num_results
if (respNumResults != numPdas) {
revert UnexpectedNumberOfResults();
}
r.results = new SolanaPdaResult[](numPdas);
// Walk through the call data and results in lock step.
for (uint idx; idx < numPdas;) {
(r.results[idx].programId, reqIdx) = pcr.request.asBytes32Unchecked(reqIdx); // Request programId
uint8 numSeeds; // Request number of seeds
(numSeeds, reqIdx) = pcr.request.asUint8Unchecked(reqIdx);
r.results[idx].seeds = new bytes[](numSeeds);
for (uint idx2; idx2 < numSeeds;) {
uint32 seedLen;
(seedLen, reqIdx) = pcr.request.asUint32Unchecked(reqIdx);
(r.results[idx].seeds[idx2], reqIdx) = pcr.request.sliceUnchecked(reqIdx, seedLen);
unchecked { ++idx2; }
}
(r.results[idx].account, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response account
(r.results[idx].bump, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response bump
(r.results[idx].lamports, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response lamports
(r.results[idx].rentEpoch, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response rent_epoch
(r.results[idx].executable, respIdx) = pcr.response.asBoolUnchecked(respIdx); // Response executable
(r.results[idx].owner, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response owner
(len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len
(r.results[idx].data, respIdx) = pcr.response.sliceUnchecked(respIdx, len);
unchecked { ++idx; }
}
checkLength(pcr.request, reqIdx);
checkLength(pcr.response, respIdx);
}
/// @dev validateBlockTime validates that the parsed block time isn't stale
/// @param _blockTime Wormhole block time in MICROseconds
/// @param _minBlockTime Minium block time in seconds
function validateBlockTime(uint64 _blockTime, uint256 _minBlockTime) public pure {
uint256 blockTimeInSeconds = _blockTime / 1_000_000; // Rounds down
if (blockTimeInSeconds < _minBlockTime) {
revert StaleBlockTime();
}
}
/// @dev validateBlockNum validates that the parsed blockNum isn't stale
function validateBlockNum(uint64 _blockNum, uint256 _minBlockNum) public pure {
if (_blockNum < _minBlockNum) {
revert StaleBlockNum();
}
}
/// @dev validateChainId validates that the parsed chainId is one of an array of chainIds we expect
function validateChainId(uint16 chainId, uint16[] memory _validChainIds) public pure {
bool validChainId = false;
uint256 numChainIds = _validChainIds.length;
for (uint256 idx; idx < numChainIds;) {
if (chainId == _validChainIds[idx]) {
validChainId = true;
break;
}
unchecked { ++idx; }
}
if (!validChainId) revert InvalidChainId();
}
/// @dev validateMutlipleEthCallData validates that each EthCallData in an array comes from a function signature and contract address we expect
function validateMultipleEthCallData(EthCallData[] memory r, address[] memory _expectedContractAddresses, bytes4[] memory _expectedFunctionSignatures) public pure {
uint256 callDatasLength = r.length;
for (uint256 idx; idx < callDatasLength;) {
validateEthCallData(r[idx], _expectedContractAddresses, _expectedFunctionSignatures);
unchecked { ++idx; }
}
}
/// @dev validateEthCallData validates that EthCallData comes from a function signature and contract address we expect
/// @dev An empty array means we accept all addresses/function signatures
/// @dev Example 1: To accept signatures 0xaaaaaaaa and 0xbbbbbbbb from `address(abcd)` you'd pass in [0xaaaaaaaa, 0xbbbbbbbb], [address(abcd)]
/// @dev Example 2: To accept any function signatures from `address(abcd)` or `address(efab)` you'd pass in [], [address(abcd), address(efab)]
/// @dev Example 3: To accept function signature 0xaaaaaaaa from any address you'd pass in [0xaaaaaaaa], []
/// @dev WARNING Example 4: If you want to accept signature 0xaaaaaaaa from `address(abcd)` and signature 0xbbbbbbbb from `address(efab)` the following input would be incorrect:
/// @dev [0xaaaaaaaa, 0xbbbbbbbb], [address(abcd), address(efab)]
/// @dev This would accept both 0xaaaaaaaa and 0xbbbbbbbb from `address(abcd)` AND `address(efab)`. Instead you should make 2 calls to this method
/// @dev using the pattern in Example 1. [0xaaaaaaaa], [address(abcd)] OR [0xbbbbbbbb], [address(efab)]
function validateEthCallData(EthCallData memory r, address[] memory _expectedContractAddresses, bytes4[] memory _expectedFunctionSignatures) public pure {
bool validContractAddress = _expectedContractAddresses.length == 0 ? true : false;
bool validFunctionSignature = _expectedFunctionSignatures.length == 0 ? true : false;
uint256 contractAddressesLength = _expectedContractAddresses.length;
// Check that the contract address called in the request is expected
for (uint256 idx; idx < contractAddressesLength;) {
if (r.contractAddress == _expectedContractAddresses[idx]) {
validContractAddress = true;
break;
}
unchecked { ++idx; }
}
// Early exit to save gas
if (!validContractAddress) {
revert InvalidContractAddress();
}
uint256 functionSignaturesLength = _expectedFunctionSignatures.length;
// Check that the function signature called is expected
for (uint256 idx; idx < functionSignaturesLength;) {
(bytes4 funcSig,) = r.callData.asBytes4Unchecked(0);
if (funcSig == _expectedFunctionSignatures[idx]) {
validFunctionSignature = true;
break;
}
unchecked { ++idx; }
}
if (!validFunctionSignature) {
revert InvalidFunctionSignature();
}
}
/**
* @dev verifyQueryResponseSignatures verifies the signatures on a query response. It calls into the Wormhole contract.
* IWormhole.Signature expects the last byte to be bumped by 27
* see https://github.com/wormhole-foundation/wormhole/blob/637b1ee657de7de05f783cbb2078dd7d8bfda4d0/ethereum/contracts/Messages.sol#L174
*/
function verifyQueryResponseSignatures(bytes memory response, IWormhole.Signature[] memory signatures) public view {
// It might be worth adding a verifyCurrentQuorum call on the core bridge so that there is only 1 cross call instead of 4.
uint32 gsi = wormhole.getCurrentGuardianSetIndex();
IWormhole.GuardianSet memory guardianSet = wormhole.getGuardianSet(gsi);
bytes32 responseHash = getResponseDigest(response);
/**
* @dev Checks whether the guardianSet has zero keys
* WARNING: This keys check is critical to ensure the guardianSet has keys present AND to ensure
* that guardianSet key size doesn't fall to zero and negatively impact quorum assessment. If guardianSet
* key length is 0 and vm.signatures length is 0, this could compromise the integrity of both vm and
* signature verification.
*/
if(guardianSet.keys.length == 0){
revert("invalid guardian set");
}
/**
* @dev We're using a fixed point number transformation with 1 decimal to deal with rounding.
* WARNING: This quorum check is critical to assessing whether we have enough Guardian signatures to validate a VM
* if making any changes to this, obtain additional peer review. If guardianSet key length is 0 and
* vm.signatures length is 0, this could compromise the integrity of both vm and signature verification.
*/
if (signatures.length < wormhole.quorum(guardianSet.keys.length)){
revert("no quorum");
}
/// @dev Verify the proposed vm.signatures against the guardianSet
(bool signaturesValid, string memory invalidReason) = wormhole.verifySignatures(responseHash, signatures, guardianSet);
if(!signaturesValid){
revert(invalidReason);
}
/// If we are here, we've validated the VM is a valid multi-sig that matches the current guardianSet.
}
/// @dev checkLength verifies that the message was fully consumed.
function checkLength(bytes memory encoded, uint256 expected) private pure {
if (encoded.length != expected) {
revert InvalidPayloadLength(encoded.length, expected);
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.23;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/// @dev This library was modified from Openzeppelin's Trace208 checkpoint library:
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/01ef448981be9d20ca85f2faf6ebdf591ce409f3/contracts/utils/structs/Checkpoints.sol#L22.
///
/// We removed the use of _unsafe access as the struct is now 2 slots.
library Checkpoints {
/**
* @dev A value was attempted to be inserted on a past checkpoint.
*/
error CheckpointUnorderedInsertion();
struct Trace256 {
Checkpoint256[] _checkpoints;
}
struct Checkpoint256 {
uint256 _key;
uint256 _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a Trace256 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint256).max` key set will disable the
* library.
*/
function push(Trace256 storage self, uint256 key, uint256 value) internal returns (uint256, uint256) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(Trace256 storage self, uint256 key) internal view returns (uint256) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? 0 : self._checkpoints[pos - 1]._value;
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(Checkpoint256[] storage self, uint256 key, uint256 value) private returns (uint256, uint256) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
Checkpoint256 memory last = self[pos - 1];
// Checkpoint keys must be non-decreasing.
if (last._key > key) revert CheckpointUnorderedInsertion();
// Update or push new checkpoint
if (last._key == key) self[pos - 1]._value = value;
else self.push(Checkpoint256({_key: key, _value: value}));
return (last._value, value);
} else {
self.push(Checkpoint256({_key: key, _value: value}));
return (0, value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(Checkpoint256[] storage self, uint256 key, uint256 low, uint256 high)
private
view
returns (uint256)
{
while (low < high) {
uint256 mid = Math.average(low, high);
if (self[mid]._key > key) high = mid;
else low = mid + 1;
}
return high;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.23;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";
import {ParsedPerChainQueryResponse} from "wormhole-sdk/QueryResponse.sol";
interface ISpokeVoteDecoder is IERC165 {
struct ProposalVote {
uint256 againstVotes;
uint256 forVotes;
uint256 abstainVotes;
}
struct QueryVote {
uint256 proposalId;
bytes32 spokeProposalId;
ProposalVote proposalVote;
uint16 chainId;
}
error TooManyEthCallResults(uint256);
error InvalidProposalVote();
error InvalidQueryBlock(bytes);
function decode(ParsedPerChainQueryResponse memory _queryResponse, IGovernor _governor)
external
view
returns (QueryVote memory);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @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.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @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
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
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
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
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
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
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
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
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
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
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
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
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
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
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
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
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
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
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
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
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
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
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
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
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
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
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
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
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
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
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
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
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
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
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
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
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
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
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
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
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
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
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
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
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
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
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
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
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
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
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
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
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
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
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
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
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
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
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
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
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
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
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
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @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
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @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
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @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
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @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
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @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
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @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
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @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
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @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
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @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
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @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
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @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
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @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
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @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
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @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
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @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
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @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
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @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
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @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
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @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
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @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
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @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
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @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
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @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
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @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
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @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
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @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
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @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
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @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
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @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
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @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
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @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);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol"; <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC6372.sol)
pragma solidity ^0.8.20;
interface IERC6372 {
/**
* @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
*/
function clock() external view returns (uint48);
/**
* @dev Description of the clock
*/
// solhint-disable-next-line func-name-mixedcase
function CLOCK_MODE() external view returns (string memory);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./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);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.13;
import "./interfaces/IWormholeRelayer.sol";
function toWormholeFormat(address addr) pure returns (bytes32) {
return bytes32(uint256(uint160(addr)));
}
function fromWormholeFormat(bytes32 whFormatAddress) pure returns (address) {
if (uint256(whFormatAddress) >> 160 != 0) {
revert NotAnEvmAddress(whFormatAddress);
}
return address(uint160(uint256(whFormatAddress)));
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.13;
library BytesParsing {
uint256 private constant freeMemoryPtr = 0x40;
uint256 private constant wordSize = 32;
error OutOfBounds(uint256 offset, uint256 length);
error LengthMismatch(uint256 encodedLength, uint256 expectedLength);
error InvalidBoolVal(uint8 val);
function checkBound(uint offset, uint length) internal pure {
if (offset > length)
revert OutOfBounds(offset, length);
}
function checkLength(bytes memory encoded, uint256 expected) internal pure {
if (encoded.length != expected)
revert LengthMismatch(encoded.length, expected);
}
function sliceUnchecked(
bytes memory encoded,
uint offset,
uint length
) internal pure returns (bytes memory ret, uint nextOffset) {
//bail early for degenerate case
if (length == 0)
return (new bytes(0), offset);
assembly ("memory-safe") {
nextOffset := add(offset, length)
ret := mload(freeMemoryPtr)
//Explanation on how we copy data here:
// The bytes type has the following layout in memory:
// [length: 32 bytes, data: length bytes]
// So if we allocate `bytes memory foo = new bytes(1);` then `foo` will be a pointer to 33
// bytes where the first 32 bytes contain the length and the last byte is the actual data.
// Since mload always loads 32 bytes of memory at once, we use our shift variable to align
// our reads so that our last read lines up exactly with the last 32 bytes of `encoded`.
// However this also means that if the length of `encoded` is not a multiple of 32 bytes, our
// first read will necessarily partly contain bytes from `encoded`'s 32 length bytes that
// will be written into the length part of our `ret` slice.
// We remedy this issue by writing the length of our `ret` slice at the end, thus
// overwritting those garbage bytes.
let shift := and(length, 31) //equivalent to `mod(length, 32)` but 2 gas cheaper
if iszero(shift) {
shift := wordSize
}
let dest := add(ret, shift)
let end := add(dest, length)
for {
let src := add(add(encoded, shift), offset)
} lt(dest, end) {
src := add(src, wordSize)
dest := add(dest, wordSize)
} {
mstore(dest, mload(src))
}
mstore(ret, length)
//When compiling with --via-ir then normally allocated memory (i.e. via new) will have 32 byte
// memory alignment and so we enforce the same memory alignment here.
mstore(freeMemoryPtr, and(add(dest, 31), not(31)))
}
}
function slice(
bytes memory encoded,
uint offset,
uint length
) internal pure returns (bytes memory ret, uint nextOffset) {
(ret, nextOffset) = sliceUnchecked(encoded, offset, length);
checkBound(nextOffset, encoded.length);
}
function asAddressUnchecked(
bytes memory encoded,
uint offset
) internal pure returns (address, uint) {
(uint160 ret, uint nextOffset) = asUint160Unchecked(encoded, offset);
return (address(ret), nextOffset);
}
function asAddress(
bytes memory encoded,
uint offset
) internal pure returns (address ret, uint nextOffset) {
(ret, nextOffset) = asAddressUnchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBoolUnchecked(
bytes memory encoded,
uint offset
) internal pure returns (bool, uint) {
(uint8 val, uint nextOffset) = asUint8Unchecked(encoded, offset);
if (val & 0xfe != 0)
revert InvalidBoolVal(val);
uint cleanedVal = uint(val);
bool ret;
//skip 2x iszero opcode
assembly ("memory-safe") {
ret := cleanedVal
}
return (ret, nextOffset);
}
function asBool(
bytes memory encoded,
uint offset
) internal pure returns (bool ret, uint nextOffset) {
(ret, nextOffset) = asBoolUnchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
/* -------------------------------------------------------------------------------------------------
Remaining library code below was auto-generated by via the following js/node code:
for (let bytes = 1; bytes <= 32; ++bytes) {
const bits = bytes*8;
console.log(
`function asUint${bits}Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint${bits} ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, ${bytes})
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint${bits}(
bytes memory encoded,
uint offset
) internal pure returns (uint${bits} ret, uint nextOffset) {
(ret, nextOffset) = asUint${bits}Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes${bytes}Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes${bytes}, uint) {
(uint${bits} ret, uint nextOffset) = asUint${bits}Unchecked(encoded, offset);
return (bytes${bytes}(ret), nextOffset);
}
function asBytes${bytes}(
bytes memory encoded,
uint offset
) internal pure returns (bytes${bytes}, uint) {
(uint${bits} ret, uint nextOffset) = asUint${bits}(encoded, offset);
return (bytes${bytes}(ret), nextOffset);
}
`
);
}
------------------------------------------------------------------------------------------------- */
function asUint8Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint8 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 1)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint8(
bytes memory encoded,
uint offset
) internal pure returns (uint8 ret, uint nextOffset) {
(ret, nextOffset) = asUint8Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes1Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes1, uint) {
(uint8 ret, uint nextOffset) = asUint8Unchecked(encoded, offset);
return (bytes1(ret), nextOffset);
}
function asBytes1(
bytes memory encoded,
uint offset
) internal pure returns (bytes1, uint) {
(uint8 ret, uint nextOffset) = asUint8(encoded, offset);
return (bytes1(ret), nextOffset);
}
function asUint16Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint16 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 2)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint16(
bytes memory encoded,
uint offset
) internal pure returns (uint16 ret, uint nextOffset) {
(ret, nextOffset) = asUint16Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes2Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes2, uint) {
(uint16 ret, uint nextOffset) = asUint16Unchecked(encoded, offset);
return (bytes2(ret), nextOffset);
}
function asBytes2(
bytes memory encoded,
uint offset
) internal pure returns (bytes2, uint) {
(uint16 ret, uint nextOffset) = asUint16(encoded, offset);
return (bytes2(ret), nextOffset);
}
function asUint24Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint24 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 3)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint24(
bytes memory encoded,
uint offset
) internal pure returns (uint24 ret, uint nextOffset) {
(ret, nextOffset) = asUint24Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes3Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes3, uint) {
(uint24 ret, uint nextOffset) = asUint24Unchecked(encoded, offset);
return (bytes3(ret), nextOffset);
}
function asBytes3(
bytes memory encoded,
uint offset
) internal pure returns (bytes3, uint) {
(uint24 ret, uint nextOffset) = asUint24(encoded, offset);
return (bytes3(ret), nextOffset);
}
function asUint32Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint32 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 4)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint32(
bytes memory encoded,
uint offset
) internal pure returns (uint32 ret, uint nextOffset) {
(ret, nextOffset) = asUint32Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes4Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes4, uint) {
(uint32 ret, uint nextOffset) = asUint32Unchecked(encoded, offset);
return (bytes4(ret), nextOffset);
}
function asBytes4(
bytes memory encoded,
uint offset
) internal pure returns (bytes4, uint) {
(uint32 ret, uint nextOffset) = asUint32(encoded, offset);
return (bytes4(ret), nextOffset);
}
function asUint40Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint40 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 5)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint40(
bytes memory encoded,
uint offset
) internal pure returns (uint40 ret, uint nextOffset) {
(ret, nextOffset) = asUint40Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes5Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes5, uint) {
(uint40 ret, uint nextOffset) = asUint40Unchecked(encoded, offset);
return (bytes5(ret), nextOffset);
}
function asBytes5(
bytes memory encoded,
uint offset
) internal pure returns (bytes5, uint) {
(uint40 ret, uint nextOffset) = asUint40(encoded, offset);
return (bytes5(ret), nextOffset);
}
function asUint48Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint48 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 6)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint48(
bytes memory encoded,
uint offset
) internal pure returns (uint48 ret, uint nextOffset) {
(ret, nextOffset) = asUint48Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes6Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes6, uint) {
(uint48 ret, uint nextOffset) = asUint48Unchecked(encoded, offset);
return (bytes6(ret), nextOffset);
}
function asBytes6(
bytes memory encoded,
uint offset
) internal pure returns (bytes6, uint) {
(uint48 ret, uint nextOffset) = asUint48(encoded, offset);
return (bytes6(ret), nextOffset);
}
function asUint56Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint56 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 7)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint56(
bytes memory encoded,
uint offset
) internal pure returns (uint56 ret, uint nextOffset) {
(ret, nextOffset) = asUint56Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes7Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes7, uint) {
(uint56 ret, uint nextOffset) = asUint56Unchecked(encoded, offset);
return (bytes7(ret), nextOffset);
}
function asBytes7(
bytes memory encoded,
uint offset
) internal pure returns (bytes7, uint) {
(uint56 ret, uint nextOffset) = asUint56(encoded, offset);
return (bytes7(ret), nextOffset);
}
function asUint64Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint64 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 8)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint64(
bytes memory encoded,
uint offset
) internal pure returns (uint64 ret, uint nextOffset) {
(ret, nextOffset) = asUint64Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes8Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes8, uint) {
(uint64 ret, uint nextOffset) = asUint64Unchecked(encoded, offset);
return (bytes8(ret), nextOffset);
}
function asBytes8(
bytes memory encoded,
uint offset
) internal pure returns (bytes8, uint) {
(uint64 ret, uint nextOffset) = asUint64(encoded, offset);
return (bytes8(ret), nextOffset);
}
function asUint72Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint72 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 9)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint72(
bytes memory encoded,
uint offset
) internal pure returns (uint72 ret, uint nextOffset) {
(ret, nextOffset) = asUint72Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes9Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes9, uint) {
(uint72 ret, uint nextOffset) = asUint72Unchecked(encoded, offset);
return (bytes9(ret), nextOffset);
}
function asBytes9(
bytes memory encoded,
uint offset
) internal pure returns (bytes9, uint) {
(uint72 ret, uint nextOffset) = asUint72(encoded, offset);
return (bytes9(ret), nextOffset);
}
function asUint80Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint80 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 10)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint80(
bytes memory encoded,
uint offset
) internal pure returns (uint80 ret, uint nextOffset) {
(ret, nextOffset) = asUint80Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes10Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes10, uint) {
(uint80 ret, uint nextOffset) = asUint80Unchecked(encoded, offset);
return (bytes10(ret), nextOffset);
}
function asBytes10(
bytes memory encoded,
uint offset
) internal pure returns (bytes10, uint) {
(uint80 ret, uint nextOffset) = asUint80(encoded, offset);
return (bytes10(ret), nextOffset);
}
function asUint88Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint88 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 11)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint88(
bytes memory encoded,
uint offset
) internal pure returns (uint88 ret, uint nextOffset) {
(ret, nextOffset) = asUint88Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes11Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes11, uint) {
(uint88 ret, uint nextOffset) = asUint88Unchecked(encoded, offset);
return (bytes11(ret), nextOffset);
}
function asBytes11(
bytes memory encoded,
uint offset
) internal pure returns (bytes11, uint) {
(uint88 ret, uint nextOffset) = asUint88(encoded, offset);
return (bytes11(ret), nextOffset);
}
function asUint96Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint96 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 12)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint96(
bytes memory encoded,
uint offset
) internal pure returns (uint96 ret, uint nextOffset) {
(ret, nextOffset) = asUint96Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes12Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes12, uint) {
(uint96 ret, uint nextOffset) = asUint96Unchecked(encoded, offset);
return (bytes12(ret), nextOffset);
}
function asBytes12(
bytes memory encoded,
uint offset
) internal pure returns (bytes12, uint) {
(uint96 ret, uint nextOffset) = asUint96(encoded, offset);
return (bytes12(ret), nextOffset);
}
function asUint104Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint104 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 13)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint104(
bytes memory encoded,
uint offset
) internal pure returns (uint104 ret, uint nextOffset) {
(ret, nextOffset) = asUint104Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes13Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes13, uint) {
(uint104 ret, uint nextOffset) = asUint104Unchecked(encoded, offset);
return (bytes13(ret), nextOffset);
}
function asBytes13(
bytes memory encoded,
uint offset
) internal pure returns (bytes13, uint) {
(uint104 ret, uint nextOffset) = asUint104(encoded, offset);
return (bytes13(ret), nextOffset);
}
function asUint112Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint112 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 14)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint112(
bytes memory encoded,
uint offset
) internal pure returns (uint112 ret, uint nextOffset) {
(ret, nextOffset) = asUint112Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes14Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes14, uint) {
(uint112 ret, uint nextOffset) = asUint112Unchecked(encoded, offset);
return (bytes14(ret), nextOffset);
}
function asBytes14(
bytes memory encoded,
uint offset
) internal pure returns (bytes14, uint) {
(uint112 ret, uint nextOffset) = asUint112(encoded, offset);
return (bytes14(ret), nextOffset);
}
function asUint120Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint120 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 15)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint120(
bytes memory encoded,
uint offset
) internal pure returns (uint120 ret, uint nextOffset) {
(ret, nextOffset) = asUint120Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes15Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes15, uint) {
(uint120 ret, uint nextOffset) = asUint120Unchecked(encoded, offset);
return (bytes15(ret), nextOffset);
}
function asBytes15(
bytes memory encoded,
uint offset
) internal pure returns (bytes15, uint) {
(uint120 ret, uint nextOffset) = asUint120(encoded, offset);
return (bytes15(ret), nextOffset);
}
function asUint128Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint128 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 16)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint128(
bytes memory encoded,
uint offset
) internal pure returns (uint128 ret, uint nextOffset) {
(ret, nextOffset) = asUint128Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes16Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes16, uint) {
(uint128 ret, uint nextOffset) = asUint128Unchecked(encoded, offset);
return (bytes16(ret), nextOffset);
}
function asBytes16(
bytes memory encoded,
uint offset
) internal pure returns (bytes16, uint) {
(uint128 ret, uint nextOffset) = asUint128(encoded, offset);
return (bytes16(ret), nextOffset);
}
function asUint136Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint136 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 17)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint136(
bytes memory encoded,
uint offset
) internal pure returns (uint136 ret, uint nextOffset) {
(ret, nextOffset) = asUint136Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes17Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes17, uint) {
(uint136 ret, uint nextOffset) = asUint136Unchecked(encoded, offset);
return (bytes17(ret), nextOffset);
}
function asBytes17(
bytes memory encoded,
uint offset
) internal pure returns (bytes17, uint) {
(uint136 ret, uint nextOffset) = asUint136(encoded, offset);
return (bytes17(ret), nextOffset);
}
function asUint144Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint144 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 18)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint144(
bytes memory encoded,
uint offset
) internal pure returns (uint144 ret, uint nextOffset) {
(ret, nextOffset) = asUint144Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes18Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes18, uint) {
(uint144 ret, uint nextOffset) = asUint144Unchecked(encoded, offset);
return (bytes18(ret), nextOffset);
}
function asBytes18(
bytes memory encoded,
uint offset
) internal pure returns (bytes18, uint) {
(uint144 ret, uint nextOffset) = asUint144(encoded, offset);
return (bytes18(ret), nextOffset);
}
function asUint152Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint152 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 19)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint152(
bytes memory encoded,
uint offset
) internal pure returns (uint152 ret, uint nextOffset) {
(ret, nextOffset) = asUint152Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes19Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes19, uint) {
(uint152 ret, uint nextOffset) = asUint152Unchecked(encoded, offset);
return (bytes19(ret), nextOffset);
}
function asBytes19(
bytes memory encoded,
uint offset
) internal pure returns (bytes19, uint) {
(uint152 ret, uint nextOffset) = asUint152(encoded, offset);
return (bytes19(ret), nextOffset);
}
function asUint160Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint160 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 20)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint160(
bytes memory encoded,
uint offset
) internal pure returns (uint160 ret, uint nextOffset) {
(ret, nextOffset) = asUint160Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes20Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes20, uint) {
(uint160 ret, uint nextOffset) = asUint160Unchecked(encoded, offset);
return (bytes20(ret), nextOffset);
}
function asBytes20(
bytes memory encoded,
uint offset
) internal pure returns (bytes20, uint) {
(uint160 ret, uint nextOffset) = asUint160(encoded, offset);
return (bytes20(ret), nextOffset);
}
function asUint168Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint168 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 21)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint168(
bytes memory encoded,
uint offset
) internal pure returns (uint168 ret, uint nextOffset) {
(ret, nextOffset) = asUint168Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes21Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes21, uint) {
(uint168 ret, uint nextOffset) = asUint168Unchecked(encoded, offset);
return (bytes21(ret), nextOffset);
}
function asBytes21(
bytes memory encoded,
uint offset
) internal pure returns (bytes21, uint) {
(uint168 ret, uint nextOffset) = asUint168(encoded, offset);
return (bytes21(ret), nextOffset);
}
function asUint176Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint176 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 22)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint176(
bytes memory encoded,
uint offset
) internal pure returns (uint176 ret, uint nextOffset) {
(ret, nextOffset) = asUint176Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes22Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes22, uint) {
(uint176 ret, uint nextOffset) = asUint176Unchecked(encoded, offset);
return (bytes22(ret), nextOffset);
}
function asBytes22(
bytes memory encoded,
uint offset
) internal pure returns (bytes22, uint) {
(uint176 ret, uint nextOffset) = asUint176(encoded, offset);
return (bytes22(ret), nextOffset);
}
function asUint184Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint184 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 23)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint184(
bytes memory encoded,
uint offset
) internal pure returns (uint184 ret, uint nextOffset) {
(ret, nextOffset) = asUint184Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes23Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes23, uint) {
(uint184 ret, uint nextOffset) = asUint184Unchecked(encoded, offset);
return (bytes23(ret), nextOffset);
}
function asBytes23(
bytes memory encoded,
uint offset
) internal pure returns (bytes23, uint) {
(uint184 ret, uint nextOffset) = asUint184(encoded, offset);
return (bytes23(ret), nextOffset);
}
function asUint192Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint192 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 24)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint192(
bytes memory encoded,
uint offset
) internal pure returns (uint192 ret, uint nextOffset) {
(ret, nextOffset) = asUint192Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes24Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes24, uint) {
(uint192 ret, uint nextOffset) = asUint192Unchecked(encoded, offset);
return (bytes24(ret), nextOffset);
}
function asBytes24(
bytes memory encoded,
uint offset
) internal pure returns (bytes24, uint) {
(uint192 ret, uint nextOffset) = asUint192(encoded, offset);
return (bytes24(ret), nextOffset);
}
function asUint200Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint200 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 25)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint200(
bytes memory encoded,
uint offset
) internal pure returns (uint200 ret, uint nextOffset) {
(ret, nextOffset) = asUint200Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes25Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes25, uint) {
(uint200 ret, uint nextOffset) = asUint200Unchecked(encoded, offset);
return (bytes25(ret), nextOffset);
}
function asBytes25(
bytes memory encoded,
uint offset
) internal pure returns (bytes25, uint) {
(uint200 ret, uint nextOffset) = asUint200(encoded, offset);
return (bytes25(ret), nextOffset);
}
function asUint208Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint208 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 26)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint208(
bytes memory encoded,
uint offset
) internal pure returns (uint208 ret, uint nextOffset) {
(ret, nextOffset) = asUint208Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes26Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes26, uint) {
(uint208 ret, uint nextOffset) = asUint208Unchecked(encoded, offset);
return (bytes26(ret), nextOffset);
}
function asBytes26(
bytes memory encoded,
uint offset
) internal pure returns (bytes26, uint) {
(uint208 ret, uint nextOffset) = asUint208(encoded, offset);
return (bytes26(ret), nextOffset);
}
function asUint216Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint216 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 27)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint216(
bytes memory encoded,
uint offset
) internal pure returns (uint216 ret, uint nextOffset) {
(ret, nextOffset) = asUint216Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes27Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes27, uint) {
(uint216 ret, uint nextOffset) = asUint216Unchecked(encoded, offset);
return (bytes27(ret), nextOffset);
}
function asBytes27(
bytes memory encoded,
uint offset
) internal pure returns (bytes27, uint) {
(uint216 ret, uint nextOffset) = asUint216(encoded, offset);
return (bytes27(ret), nextOffset);
}
function asUint224Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint224 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 28)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint224(
bytes memory encoded,
uint offset
) internal pure returns (uint224 ret, uint nextOffset) {
(ret, nextOffset) = asUint224Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes28Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes28, uint) {
(uint224 ret, uint nextOffset) = asUint224Unchecked(encoded, offset);
return (bytes28(ret), nextOffset);
}
function asBytes28(
bytes memory encoded,
uint offset
) internal pure returns (bytes28, uint) {
(uint224 ret, uint nextOffset) = asUint224(encoded, offset);
return (bytes28(ret), nextOffset);
}
function asUint232Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint232 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 29)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint232(
bytes memory encoded,
uint offset
) internal pure returns (uint232 ret, uint nextOffset) {
(ret, nextOffset) = asUint232Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes29Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes29, uint) {
(uint232 ret, uint nextOffset) = asUint232Unchecked(encoded, offset);
return (bytes29(ret), nextOffset);
}
function asBytes29(
bytes memory encoded,
uint offset
) internal pure returns (bytes29, uint) {
(uint232 ret, uint nextOffset) = asUint232(encoded, offset);
return (bytes29(ret), nextOffset);
}
function asUint240Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint240 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 30)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint240(
bytes memory encoded,
uint offset
) internal pure returns (uint240 ret, uint nextOffset) {
(ret, nextOffset) = asUint240Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes30Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes30, uint) {
(uint240 ret, uint nextOffset) = asUint240Unchecked(encoded, offset);
return (bytes30(ret), nextOffset);
}
function asBytes30(
bytes memory encoded,
uint offset
) internal pure returns (bytes30, uint) {
(uint240 ret, uint nextOffset) = asUint240(encoded, offset);
return (bytes30(ret), nextOffset);
}
function asUint248Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint248 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 31)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint248(
bytes memory encoded,
uint offset
) internal pure returns (uint248 ret, uint nextOffset) {
(ret, nextOffset) = asUint248Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes31Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes31, uint) {
(uint248 ret, uint nextOffset) = asUint248Unchecked(encoded, offset);
return (bytes31(ret), nextOffset);
}
function asBytes31(
bytes memory encoded,
uint offset
) internal pure returns (bytes31, uint) {
(uint248 ret, uint nextOffset) = asUint248(encoded, offset);
return (bytes31(ret), nextOffset);
}
function asUint256Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint256 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 32)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint256(
bytes memory encoded,
uint offset
) internal pure returns (uint256 ret, uint nextOffset) {
(ret, nextOffset) = asUint256Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes32Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes32, uint) {
(uint256 ret, uint nextOffset) = asUint256Unchecked(encoded, offset);
return (bytes32(ret), nextOffset);
}
function asBytes32(
bytes memory encoded,
uint offset
) internal pure returns (bytes32, uint) {
(uint256 ret, uint nextOffset) = asUint256(encoded, offset);
return (bytes32(ret), nextOffset);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @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 towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (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 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
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.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 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.
uint256 twos = denominator & (0 - denominator);
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 (unsignedRoundsUp(rounding) && 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
* towards zero.
*
* 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
/**
* @title WormholeRelayer
* @author
* @notice This project allows developers to build cross-chain applications powered by Wormhole without needing to
* write and run their own relaying infrastructure
*
* We implement the IWormholeRelayer interface that allows users to request a delivery provider to relay a payload (and/or additional messages)
* to a chain and address of their choice.
*/
/**
* @notice VaaKey identifies a wormhole message
*
* @custom:member chainId Wormhole chain ID of the chain where this VAA was emitted from
* @custom:member emitterAddress Address of the emitter of the VAA, in Wormhole bytes32 format
* @custom:member sequence Sequence number of the VAA
*/
struct VaaKey {
uint16 chainId;
bytes32 emitterAddress;
uint64 sequence;
}
// 0-127 are reserved for standardized KeyTypes, 128-255 are for custom use
uint8 constant VAA_KEY_TYPE = 1;
struct MessageKey {
uint8 keyType; // 0-127 are reserved for standardized KeyTypes, 128-255 are for custom use
bytes encodedKey;
}
interface IWormholeRelayerBase {
event SendEvent(
uint64 indexed sequence,
uint256 deliveryQuote,
uint256 paymentForExtraReceiverValue
);
function getRegisteredWormholeRelayerContract(
uint16 chainId
) external view returns (bytes32);
/**
* @notice Returns true if a delivery has been attempted for the given deliveryHash
* Note: invalid deliveries where the tx reverts are not considered attempted
*/
function deliveryAttempted(
bytes32 deliveryHash
) external view returns (bool attempted);
/**
* @notice block number at which a delivery was successfully executed
*/
function deliverySuccessBlock(
bytes32 deliveryHash
) external view returns (uint256 blockNumber);
/**
* @notice block number of the latest attempt to execute a delivery that failed
*/
function deliveryFailureBlock(
bytes32 deliveryHash
) external view returns (uint256 blockNumber);
}
/**
* @title IWormholeRelayerSend
* @notice The interface to request deliveries
*/
interface IWormholeRelayerSend is IWormholeRelayerBase {
/**
* @notice Publishes an instruction for the default delivery provider
* to relay a payload to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
*
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
*
* Any refunds (from leftover gas) will be paid to the delivery provider. In order to receive the refunds, use the `sendPayloadToEvm` function
* with `refundChain` and `refundAddress` as parameters
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`.
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the default delivery provider
* to relay a payload to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the default delivery provider
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
*
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
*
* Any refunds (from leftover gas) will be paid to the delivery provider. In order to receive the refunds, use the `sendVaasToEvm` function
* with `refundChain` and `refundAddress` as parameters
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`.
* @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendVaasToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
VaaKey[] memory vaaKeys
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the default delivery provider
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider
* @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendVaasToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
VaaKey[] memory vaaKeys,
uint16 refundChain,
address refundAddress
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to
* receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to
* quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit, deliveryProviderAddress) + paymentForExtraReceiverValue
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
* (in addition to the `receiverValue` specified)
* @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
* @param consistencyLevel Consistency level with which to publish the delivery instructions - see
* https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress,
address deliveryProviderAddress,
VaaKey[] memory vaaKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
* to relay a payload and external messages specified by `messageKeys` to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to
* receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to
* quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit, deliveryProviderAddress) + paymentForExtraReceiverValue
*
* Note: MessageKeys can specify wormhole messages (VaaKeys) or other types of messages (ex. USDC CCTP attestations). Ensure the selected
* DeliveryProvider supports all the MessageKey.keyType values specified or it will not be delivered!
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
* (in addition to the `receiverValue` specified)
* @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @param messageKeys Additional messagess to pass in as parameter in call to `targetAddress`
* @param consistencyLevel Consistency level with which to publish the delivery instructions - see
* https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress,
address deliveryProviderAddress,
MessageKey[] memory messageKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with `msg.value` equal to
* receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to
* quoteDeliveryPrice(targetChain, receiverValue, encodedExecutionParameters, deliveryProviderAddress) + paymentForExtraReceiverValue
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
* (in addition to the `receiverValue` specified)
* @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
* @param consistencyLevel Consistency level with which to publish the delivery instructions - see
* https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
* @return sequence sequence number of published VAA containing delivery instructions
*/
function send(
uint16 targetChain,
bytes32 targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
bytes memory encodedExecutionParameters,
uint16 refundChain,
bytes32 refundAddress,
address deliveryProviderAddress,
VaaKey[] memory vaaKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with `msg.value` equal to
* receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to
* quoteDeliveryPrice(targetChain, receiverValue, encodedExecutionParameters, deliveryProviderAddress) + paymentForExtraReceiverValue
*
* Note: MessageKeys can specify wormhole messages (VaaKeys) or other types of messages (ex. USDC CCTP attestations). Ensure the selected
* DeliveryProvider supports all the MessageKey.keyType values specified or it will not be delivered!
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
* (in addition to the `receiverValue` specified)
* @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @param messageKeys Additional messagess to pass in as parameter in call to `targetAddress`
* @param consistencyLevel Consistency level with which to publish the delivery instructions - see
* https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
* @return sequence sequence number of published VAA containing delivery instructions
*/
function send(
uint16 targetChain,
bytes32 targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
bytes memory encodedExecutionParameters,
uint16 refundChain,
bytes32 refundAddress,
address deliveryProviderAddress,
MessageKey[] memory messageKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
/**
* @notice Requests a previously published delivery instruction to be redelivered
* (e.g. with a different delivery provider)
*
* This function must be called with `msg.value` equal to
* quoteEVMDeliveryPrice(targetChain, newReceiverValue, newGasLimit, newDeliveryProviderAddress)
*
* @notice *** This will only be able to succeed if the following is true **
* - newGasLimit >= gas limit of the old instruction
* - newReceiverValue >= receiver value of the old instruction
* - newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused`
*
* @param deliveryVaaKey VaaKey identifying the wormhole message containing the
* previously published delivery instructions
* @param targetChain The target chain that the original delivery targeted. Must match targetChain from original delivery instructions
* @param newReceiverValue new msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param newGasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider, to the refund chain and address specified in the original request
* @param newDeliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return sequence sequence number of published VAA containing redelivery instructions
*
* @notice *** This will only be able to succeed if the following is true **
* - newGasLimit >= gas limit of the old instruction
* - newReceiverValue >= receiver value of the old instruction
*/
function resendToEvm(
VaaKey memory deliveryVaaKey,
uint16 targetChain,
uint256 newReceiverValue,
uint256 newGasLimit,
address newDeliveryProviderAddress
) external payable returns (uint64 sequence);
/**
* @notice Requests a previously published delivery instruction to be redelivered
*
*
* This function must be called with `msg.value` equal to
* quoteDeliveryPrice(targetChain, newReceiverValue, newEncodedExecutionParameters, newDeliveryProviderAddress)
*
* @param deliveryVaaKey VaaKey identifying the wormhole message containing the
* previously published delivery instructions
* @param targetChain The target chain that the original delivery targeted. Must match targetChain from original delivery instructions
* @param newReceiverValue new msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param newEncodedExecutionParameters new encoded information on how to execute delivery that may impact pricing
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
* @param newDeliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return sequence sequence number of published VAA containing redelivery instructions
*
* @notice *** This will only be able to succeed if the following is true **
* - (For EVM_V1) newGasLimit >= gas limit of the old instruction
* - newReceiverValue >= receiver value of the old instruction
* - (For EVM_V1) newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused`
*/
function resend(
VaaKey memory deliveryVaaKey,
uint16 targetChain,
uint256 newReceiverValue,
bytes memory newEncodedExecutionParameters,
address newDeliveryProviderAddress
) external payable returns (uint64 sequence);
/**
* @notice Returns the price to request a relay to chain `targetChain`, using the default delivery provider
*
* @param targetChain in Wormhole Chain ID format
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`.
* @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
* @return targetChainRefundPerGasUnused amount of target chain currency that will be refunded per unit of gas unused,
* if a refundAddress is specified.
* Note: This value can be overridden by the delivery provider on the target chain. The returned value here should be considered to be a
* promise by the delivery provider of the amount of refund per gas unused that will be returned to the refundAddress at the target chain.
* If a delivery provider decides to override, this will be visible as part of the emitted Delivery event on the target chain.
*/
function quoteEVMDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
uint256 gasLimit
)
external
view
returns (
uint256 nativePriceQuote,
uint256 targetChainRefundPerGasUnused
);
/**
* @notice Returns the price to request a relay to chain `targetChain`, using delivery provider `deliveryProviderAddress`
*
* @param targetChain in Wormhole Chain ID format
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`.
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
* @return targetChainRefundPerGasUnused amount of target chain currency that will be refunded per unit of gas unused,
* if a refundAddress is specified
* Note: This value can be overridden by the delivery provider on the target chain. The returned value here should be considered to be a
* promise by the delivery provider of the amount of refund per gas unused that will be returned to the refundAddress at the target chain.
* If a delivery provider decides to override, this will be visible as part of the emitted Delivery event on the target chain.
*/
function quoteEVMDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
uint256 gasLimit,
address deliveryProviderAddress
)
external
view
returns (
uint256 nativePriceQuote,
uint256 targetChainRefundPerGasUnused
);
/**
* @notice Returns the price to request a relay to chain `targetChain`, using delivery provider `deliveryProviderAddress`
*
* @param targetChain in Wormhole Chain ID format
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
* @return encodedExecutionInfo encoded information on how the delivery will be executed
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` and `targetChainRefundPerGasUnused`
* (which is the amount of target chain currency that will be refunded per unit of gas unused,
* if a refundAddress is specified)
*/
function quoteDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
bytes memory encodedExecutionParameters,
address deliveryProviderAddress
)
external
view
returns (uint256 nativePriceQuote, bytes memory encodedExecutionInfo);
/**
* @notice Returns the (extra) amount of target chain currency that `targetAddress`
* will be called with, if the `paymentForExtraReceiverValue` field is set to `currentChainAmount`
*
* @param targetChain in Wormhole Chain ID format
* @param currentChainAmount The value that `paymentForExtraReceiverValue` will be set to
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return targetChainAmount The amount such that if `targetAddress` will be called with `msg.value` equal to
* receiverValue + targetChainAmount
*/
function quoteNativeForChain(
uint16 targetChain,
uint256 currentChainAmount,
address deliveryProviderAddress
) external view returns (uint256 targetChainAmount);
/**
* @notice Returns the address of the current default delivery provider
* @return deliveryProvider The address of (the default delivery provider)'s contract on this source
* chain. This must be a contract that implements IDeliveryProvider.
*/
function getDefaultDeliveryProvider()
external
view
returns (address deliveryProvider);
}
/**
* @title IWormholeRelayerDelivery
* @notice The interface to execute deliveries. Only relevant for Delivery Providers
*/
interface IWormholeRelayerDelivery is IWormholeRelayerBase {
enum DeliveryStatus {
SUCCESS,
RECEIVER_FAILURE
}
enum RefundStatus {
REFUND_SENT,
REFUND_FAIL,
CROSS_CHAIN_REFUND_SENT,
CROSS_CHAIN_REFUND_FAIL_PROVIDER_NOT_SUPPORTED,
CROSS_CHAIN_REFUND_FAIL_NOT_ENOUGH,
NO_REFUND_REQUESTED
}
/**
* @custom:member recipientContract - The target contract address
* @custom:member sourceChain - The chain which this delivery was requested from (in wormhole
* ChainID format)
* @custom:member sequence - The wormhole sequence number of the delivery VAA on the source chain
* corresponding to this delivery request
* @custom:member deliveryVaaHash - The hash of the delivery VAA corresponding to this delivery
* request
* @custom:member gasUsed - The amount of gas that was used to call your target contract
* @custom:member status:
* - RECEIVER_FAILURE, if the target contract reverts
* - SUCCESS, if the target contract doesn't revert
* @custom:member additionalStatusInfo:
* - If status is SUCCESS, then this is empty.
* - If status is RECEIVER_FAILURE, this is `RETURNDATA_TRUNCATION_THRESHOLD` bytes of the
* return data (i.e. potentially truncated revert reason information).
* @custom:member refundStatus - Result of the refund. REFUND_SUCCESS or REFUND_FAIL are for
* refunds where targetChain=refundChain; the others are for targetChain!=refundChain,
* where a cross chain refund is necessary, or if the default code path is used where no refund is requested (NO_REFUND_REQUESTED)
* @custom:member overridesInfo:
* - If not an override: empty bytes array
* - Otherwise: An encoded `DeliveryOverride`
*/
event Delivery(
address indexed recipientContract,
uint16 indexed sourceChain,
uint64 indexed sequence,
bytes32 deliveryVaaHash,
DeliveryStatus status,
uint256 gasUsed,
RefundStatus refundStatus,
bytes additionalStatusInfo,
bytes overridesInfo
);
/**
* @notice The delivery provider calls `deliver` to relay messages as described by one delivery instruction
*
* The delivery provider must pass in the specified (by VaaKeys[]) signed wormhole messages (VAAs) from the source chain
* as well as the signed wormhole message with the delivery instructions (the delivery VAA)
*
* The messages will be relayed to the target address (with the specified gas limit and receiver value) iff the following checks are met:
* - the delivery VAA has a valid signature
* - the delivery VAA's emitter is one of these WormholeRelayer contracts
* - the delivery provider passed in at least enough of this chain's currency as msg.value (enough meaning the maximum possible refund)
* - the instruction's target chain is this chain
* - the relayed signed VAAs match the descriptions in container.messages (the VAA hashes match, or the emitter address, sequence number pair matches, depending on the description given)
*
* @param encodedVMs - An array of signed wormhole messages (all from the same source chain
* transaction)
* @param encodedDeliveryVAA - Signed wormhole message from the source chain's WormholeRelayer
* contract with payload being the encoded delivery instruction container
* @param relayerRefundAddress - The address to which any refunds to the delivery provider
* should be sent
* @param deliveryOverrides - Optional overrides field which must be either an empty bytes array or
* an encoded DeliveryOverride struct
*/
function deliver(
bytes[] memory encodedVMs,
bytes memory encodedDeliveryVAA,
address payable relayerRefundAddress,
bytes memory deliveryOverrides
) external payable;
}
interface IWormholeRelayer is IWormholeRelayerDelivery, IWormholeRelayerSend {}
/*
* Errors thrown by IWormholeRelayer contract
*/
// Bound chosen by the following formula: `memoryWord * 4 + selectorSize`.
// This means that an error identifier plus four fixed size arguments should be available to developers.
// In the case of a `require` revert with error message, this should provide 2 memory word's worth of data.
uint256 constant RETURNDATA_TRUNCATION_THRESHOLD = 132;
//When msg.value was not equal to `delivery provider's quoted delivery price` + `paymentForExtraReceiverValue`
error InvalidMsgValue(uint256 msgValue, uint256 totalFee);
error RequestedGasLimitTooLow();
error DeliveryProviderDoesNotSupportTargetChain(
address relayer,
uint16 chainId
);
error DeliveryProviderCannotReceivePayment();
error DeliveryProviderDoesNotSupportMessageKeyType(uint8 keyType);
//When calling `delivery()` a second time even though a delivery is already in progress
error ReentrantDelivery(address msgSender, address lockedBy);
error InvalidPayloadId(uint8 parsed, uint8 expected);
error InvalidPayloadLength(uint256 received, uint256 expected);
error InvalidVaaKeyType(uint8 parsed);
error TooManyMessageKeys(uint256 numMessageKeys);
error InvalidDeliveryVaa(string reason);
//When the delivery VAA (signed wormhole message with delivery instructions) was not emitted by the
// registered WormholeRelayer contract
error InvalidEmitter(bytes32 emitter, bytes32 registered, uint16 chainId);
error MessageKeysLengthDoesNotMatchMessagesLength(uint256 keys, uint256 vaas);
error VaaKeysDoNotMatchVaas(uint8 index);
//When someone tries to call an external function of the WormholeRelayer that is only intended to be
// called by the WormholeRelayer itself (to allow retroactive reverts for atomicity)
error RequesterNotWormholeRelayer();
//When trying to relay a `DeliveryInstruction` to any other chain but the one it was specified for
error TargetChainIsNotThisChain(uint16 targetChain);
//When a `DeliveryOverride` contains a gas limit that's less than the original
error InvalidOverrideGasLimit();
//When a `DeliveryOverride` contains a receiver value that's less than the original
error InvalidOverrideReceiverValue();
//When a `DeliveryOverride` contains a 'refund per unit of gas unused' that's less than the original
error InvalidOverrideRefundPerGasUnused();
//When the delivery provider doesn't pass in sufficient funds (i.e. msg.value does not cover the
// maximum possible refund to the user)
error InsufficientRelayerFunds(uint256 msgValue, uint256 minimum);
//When a bytes32 field can't be converted into a 20 byte EVM address, because the 12 padding bytes
// are non-zero (duplicated from Utils.sol)
error NotAnEvmAddress(bytes32);