Overview
ETH Balance
ETH Value
$0.00Multichain Info
Latest 25 from a total of 258 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Transmit | 15585037 | 1 hr ago | IN | 0 ETH | 0.00000307 | ||||
| Transmit | 15501792 | 24 hrs ago | IN | 0 ETH | 0.00000805 | ||||
| Transmit | 15409327 | 2 days ago | IN | 0 ETH | 0.00000775 | ||||
| Transmit | 15196543 | 4 days ago | IN | 0 ETH | 0.00000854 | ||||
| Transmit | 15164293 | 4 days ago | IN | 0 ETH | 0.00000233 | ||||
| Transmit | 15159669 | 4 days ago | IN | 0 ETH | 0.00000463 | ||||
| Transmit | 15158527 | 4 days ago | IN | 0 ETH | 0.00000218 | ||||
| Transmit | 15156725 | 5 days ago | IN | 0 ETH | 0.00000344 | ||||
| Transmit | 14969980 | 7 days ago | IN | 0 ETH | 0.00000197 | ||||
| Transmit | 14927724 | 7 days ago | IN | 0 ETH | 0.00000557 | ||||
| Transmit | 14876277 | 8 days ago | IN | 0 ETH | 0.00000167 | ||||
| Transmit | 14869761 | 8 days ago | IN | 0 ETH | 0.00000188 | ||||
| Transmit | 14866698 | 8 days ago | IN | 0 ETH | 0.00000157 | ||||
| Transmit | 14827730 | 8 days ago | IN | 0 ETH | 0.00000217 | ||||
| Transmit | 14811395 | 8 days ago | IN | 0 ETH | 0.00000192 | ||||
| Transmit | 14580541 | 11 days ago | IN | 0 ETH | 0.00000399 | ||||
| Transmit | 14567180 | 11 days ago | IN | 0 ETH | 0.00001253 | ||||
| Manually Execute | 14497760 | 12 days ago | IN | 0 ETH | 0.0000019 | ||||
| Transmit | 14497373 | 12 days ago | IN | 0 ETH | 0.00001206 | ||||
| Transmit | 14469328 | 12 days ago | IN | 0 ETH | 0.00000183 | ||||
| Transmit | 14330257 | 14 days ago | IN | 0 ETH | 0.00000569 | ||||
| Transmit | 14224277 | 15 days ago | IN | 0 ETH | 0.00000521 | ||||
| Transmit | 14223136 | 15 days ago | IN | 0 ETH | 0.00000181 | ||||
| Transmit | 14130943 | 16 days ago | IN | 0 ETH | 0.00000173 | ||||
| Transmit | 14095000 | 17 days ago | IN | 0 ETH | 0.00000188 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15585037 | 1 hr ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15501792 | 24 hrs ago | 0 ETH | |||||
| 15409327 | 2 days ago | 0 ETH | |||||
| 15409327 | 2 days ago | 0 ETH | |||||
| 15409327 | 2 days ago | 0 ETH | |||||
| 15409327 | 2 days ago | 0 ETH | |||||
| 15409327 | 2 days ago | 0 ETH |
Cross-Chain Transactions
Contract Source Code Verified (Exact Match)
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;
import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {IAny2EVMMessageReceiver} from "../interfaces/IAny2EVMMessageReceiver.sol";
import {IAny2EVMOffRamp} from "../interfaces/IAny2EVMOffRamp.sol";
import {ICommitStore} from "../interfaces/ICommitStore.sol";
import {IPoolV1} from "../interfaces/IPool.sol";
import {IPriceRegistry} from "../interfaces/IPriceRegistry.sol";
import {IRMN} from "../interfaces/IRMN.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol";
import {CallWithExactGas} from "../../shared/call/CallWithExactGas.sol";
import {EnumerableMapAddresses} from "../../shared/enumerable/EnumerableMapAddresses.sol";
import {AggregateRateLimiter} from "../AggregateRateLimiter.sol";
import {Client} from "../libraries/Client.sol";
import {Internal} from "../libraries/Internal.sol";
import {Pool} from "../libraries/Pool.sol";
import {RateLimiter} from "../libraries/RateLimiter.sol";
import {OCR2BaseNoChecks} from "../ocr/OCR2BaseNoChecks.sol";
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {ERC165Checker} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/ERC165Checker.sol";
/// @notice EVM2EVMOffRamp enables OCR networks to execute multiple messages
/// in an OffRamp in a single transaction.
/// @dev The EVM2EVMOnRamp, CommitStore and EVM2EVMOffRamp form an xchain upgradeable unit. Any change to one of them
/// results an onchain upgrade of all 3.
/// @dev OCR2BaseNoChecks is used to save gas, signatures are not required as the offramp can only execute
/// messages which are committed in the commitStore. We still make use of OCR2 as an executor whitelist
/// and turn-taking mechanism.
contract EVM2EVMOffRamp is IAny2EVMOffRamp, AggregateRateLimiter, ITypeAndVersion, OCR2BaseNoChecks {
using ERC165Checker for address;
using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap;
error ZeroAddressNotAllowed();
error CommitStoreAlreadyInUse();
error ExecutionError(bytes err);
error InvalidSourceChain(uint64 sourceChainSelector);
error MessageTooLarge(uint256 maxSize, uint256 actualSize);
error TokenDataMismatch(uint64 sequenceNumber);
error UnexpectedTokenData();
error UnsupportedNumberOfTokens(uint64 sequenceNumber);
error ManualExecutionNotYetEnabled();
error ManualExecutionGasLimitMismatch();
error DestinationGasAmountCountMismatch(bytes32 messageId, uint64 sequenceNumber);
error InvalidManualExecutionGasLimit(bytes32 messageId, uint256 oldLimit, uint256 newLimit);
error InvalidTokenGasOverride(bytes32 messageId, uint256 tokenIndex, uint256 oldLimit, uint256 tokenGasOverride);
error RootNotCommitted();
error CanOnlySelfCall();
error ReceiverError(bytes err);
error TokenHandlingError(bytes err);
error ReleaseOrMintBalanceMismatch(uint256 amountReleased, uint256 balancePre, uint256 balancePost);
error EmptyReport();
error CursedByRMN();
error InvalidMessageId();
error NotACompatiblePool(address notPool);
error InvalidDataLength(uint256 expected, uint256 got);
error InvalidNewState(uint64 sequenceNumber, Internal.MessageExecutionState newState);
/// @dev Atlas depends on this event, if changing, please notify Atlas.
event ConfigSet(StaticConfig staticConfig, DynamicConfig dynamicConfig);
event SkippedIncorrectNonce(uint64 indexed nonce, address indexed sender);
event SkippedSenderWithPreviousRampMessageInflight(uint64 indexed nonce, address indexed sender);
/// @dev RMN depends on this event, if changing, please notify the RMN maintainers.
event ExecutionStateChanged(
uint64 indexed sequenceNumber, bytes32 indexed messageId, Internal.MessageExecutionState state, bytes returnData
);
event TokenAggregateRateLimitAdded(address sourceToken, address destToken);
event TokenAggregateRateLimitRemoved(address sourceToken, address destToken);
event SkippedAlreadyExecutedMessage(uint64 indexed sequenceNumber);
event AlreadyAttempted(uint64 sequenceNumber);
/// @notice Static offRamp config
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
//solhint-disable gas-struct-packing
struct StaticConfig {
address commitStore; // --------? CommitStore address on the destination chain
uint64 chainSelector; // -------? Destination chainSelector
uint64 sourceChainSelector; // -? Source chainSelector
address onRamp; // -------------? OnRamp address on the source chain
address prevOffRamp; // Address of previous-version OffRamp
address rmnProxy; // RMN proxy address
address tokenAdminRegistry; // Token admin registry address
}
/// @notice Dynamic offRamp config
/// @dev since OffRampConfig is part of OffRampConfigChanged event, if changing it, we should update the ABI on Atlas
struct DynamicConfig {
uint32 permissionLessExecutionThresholdSeconds; // -? Waiting time before manual execution is enabled
uint32 maxDataBytes; // ¦ Maximum payload data size in bytes
uint16 maxNumberOfTokensPerMsg; // ¦ Maximum number of ERC20 token transfers that can be included per message
address router; // ---------------------------------? Router address
address priceRegistry; // Price registry address
}
/// @notice RateLimitToken struct containing both the source and destination token addresses
struct RateLimitToken {
address sourceToken;
address destToken;
}
/// @notice Gas overrides for manual exec, the number of token overrides must match the number of tokens in the msg.
struct GasLimitOverride {
/// @notice Overrides EVM2EVMMessage.gasLimit. A value of zero indicates no override and is valid.
uint256 receiverExecutionGasLimit;
/// @notice Overrides EVM2EVMMessage.sourceTokenData.destGasAmount. Must be same length as tokenAmounts. A value
/// of zero indicates no override and is valid.
uint32[] tokenGasOverrides;
}
// STATIC CONFIG
string public constant override typeAndVersion = "EVM2EVMOffRamp 1.5.0";
/// @dev Commit store address on the destination chain
address internal immutable i_commitStore;
/// @dev ChainSelector of the source chain
uint64 internal immutable i_sourceChainSelector;
/// @dev ChainSelector of this chain
uint64 internal immutable i_chainSelector;
/// @dev OnRamp address on the source chain
address internal immutable i_onRamp;
/// @dev metadataHash is a lane-specific prefix for a message hash preimage which ensures global uniqueness.
/// Ensures that 2 identical messages sent to 2 different lanes will have a distinct hash.
/// Must match the metadataHash used in computing leaf hashes offchain for the root committed in
/// the commitStore and i_metadataHash in the onRamp.
bytes32 internal immutable i_metadataHash;
/// @dev The address of previous-version OffRamp for this lane.
/// Used to be able to provide sequencing continuity during a zero downtime upgrade.
address internal immutable i_prevOffRamp;
/// @dev The address of the RMN proxy
address internal immutable i_rmnProxy;
/// @dev The address of the token admin registry
address internal immutable i_tokenAdminRegistry;
// DYNAMIC CONFIG
DynamicConfig internal s_dynamicConfig;
/// @dev Tokens that should be included in Aggregate Rate Limiting
/// An (address => address) map is used for backwards compatability of offchain code
EnumerableMapAddresses.AddressToAddressMap internal s_rateLimitedTokensDestToSource;
// STATE
/// @dev The expected nonce for a given sender.
/// Corresponds to s_senderNonce in the OnRamp, used to enforce that messages are
/// executed in the same order they are sent (assuming they are DON). Note that re-execution
/// of FAILED messages however, can be out of order.
mapping(address sender => uint64 nonce) internal s_senderNonce;
/// @dev A mapping of sequence numbers to execution state using a bitmap with each execution
/// state only taking up 2 bits of the uint256, packing 128 states into a single slot.
/// Message state is tracked to ensure message can only be executed successfully once.
mapping(uint64 seqNum => uint256 executionStateBitmap) internal s_executionStates;
constructor(
StaticConfig memory staticConfig,
RateLimiter.Config memory rateLimiterConfig
) OCR2BaseNoChecks() AggregateRateLimiter(rateLimiterConfig) {
if (
staticConfig.onRamp == address(0) || staticConfig.commitStore == address(0)
|| staticConfig.tokenAdminRegistry == address(0)
) revert ZeroAddressNotAllowed();
// Ensures we can never deploy a new offRamp that points to a commitStore that
// already has roots committed.
if (ICommitStore(staticConfig.commitStore).getExpectedNextSequenceNumber() != 1) revert CommitStoreAlreadyInUse();
i_commitStore = staticConfig.commitStore;
i_sourceChainSelector = staticConfig.sourceChainSelector;
i_chainSelector = staticConfig.chainSelector;
i_onRamp = staticConfig.onRamp;
i_prevOffRamp = staticConfig.prevOffRamp;
i_rmnProxy = staticConfig.rmnProxy;
i_tokenAdminRegistry = staticConfig.tokenAdminRegistry;
i_metadataHash = _metadataHash(Internal.EVM_2_EVM_MESSAGE_HASH);
}
// ================================================================
// ¦ Messaging ¦
// ================================================================
// The size of the execution state in bits
uint256 private constant MESSAGE_EXECUTION_STATE_BIT_WIDTH = 2;
// The mask for the execution state bits
uint256 private constant MESSAGE_EXECUTION_STATE_MASK = (1 << MESSAGE_EXECUTION_STATE_BIT_WIDTH) - 1;
/// @notice Returns the current execution state of a message based on its sequenceNumber.
/// @param sequenceNumber The sequence number of the message to get the execution state for.
/// @return The current execution state of the message.
/// @dev we use the literal number 128 because using a constant increased gas usage.
function getExecutionState(uint64 sequenceNumber) public view returns (Internal.MessageExecutionState) {
return Internal.MessageExecutionState(
(s_executionStates[sequenceNumber / 128] >> ((sequenceNumber % 128) * MESSAGE_EXECUTION_STATE_BIT_WIDTH))
& MESSAGE_EXECUTION_STATE_MASK
);
}
/// @notice Sets a new execution state for a given sequence number. It will overwrite any existing state.
/// @param sequenceNumber The sequence number for which the state will be saved.
/// @param newState The new value the state will be in after this function is called.
/// @dev we use the literal number 128 because using a constant increased gas usage.
function _setExecutionState(uint64 sequenceNumber, Internal.MessageExecutionState newState) internal {
uint256 offset = (sequenceNumber % 128) * MESSAGE_EXECUTION_STATE_BIT_WIDTH;
uint256 bitmap = s_executionStates[sequenceNumber / 128];
// to unset any potential existing state we zero the bits of the section the state occupies,
// then we do an AND operation to blank out any existing state for the section.
bitmap &= ~(MESSAGE_EXECUTION_STATE_MASK << offset);
// Set the new state
bitmap |= uint256(newState) << offset;
s_executionStates[sequenceNumber / 128] = bitmap;
}
/// @inheritdoc IAny2EVMOffRamp
function getSenderNonce(address sender) external view returns (uint64 nonce) {
uint256 senderNonce = s_senderNonce[sender];
if (senderNonce == 0) {
if (i_prevOffRamp != address(0)) {
// If OffRamp was upgraded, check if sender has a nonce from the previous OffRamp.
return IAny2EVMOffRamp(i_prevOffRamp).getSenderNonce(sender);
}
}
return uint64(senderNonce);
}
/// @notice Manually execute a message.
/// @param report Internal.ExecutionReport.
/// @param gasLimitOverrides New gasLimit for each message in the report.
/// @dev We permit gas limit overrides so that users may manually execute messages which failed due to
/// insufficient gas provided.
function manuallyExecute(
Internal.ExecutionReport memory report,
GasLimitOverride[] memory gasLimitOverrides
) external {
// We do this here because the other _execute path is already covered OCR2BaseXXX.
_checkChainForked();
uint256 numMsgs = report.messages.length;
if (numMsgs != gasLimitOverrides.length) revert ManualExecutionGasLimitMismatch();
for (uint256 i = 0; i < numMsgs; ++i) {
Internal.EVM2EVMMessage memory message = report.messages[i];
GasLimitOverride memory gasLimitOverride = gasLimitOverrides[i];
uint256 newLimit = gasLimitOverride.receiverExecutionGasLimit;
// Checks to ensure message cannot be executed with less gas than specified.
if (newLimit != 0) {
if (newLimit < message.gasLimit) {
revert InvalidManualExecutionGasLimit(message.messageId, message.gasLimit, newLimit);
}
}
if (message.tokenAmounts.length != gasLimitOverride.tokenGasOverrides.length) {
revert DestinationGasAmountCountMismatch(message.messageId, message.sequenceNumber);
}
bytes[] memory encodedSourceTokenData = message.sourceTokenData;
for (uint256 j = 0; j < message.tokenAmounts.length; ++j) {
Internal.SourceTokenData memory sourceTokenData =
abi.decode(encodedSourceTokenData[i], (Internal.SourceTokenData));
uint256 tokenGasOverride = gasLimitOverride.tokenGasOverrides[j];
// The gas limit can not be lowered as that could cause the message to fail. If manual execution is done
// from an UNTOUCHED state and we would allow lower gas limit, anyone could grief by executing the message with
// lower gas limit than the DON would have used. This results in the message being marked FAILURE and the DON
// would not attempt it with the correct gas limit.
if (tokenGasOverride != 0 && tokenGasOverride < sourceTokenData.destGasAmount) {
revert InvalidTokenGasOverride(message.messageId, j, sourceTokenData.destGasAmount, tokenGasOverride);
}
}
}
_execute(report, gasLimitOverrides);
}
/// @notice Entrypoint for execution, called by the OCR network
/// @dev Expects an encoded ExecutionReport
/// @dev Supplies no GasLimitOverrides as the DON will only execute with the original gas limits.
function _report(bytes calldata report) internal override {
_execute(abi.decode(report, (Internal.ExecutionReport)), new GasLimitOverride[](0));
}
/// @notice Executes a report, executing each message in order.
/// @param report The execution report containing the messages and proofs.
/// @param manualExecGasOverrides An array of gas limits to use for manual execution.
/// @dev If called from the DON, this array is always empty.
/// @dev If called from manual execution, this array is always same length as messages.
function _execute(Internal.ExecutionReport memory report, GasLimitOverride[] memory manualExecGasOverrides) internal {
if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(i_sourceChainSelector)))) revert CursedByRMN();
uint256 numMsgs = report.messages.length;
if (numMsgs == 0) revert EmptyReport();
if (numMsgs != report.offchainTokenData.length) revert UnexpectedTokenData();
bytes32[] memory hashedLeaves = new bytes32[](numMsgs);
for (uint256 i = 0; i < numMsgs; ++i) {
Internal.EVM2EVMMessage memory message = report.messages[i];
// We do this hash here instead of in _verifyMessages to avoid two separate loops
// over the same data, which increases gas cost
hashedLeaves[i] = Internal._hash(message, i_metadataHash);
// For EVM2EVM offramps, the messageID is the leaf hash.
// Asserting that this is true ensures we don't accidentally commit and then execute
// a message with an unexpected hash.
if (hashedLeaves[i] != message.messageId) revert InvalidMessageId();
}
bool manualExecution = manualExecGasOverrides.length != 0;
// SECURITY CRITICAL CHECK
uint256 timestampCommitted = ICommitStore(i_commitStore).verify(hashedLeaves, report.proofs, report.proofFlagBits);
if (timestampCommitted == 0) revert RootNotCommitted();
// Execute messages
for (uint256 i = 0; i < numMsgs; ++i) {
Internal.EVM2EVMMessage memory message = report.messages[i];
Internal.MessageExecutionState originalState = getExecutionState(message.sequenceNumber);
// Two valid cases here, we either have never touched this message before, or we tried to execute
// and failed. This check protects against reentry and re-execution because the other state is
// IN_PROGRESS which should not be allowed to execute.
if (
!(
originalState == Internal.MessageExecutionState.UNTOUCHED
|| originalState == Internal.MessageExecutionState.FAILURE
)
) {
// If the message has already been executed, we skip it. We want to not revert on race conditions between
// executing parties. This will allow us to open up manual exec while also attempting with the DON, without
// reverting an entire DON batch when a user manually executes while the tx is inflight.
emit SkippedAlreadyExecutedMessage(message.sequenceNumber);
continue;
}
uint32[] memory tokenGasOverrides;
if (manualExecution) {
tokenGasOverrides = manualExecGasOverrides[i].tokenGasOverrides;
bool isOldCommitReport =
(block.timestamp - timestampCommitted) > s_dynamicConfig.permissionLessExecutionThresholdSeconds;
// Manually execution is fine if we previously failed or if the commit report is just too old
// Acceptable state transitions: FAILURE->SUCCESS, UNTOUCHED->SUCCESS, FAILURE->FAILURE
if (!(isOldCommitReport || originalState == Internal.MessageExecutionState.FAILURE)) {
revert ManualExecutionNotYetEnabled();
}
// Manual execution gas limit can override gas limit specified in the message. Value of 0 indicates no override.
if (manualExecGasOverrides[i].receiverExecutionGasLimit != 0) {
message.gasLimit = manualExecGasOverrides[i].receiverExecutionGasLimit;
}
} else {
// DON can only execute a message once
// Acceptable state transitions: UNTOUCHED->SUCCESS, UNTOUCHED->FAILURE
if (originalState != Internal.MessageExecutionState.UNTOUCHED) {
emit AlreadyAttempted(message.sequenceNumber);
continue;
}
}
if (message.nonce != 0) {
// In the scenario where we upgrade offRamps, we still want to have sequential nonces.
// Referencing the old offRamp to check the expected nonce if none is set for a
// given sender allows us to skip the current message if it would not be the next according
// to the old offRamp. This preserves sequencing between updates.
uint64 prevNonce = s_senderNonce[message.sender];
if (prevNonce == 0) {
if (i_prevOffRamp != address(0)) {
prevNonce = IAny2EVMOffRamp(i_prevOffRamp).getSenderNonce(message.sender);
if (prevNonce + 1 != message.nonce) {
// the starting v2 onramp nonce, i.e. the 1st message nonce v2 offramp is expected to receive,
// is guaranteed to equal (largest v1 onramp nonce + 1).
// if this message's nonce isn't (v1 offramp nonce + 1), then v1 offramp nonce != largest v1 onramp nonce,
// it tells us there are still messages inflight for v1 offramp
emit SkippedSenderWithPreviousRampMessageInflight(message.nonce, message.sender);
continue;
}
// Otherwise this nonce is indeed the "transitional nonce", that is
// all messages sent to v1 ramp have been executed by the DON and the sequence can resume in V2.
// Note if first time user in V2, then prevNonce will be 0, and message.nonce = 1, so this will be a no-op.
s_senderNonce[message.sender] = prevNonce;
}
}
// UNTOUCHED messages MUST be executed in order always IF message.nonce > 0.
if (originalState == Internal.MessageExecutionState.UNTOUCHED) {
if (prevNonce + 1 != message.nonce) {
// We skip the message if the nonce is incorrect, since message.nonce > 0.
emit SkippedIncorrectNonce(message.nonce, message.sender);
continue;
}
}
}
// Although we expect only valid messages will be committed, we check again
// when executing as a defense in depth measure.
bytes[] memory offchainTokenData = report.offchainTokenData[i];
_isWellFormed(
message.sequenceNumber,
message.sourceChainSelector,
message.tokenAmounts.length,
message.data.length,
offchainTokenData.length
);
_setExecutionState(message.sequenceNumber, Internal.MessageExecutionState.IN_PROGRESS);
(Internal.MessageExecutionState newState, bytes memory returnData) =
_trialExecute(message, offchainTokenData, tokenGasOverrides);
_setExecutionState(message.sequenceNumber, newState);
// Since it's hard to estimate whether manual execution will succeed, we
// revert the entire transaction if it fails. This will show the user if
// their manual exec will fail before they submit it.
if (manualExecution) {
if (newState == Internal.MessageExecutionState.FAILURE) {
if (originalState != Internal.MessageExecutionState.UNTOUCHED) {
// If manual execution fails, we revert the entire transaction, unless the originalState is UNTOUCHED as we
// would still be making progress by changing the state from UNTOUCHED to FAILURE.
revert ExecutionError(returnData);
}
}
}
// The only valid prior states are UNTOUCHED and FAILURE (checked above)
// The only valid post states are SUCCESS and FAILURE (checked below)
if (newState != Internal.MessageExecutionState.SUCCESS) {
if (newState != Internal.MessageExecutionState.FAILURE) {
revert InvalidNewState(message.sequenceNumber, newState);
}
}
// Nonce changes per state transition.
// These only apply for ordered messages.
// UNTOUCHED -> FAILURE nonce bump
// UNTOUCHED -> SUCCESS nonce bump
// FAILURE -> FAILURE no nonce bump
// FAILURE -> SUCCESS no nonce bump
if (message.nonce != 0) {
if (originalState == Internal.MessageExecutionState.UNTOUCHED) {
s_senderNonce[message.sender]++;
}
}
emit ExecutionStateChanged(message.sequenceNumber, message.messageId, newState, returnData);
}
}
/// @notice Does basic message validation. Should never fail.
/// @param sequenceNumber Sequence number of the message.
/// @param sourceChainSelector SourceChainSelector of the message.
/// @param numberOfTokens Length of tokenAmounts array in the message.
/// @param dataLength Length of data field in the message.
/// @param offchainTokenDataLength Length of offchainTokenData array.
/// @dev reverts on validation failures.
function _isWellFormed(
uint64 sequenceNumber,
uint64 sourceChainSelector,
uint256 numberOfTokens,
uint256 dataLength,
uint256 offchainTokenDataLength
) private view {
if (sourceChainSelector != i_sourceChainSelector) revert InvalidSourceChain(sourceChainSelector);
if (numberOfTokens > uint256(s_dynamicConfig.maxNumberOfTokensPerMsg)) {
revert UnsupportedNumberOfTokens(sequenceNumber);
}
if (numberOfTokens != offchainTokenDataLength) revert TokenDataMismatch(sequenceNumber);
if (dataLength > uint256(s_dynamicConfig.maxDataBytes)) {
revert MessageTooLarge(uint256(s_dynamicConfig.maxDataBytes), dataLength);
}
}
/// @notice Try executing a message.
/// @param message Internal.EVM2EVMMessage memory message.
/// @param offchainTokenData Data provided by the DON for token transfers.
/// @return the new state of the message, being either SUCCESS or FAILURE.
/// @return revert data in bytes if CCIP receiver reverted during execution.
function _trialExecute(
Internal.EVM2EVMMessage memory message,
bytes[] memory offchainTokenData,
uint32[] memory tokenGasOverrides
) internal returns (Internal.MessageExecutionState, bytes memory) {
try this.executeSingleMessage(message, offchainTokenData, tokenGasOverrides) {}
catch (bytes memory err) {
// return the message execution state as FAILURE and the revert data
// Max length of revert data is Router.MAX_RET_BYTES, max length of err is 4 + Router.MAX_RET_BYTES
return (Internal.MessageExecutionState.FAILURE, err);
}
// If message execution succeeded, no CCIP receiver return data is expected, return with empty bytes.
return (Internal.MessageExecutionState.SUCCESS, "");
}
/// @notice Execute a single message.
/// @param message The message that will be executed.
/// @param offchainTokenData Token transfer data to be passed to TokenPool.
/// @dev We make this external and callable by the contract itself, in order to try/catch
/// its execution and enforce atomicity among successful message processing and token transfer.
/// @dev We use ERC-165 to check for the ccipReceive interface to permit sending tokens to contracts
/// (for example smart contract wallets) without an associated message.
function executeSingleMessage(
Internal.EVM2EVMMessage calldata message,
bytes[] calldata offchainTokenData,
uint32[] memory tokenGasOverrides
) external {
if (msg.sender != address(this)) revert CanOnlySelfCall();
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](0);
if (message.tokenAmounts.length > 0) {
destTokenAmounts = _releaseOrMintTokens(
message.tokenAmounts,
abi.encode(message.sender),
message.receiver,
message.sourceTokenData,
offchainTokenData,
tokenGasOverrides
);
}
// There are three cases in which we skip calling the receiver:
// 1. If the message data is empty AND the gas limit is 0.
// This indicates a message that only transfers tokens. It is valid to only send tokens to a contract
// that supports the IAny2EVMMessageReceiver interface, but without this first check we would call the
// receiver without any gas, which would revert the transaction.
// 2. If the receiver is not a contract.
// 3. If the receiver is a contract but it does not support the IAny2EVMMessageReceiver interface.
//
// The ordering of these checks is important, as the first check is the cheapest to execute.
if (
(message.data.length == 0 && message.gasLimit == 0) || message.receiver.code.length == 0
|| !message.receiver.supportsInterface(type(IAny2EVMMessageReceiver).interfaceId)
) return;
(bool success, bytes memory returnData,) = IRouter(s_dynamicConfig.router).routeMessage(
Client.Any2EVMMessage({
messageId: message.messageId,
sourceChainSelector: message.sourceChainSelector,
sender: abi.encode(message.sender),
data: message.data,
destTokenAmounts: destTokenAmounts
}),
Internal.GAS_FOR_CALL_EXACT_CHECK,
message.gasLimit,
message.receiver
);
// If CCIP receiver execution is not successful, revert the call including token transfers
if (!success) revert ReceiverError(returnData);
}
/// @notice creates a unique hash to be used in message hashing.
function _metadataHash(bytes32 prefix) internal view returns (bytes32) {
return keccak256(abi.encode(prefix, i_sourceChainSelector, i_chainSelector, i_onRamp));
}
// ================================================================
// ¦ Config ¦
// ================================================================
/// @notice Returns the static config.
/// @dev This function will always return the same struct as the contents is static and can never change.
/// RMN depends on this function, if changing, please notify the RMN maintainers.
function getStaticConfig() external view returns (StaticConfig memory) {
return StaticConfig({
commitStore: i_commitStore,
chainSelector: i_chainSelector,
sourceChainSelector: i_sourceChainSelector,
onRamp: i_onRamp,
prevOffRamp: i_prevOffRamp,
rmnProxy: i_rmnProxy,
tokenAdminRegistry: i_tokenAdminRegistry
});
}
/// @notice Returns the current dynamic config.
/// @return The current config.
function getDynamicConfig() external view returns (DynamicConfig memory) {
return s_dynamicConfig;
}
/// @notice Sets the dynamic config. This function is called during `setOCR2Config` flow
function _beforeSetConfig(bytes memory onchainConfig) internal override {
DynamicConfig memory dynamicConfig = abi.decode(onchainConfig, (DynamicConfig));
if (dynamicConfig.router == address(0)) revert ZeroAddressNotAllowed();
s_dynamicConfig = dynamicConfig;
emit ConfigSet(
StaticConfig({
commitStore: i_commitStore,
chainSelector: i_chainSelector,
sourceChainSelector: i_sourceChainSelector,
onRamp: i_onRamp,
prevOffRamp: i_prevOffRamp,
rmnProxy: i_rmnProxy,
tokenAdminRegistry: i_tokenAdminRegistry
}),
dynamicConfig
);
}
/// @notice Get all tokens which are included in Aggregate Rate Limiting.
/// @return sourceTokens The source representation of the tokens that are rate limited.
/// @return destTokens The destination representation of the tokens that are rate limited.
/// @dev the order of IDs in the list is **not guaranteed**, therefore, if ordering matters when
/// making successive calls, one should keep the block height constant to ensure a consistent result.
function getAllRateLimitTokens() external view returns (address[] memory sourceTokens, address[] memory destTokens) {
uint256 numRateLimitedTokens = s_rateLimitedTokensDestToSource.length();
sourceTokens = new address[](numRateLimitedTokens);
destTokens = new address[](numRateLimitedTokens);
for (uint256 i = 0; i < numRateLimitedTokens; ++i) {
(address destToken, address sourceToken) = s_rateLimitedTokensDestToSource.at(i);
sourceTokens[i] = sourceToken;
destTokens[i] = destToken;
}
return (sourceTokens, destTokens);
}
/// @notice Adds or removes tokens from being used in Aggregate Rate Limiting.
/// @param removes - A list of one or more tokens to be removed.
/// @param adds - A list of one or more tokens to be added.
function updateRateLimitTokens(RateLimitToken[] memory removes, RateLimitToken[] memory adds) external onlyOwner {
for (uint256 i = 0; i < removes.length; ++i) {
if (s_rateLimitedTokensDestToSource.remove(removes[i].destToken)) {
emit TokenAggregateRateLimitRemoved(removes[i].sourceToken, removes[i].destToken);
}
}
for (uint256 i = 0; i < adds.length; ++i) {
if (s_rateLimitedTokensDestToSource.set(adds[i].destToken, adds[i].sourceToken)) {
emit TokenAggregateRateLimitAdded(adds[i].sourceToken, adds[i].destToken);
}
}
}
// ================================================================
// ¦ Tokens and pools ¦
// ================================================================
/// @notice Uses a pool to release or mint a token to a receiver address, with balance checks before and after the
/// transfer. This is done to ensure the exact number of tokens the pool claims to release are actually transferred.
/// @dev The local token address is validated through the TokenAdminRegistry. If, due to some misconfiguration, the
/// token is unknown to the registry, the offRamp will revert. The tx, and the tokens, can be retrieved by
/// registering the token on this chain, and re-trying the msg.
/// @param sourceAmount The amount of tokens to be released/minted.
/// @param originalSender The message sender on the source chain.
/// @param receiver The address that will receive the tokens.
/// @param sourceTokenData A struct containing the local token address, the source pool address and optional data
/// returned from the source pool.
/// @param offchainTokenData Data fetched offchain by the DON.
function _releaseOrMintToken(
uint256 sourceAmount,
bytes memory originalSender,
address receiver,
Internal.SourceTokenData memory sourceTokenData,
bytes memory offchainTokenData
) internal returns (Client.EVMTokenAmount memory destTokenAmount) {
// We need to safely decode the token address from the sourceTokenData, as it could be wrong,
// in which case it doesn't have to be a valid EVM address.
address localToken = Internal._validateEVMAddress(sourceTokenData.destTokenAddress);
// We check with the token admin registry if the token has a pool on this chain.
address localPoolAddress = ITokenAdminRegistry(i_tokenAdminRegistry).getPool(localToken);
// This will call the supportsInterface through the ERC165Checker, and not directly on the pool address.
// This is done to prevent a pool from reverting the entire transaction if it doesn't support the interface.
// The call gets a max or 30k gas per instance, of which there are three. This means gas estimations should
// account for 90k gas overhead due to the interface check.
if (localPoolAddress == address(0) || !localPoolAddress.supportsInterface(Pool.CCIP_POOL_V1)) {
revert NotACompatiblePool(localPoolAddress);
}
// We retrieve the local token balance of the receiver before the pool call.
(uint256 balancePre, uint256 gasLeft) = _getBalanceOfReceiver(receiver, localToken, sourceTokenData.destGasAmount);
// We determined that the pool address is a valid EVM address, but that does not mean the code at this
// address is a (compatible) pool contract. _callWithExactGasSafeReturnData will check if the location
// contains a contract. If it doesn't it reverts with a known error, which we catch gracefully.
// We call the pool with exact gas to increase resistance against malicious tokens or token pools.
// We protects against return data bombs by capping the return data size at MAX_RET_BYTES.
(bool success, bytes memory returnData, uint256 gasUsedReleaseOrMint) = CallWithExactGas
._callWithExactGasSafeReturnData(
abi.encodeCall(
IPoolV1.releaseOrMint,
Pool.ReleaseOrMintInV1({
originalSender: originalSender,
receiver: receiver,
amount: sourceAmount,
localToken: localToken,
remoteChainSelector: i_sourceChainSelector,
sourcePoolAddress: sourceTokenData.sourcePoolAddress,
sourcePoolData: sourceTokenData.extraData,
offchainTokenData: offchainTokenData
})
),
localPoolAddress,
gasLeft,
Internal.GAS_FOR_CALL_EXACT_CHECK,
Internal.MAX_RET_BYTES
);
// wrap and rethrow the error so we can catch it lower in the stack
if (!success) revert TokenHandlingError(returnData);
// If the call was successful, the returnData should contain only the local token amount.
if (returnData.length != Pool.CCIP_POOL_V1_RET_BYTES) {
revert InvalidDataLength(Pool.CCIP_POOL_V1_RET_BYTES, returnData.length);
}
uint256 localAmount = abi.decode(returnData, (uint256));
// We don't need to do balance checks if the pool is the receiver, as they would always fail in the case
// of a lockRelease pool.
if (receiver != localPoolAddress) {
(uint256 balancePost,) = _getBalanceOfReceiver(receiver, localToken, gasLeft - gasUsedReleaseOrMint);
// First we check if the subtraction would result in an underflow to ensure we revert with a clear error
if (balancePost < balancePre || balancePost - balancePre != localAmount) {
revert ReleaseOrMintBalanceMismatch(localAmount, balancePre, balancePost);
}
}
return Client.EVMTokenAmount({token: localToken, amount: localAmount});
}
function _getBalanceOfReceiver(
address receiver,
address token,
uint256 gasLimit
) internal returns (uint256 balance, uint256 gasLeft) {
(bool success, bytes memory returnData, uint256 gasUsed) = CallWithExactGas._callWithExactGasSafeReturnData(
abi.encodeCall(IERC20.balanceOf, (receiver)),
token,
gasLimit,
Internal.GAS_FOR_CALL_EXACT_CHECK,
Internal.MAX_RET_BYTES
);
if (!success) revert TokenHandlingError(returnData);
// If the call was successful, the returnData should contain only the balance.
if (returnData.length != Internal.MAX_BALANCE_OF_RET_BYTES) {
revert InvalidDataLength(Internal.MAX_BALANCE_OF_RET_BYTES, returnData.length);
}
// Return the decoded balance, which cannot fail as we checked the length, and the gas that is left
// after this call.
return (abi.decode(returnData, (uint256)), gasLimit - gasUsed);
}
/// @notice Uses pools to release or mint a number of different tokens to a receiver address.
/// @param sourceTokenAmounts List of tokens and amount values to be released/minted.
/// @param originalSender The message sender.
/// @param receiver The address that will receive the tokens.
/// @param encodedSourceTokenData Array of token data returned by token pools on the source chain.
/// @param offchainTokenData Array of token data fetched offchain by the DON.
/// @dev This function wrappes the token pool call in a try catch block to gracefully handle
/// any non-rate limiting errors that may occur. If we encounter a rate limiting related error
/// we bubble it up. If we encounter a non-rate limiting error we wrap it in a TokenHandlingError.
function _releaseOrMintTokens(
Client.EVMTokenAmount[] calldata sourceTokenAmounts,
bytes memory originalSender,
address receiver,
bytes[] calldata encodedSourceTokenData,
bytes[] calldata offchainTokenData,
uint32[] memory tokenGasOverrides
) internal returns (Client.EVMTokenAmount[] memory destTokenAmounts) {
// Creating a copy is more gas efficient than initializing a new array.
destTokenAmounts = sourceTokenAmounts;
uint256 value = 0;
for (uint256 i = 0; i < sourceTokenAmounts.length; ++i) {
Internal.SourceTokenData memory sourceTokenData =
abi.decode(encodedSourceTokenData[i], (Internal.SourceTokenData));
if (tokenGasOverrides.length != 0) {
if (tokenGasOverrides[i] != 0) {
sourceTokenData.destGasAmount = tokenGasOverrides[i];
}
}
destTokenAmounts[i] = _releaseOrMintToken(
sourceTokenAmounts[i].amount,
originalSender,
receiver,
// This should never revert as the onRamp encodes the sourceTokenData struct. Only the inner components from
// this struct come from untrusted sources.
sourceTokenData,
offchainTokenData[i]
);
if (s_rateLimitedTokensDestToSource.contains(destTokenAmounts[i].token)) {
value += _getTokenValue(destTokenAmounts[i], IPriceRegistry(s_dynamicConfig.priceRegistry));
}
}
if (value > 0) _rateLimitValue(value);
return destTokenAmounts;
}
// ================================================================
// ¦ Access ¦
// ================================================================
/// @notice Reverts as this contract should not access CCIP messages
function ccipReceive(Client.Any2EVMMessage calldata) external pure {
// solhint-disable-next-line
revert();
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;
import {IPriceRegistry} from "./interfaces/IPriceRegistry.sol";
import {OwnerIsCreator} from "./../shared/access/OwnerIsCreator.sol";
import {Client} from "./libraries/Client.sol";
import {RateLimiter} from "./libraries/RateLimiter.sol";
import {USDPriceWith18Decimals} from "./libraries/USDPriceWith18Decimals.sol";
/// @notice The aggregate rate limiter is a wrapper of the token bucket rate limiter
/// which permits rate limiting based on the aggregate value of a group of
/// token transfers, using a price registry to convert to a numeraire asset (e.g. USD).
contract AggregateRateLimiter is OwnerIsCreator {
using RateLimiter for RateLimiter.TokenBucket;
using USDPriceWith18Decimals for uint224;
error PriceNotFoundForToken(address token);
event AdminSet(address newAdmin);
// The address of the token limit admin that has the same permissions as the owner.
address internal s_admin;
// The token bucket object that contains the bucket state.
RateLimiter.TokenBucket private s_rateLimiter;
/// @param config The RateLimiter.Config
constructor(RateLimiter.Config memory config) {
s_rateLimiter = RateLimiter.TokenBucket({
rate: config.rate,
capacity: config.capacity,
tokens: config.capacity,
lastUpdated: uint32(block.timestamp),
isEnabled: config.isEnabled
});
}
/// @notice Consumes value from the rate limiter bucket based on the token value given.
function _rateLimitValue(uint256 value) internal {
s_rateLimiter._consume(value, address(0));
}
function _getTokenValue(
Client.EVMTokenAmount memory tokenAmount,
IPriceRegistry priceRegistry
) internal view returns (uint256) {
// not fetching validated price, as price staleness is not important for value-based rate limiting
// we only need to verify the price is not 0
uint224 pricePerToken = priceRegistry.getTokenPrice(tokenAmount.token).value;
if (pricePerToken == 0) revert PriceNotFoundForToken(tokenAmount.token);
return pricePerToken._calcUSDValueFromTokenAmount(tokenAmount.amount);
}
/// @notice Gets the token bucket with its values for the block it was requested at.
/// @return The token bucket.
function currentRateLimiterState() external view returns (RateLimiter.TokenBucket memory) {
return s_rateLimiter._currentTokenBucketState();
}
/// @notice Sets the rate limited config.
/// @param config The new rate limiter config.
/// @dev should only be callable by the owner or token limit admin.
function setRateLimiterConfig(RateLimiter.Config memory config) external onlyAdminOrOwner {
s_rateLimiter._setTokenBucketConfig(config);
}
// ================================================================
// ¦ Access ¦
// ================================================================
/// @notice Gets the token limit admin address.
/// @return the token limit admin address.
function getTokenLimitAdmin() external view returns (address) {
return s_admin;
}
/// @notice Sets the token limit admin address.
/// @param newAdmin the address of the new admin.
/// @dev setting this to address(0) indicates there is no active admin.
function setAdmin(address newAdmin) external onlyAdminOrOwner {
s_admin = newAdmin;
emit AdminSet(newAdmin);
}
/// @notice a modifier that allows the owner or the s_tokenLimitAdmin call the functions
/// it is applied to.
modifier onlyAdminOrOwner() {
if (msg.sender != owner() && msg.sender != s_admin) revert RateLimiter.OnlyCallableByAdminOrOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Client} from "../libraries/Client.sol";
/// @notice Application contracts that intend to receive messages from
/// the router should implement this interface.
interface IAny2EVMMessageReceiver {
/// @notice Called by the Router to deliver a message.
/// If this reverts, any token transfers also revert. The message
/// will move to a FAILED state and become available for manual execution.
/// @param message CCIP Message
/// @dev Note ensure you check the msg.sender is the OffRampRouter
function ccipReceive(Client.Any2EVMMessage calldata message) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IAny2EVMOffRamp {
/// @notice Returns the the current nonce for a receiver.
/// @param sender The sender address
/// @return nonce The nonce value belonging to the sender address.
function getSenderNonce(address sender) external view returns (uint64 nonce);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ICommitStore {
/// @notice Returns timestamp of when root was accepted or 0 if verification fails.
/// @dev This method uses a merkle tree within a merkle tree, with the hashedLeaves,
/// proofs and proofFlagBits being used to get the root of the inner tree.
/// This root is then used as the singular leaf of the outer tree.
function verify(
bytes32[] calldata hashedLeaves,
bytes32[] calldata proofs,
uint256 proofFlagBits
) external view returns (uint256 timestamp);
/// @notice Returns the expected next sequence number
function getExpectedNextSequenceNumber() external view returns (uint64 sequenceNumber);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Pool} from "../libraries/Pool.sol";
import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol";
/// @notice Shared public interface for multiple V1 pool types.
/// Each pool type handles a different child token model (lock/unlock, mint/burn.)
interface IPoolV1 is IERC165 {
/// @notice Lock tokens into the pool or burn the tokens.
/// @param lockOrBurnIn Encoded data fields for the processing of tokens on the source chain.
/// @return lockOrBurnOut Encoded data fields for the processing of tokens on the destination chain.
function lockOrBurn(Pool.LockOrBurnInV1 calldata lockOrBurnIn)
external
returns (Pool.LockOrBurnOutV1 memory lockOrBurnOut);
/// @notice Releases or mints tokens to the receiver address.
/// @param releaseOrMintIn All data required to release or mint tokens.
/// @return releaseOrMintOut The amount of tokens released or minted on the local chain, denominated
/// in the local token's decimals.
/// @dev The offramp asserts that the balanceOf of the receiver has been incremented by exactly the number
/// of tokens that is returned in ReleaseOrMintOutV1.destinationAmount. If the amounts do not match, the tx reverts.
function releaseOrMint(Pool.ReleaseOrMintInV1 calldata releaseOrMintIn)
external
returns (Pool.ReleaseOrMintOutV1 memory);
/// @notice Checks whether a remote chain is supported in the token pool.
/// @param remoteChainSelector The selector of the remote chain.
/// @return true if the given chain is a permissioned remote chain.
function isSupportedChain(uint64 remoteChainSelector) external view returns (bool);
/// @notice Returns if the token pool supports the given token.
/// @param token The address of the token.
/// @return true if the token is supported by the pool.
function isSupportedToken(address token) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Client} from "../libraries/Client.sol";
import {Internal} from "../libraries/Internal.sol";
interface IPriceRegistry {
/// @notice Token price data feed configuration
struct TokenPriceFeedConfig {
address dataFeedAddress; // --? AggregatorV3Interface contract (0 - feed is unset)
uint8 tokenDecimals; // ------? Decimals of the token that the feed represents
}
/// @notice Update the price for given tokens and gas prices for given chains.
/// @param priceUpdates The price updates to apply.
function updatePrices(Internal.PriceUpdates memory priceUpdates) external;
/// @notice Get the `tokenPrice` for a given token.
/// @param token The token to get the price for.
/// @return tokenPrice The tokenPrice for the given token.
function getTokenPrice(address token) external view returns (Internal.TimestampedPackedUint224 memory);
/// @notice Get the `tokenPrice` for a given token, checks if the price is valid.
/// @param token The token to get the price for.
/// @return tokenPrice The tokenPrice for the given token if it exists and is valid.
function getValidatedTokenPrice(address token) external view returns (uint224);
/// @notice Get the `tokenPrice` for an array of tokens.
/// @param tokens The tokens to get prices for.
/// @return tokenPrices The tokenPrices for the given tokens.
function getTokenPrices(address[] calldata tokens) external view returns (Internal.TimestampedPackedUint224[] memory);
/// @notice Returns the token price data feed configuration
/// @param token The token to retrieve the feed config for
/// @return dataFeedAddress The token price data feed config (if feed address is 0, the feed config is disabled)
function getTokenPriceFeedConfig(address token) external view returns (TokenPriceFeedConfig memory);
/// @notice Get an encoded `gasPrice` for a given destination chain ID.
/// The 224-bit result encodes necessary gas price components.
/// On L1 chains like Ethereum or Avax, the only component is the gas price.
/// On Optimistic Rollups, there are two components - the L2 gas price, and L1 base fee for data availability.
/// On future chains, there could be more or differing price components.
/// PriceRegistry does not contain chain-specific logic to parse destination chain price components.
/// @param destChainSelector The destination chain to get the price for.
/// @return gasPrice The encoded gasPrice for the given destination chain ID.
function getDestinationChainGasPrice(uint64 destChainSelector)
external
view
returns (Internal.TimestampedPackedUint224 memory);
/// @notice Gets the fee token price and the gas price, both denominated in dollars.
/// @param token The source token to get the price for.
/// @param destChainSelector The destination chain to get the gas price for.
/// @return tokenPrice The price of the feeToken in 1e18 dollars per base unit.
/// @return gasPrice The price of gas in 1e18 dollars per base unit.
function getTokenAndGasPrices(
address token,
uint64 destChainSelector
) external view returns (uint224 tokenPrice, uint224 gasPrice);
/// @notice Convert a given token amount to target token amount.
/// @param fromToken The given token address.
/// @param fromTokenAmount The given token amount.
/// @param toToken The target token address.
/// @return toTokenAmount The target token amount.
function convertTokenAmount(
address fromToken,
uint256 fromTokenAmount,
address toToken
) external view returns (uint256 toTokenAmount);
/// @notice Get the list of fee tokens.
/// @return The tokens set as fee tokens.
function getFeeTokens() external view returns (address[] memory);
/// @notice Validates the ccip message & returns the fee
/// @param destChainSelector The destination chain selector.
/// @param message The message to get quote for.
/// @return feeTokenAmount The amount of fee token needed for the fee, in smallest denomination of the fee token.
function getValidatedFee(
uint64 destChainSelector,
Client.EVM2AnyMessage calldata message
) external view returns (uint256 feeTokenAmount);
/// @notice Converts the extraArgs to the latest version and returns the converted message fee in juels
/// @param destChainSelector destination chain selector to process
/// @param feeToken Fee token address used to pay for message fees
/// @param feeTokenAmount Fee token amount
/// @param extraArgs Message extra args that were passed in by the client
/// @return msgFeeJuels message fee in juels
/// @return isOutOfOrderExecution true if the message should be executed out of order
/// @return convertedExtraArgs extra args converted to the latest family-specific args version
function processMessageArgs(
uint64 destChainSelector,
address feeToken,
uint256 feeTokenAmount,
bytes memory extraArgs
) external view returns (uint256 msgFeeJuels, bool isOutOfOrderExecution, bytes memory convertedExtraArgs);
/// @notice Validates pool return data
/// @param destChainSelector Destination chain selector to which the token amounts are sent to
/// @param rampTokenAmounts Token amounts with populated pool return data
/// @param sourceTokenAmounts Token amounts originally sent in a Client.EVM2AnyMessage message
function validatePoolReturnData(
uint64 destChainSelector,
Internal.RampTokenAmount[] calldata rampTokenAmounts,
Client.EVMTokenAmount[] calldata sourceTokenAmounts
) external view;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice This interface contains the only RMN-related functions that might be used on-chain by other CCIP contracts.
interface IRMN {
/// @notice A Merkle root tagged with the address of the commit store contract it is destined for.
struct TaggedRoot {
address commitStore;
bytes32 root;
}
/// @notice Callers MUST NOT cache the return value as a blessed tagged root could become unblessed.
function isBlessed(TaggedRoot calldata taggedRoot) external view returns (bool);
/// @notice Iff there is an active global or legacy curse, this function returns true.
function isCursed() external view returns (bool);
/// @notice Iff there is an active global curse, or an active curse for `subject`, this function returns true.
/// @param subject To check whether a particular chain is cursed, set to bytes16(uint128(chainSelector)).
function isCursed(bytes16 subject) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Client} from "../libraries/Client.sol";
interface IRouter {
error OnlyOffRamp();
/// @notice Route the message to its intended receiver contract.
/// @param message Client.Any2EVMMessage struct.
/// @param gasForCallExactCheck of params for exec
/// @param gasLimit set of params for exec
/// @param receiver set of params for exec
/// @dev if the receiver is a contracts that signals support for CCIP execution through EIP-165.
/// the contract is called. If not, only tokens are transferred.
/// @return success A boolean value indicating whether the ccip message was received without errors.
/// @return retBytes A bytes array containing return data form CCIP receiver.
/// @return gasUsed the gas used by the external customer call. Does not include any overhead.
function routeMessage(
Client.Any2EVMMessage calldata message,
uint16 gasForCallExactCheck,
uint256 gasLimit,
address receiver
) external returns (bool success, bytes memory retBytes, uint256 gasUsed);
/// @notice Returns the configured onramp for a specific destination chain.
/// @param destChainSelector The destination chain Id to get the onRamp for.
/// @return onRampAddress The address of the onRamp.
function getOnRamp(uint64 destChainSelector) external view returns (address onRampAddress);
/// @notice Return true if the given offRamp is a configured offRamp for the given source chain.
/// @param sourceChainSelector The source chain selector to check.
/// @param offRamp The address of the offRamp to check.
function isOffRamp(uint64 sourceChainSelector, address offRamp) external view returns (bool isOffRamp);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
interface ITokenAdminRegistry {
/// @notice Returns the pool for the given token.
function getPool(address token) external view returns (address);
/// @notice Proposes an administrator for the given token as pending administrator.
/// @param localToken The token to register the administrator for.
/// @param administrator The administrator to register.
function proposeAdministrator(address localToken, address administrator) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// End consumer library.
library Client {
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct EVMTokenAmount {
address token; // token address on the local chain.
uint256 amount; // Amount of tokens.
}
struct Any2EVMMessage {
bytes32 messageId; // MessageId corresponding to ccipSend on source.
uint64 sourceChainSelector; // Source chain selector.
bytes sender; // abi.decode(sender) if coming from an EVM chain.
bytes data; // payload sent in original message.
EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.
}
// If extraArgs is empty bytes, the default is 200k gas limit.
struct EVM2AnyMessage {
bytes receiver; // abi.encode(receiver address) for dest EVM chains
bytes data; // Data payload
EVMTokenAmount[] tokenAmounts; // Token transfers
address feeToken; // Address of feeToken. address(0) means you will send msg.value.
bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV2)
}
// bytes4(keccak256("CCIP EVMExtraArgsV1"));
bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;
struct EVMExtraArgsV1 {
uint256 gasLimit;
}
function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);
}
// bytes4(keccak256("CCIP EVMExtraArgsV2"));
bytes4 public constant EVM_EXTRA_ARGS_V2_TAG = 0x181dcf10;
/// @param gasLimit: gas limit for the callback on the destination chain.
/// @param allowOutOfOrderExecution: if true, it indicates that the message can be executed in any order relative to other messages from the same sender.
/// This value's default varies by chain. On some chains, a particular value is enforced, meaning if the expected value
/// is not set, the message request will revert.
struct EVMExtraArgsV2 {
uint256 gasLimit;
bool allowOutOfOrderExecution;
}
function _argsToBytes(EVMExtraArgsV2 memory extraArgs) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EVM_EXTRA_ARGS_V2_TAG, extraArgs);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {MerkleMultiProof} from "../libraries/MerkleMultiProof.sol";
import {Client} from "./Client.sol";
// Library for CCIP internal definitions common to multiple contracts.
library Internal {
error InvalidEVMAddress(bytes encodedAddress);
/// @dev The minimum amount of gas to perform the call with exact gas.
/// We include this in the offramp so that we can redeploy to adjust it
/// should a hardfork change the gas costs of relevant opcodes in callWithExactGas.
uint16 internal constant GAS_FOR_CALL_EXACT_CHECK = 5_000;
// @dev We limit return data to a selector plus 4 words. This is to avoid
// malicious contracts from returning large amounts of data and causing
// repeated out-of-gas scenarios.
uint16 internal constant MAX_RET_BYTES = 4 + 4 * 32;
/// @dev The expected number of bytes returned by the balanceOf function.
uint256 internal constant MAX_BALANCE_OF_RET_BYTES = 32;
/// @notice A collection of token price and gas price updates.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct PriceUpdates {
TokenPriceUpdate[] tokenPriceUpdates;
GasPriceUpdate[] gasPriceUpdates;
}
/// @notice Token price in USD.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct TokenPriceUpdate {
address sourceToken; // Source token
uint224 usdPerToken; // 1e18 USD per 1e18 of the smallest token denomination.
}
/// @notice Gas price for a given chain in USD, its value may contain tightly packed fields.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct GasPriceUpdate {
uint64 destChainSelector; // Destination chain selector
uint224 usdPerUnitGas; // 1e18 USD per smallest unit (e.g. wei) of destination chain gas
}
/// @notice A timestamped uint224 value that can contain several tightly packed fields.
struct TimestampedPackedUint224 {
uint224 value; // -------? Value in uint224, packed.
uint32 timestamp; // ----? Timestamp of the most recent price update.
}
/// @dev Gas price is stored in 112-bit unsigned int. uint224 can pack 2 prices.
/// When packing L1 and L2 gas prices, L1 gas price is left-shifted to the higher-order bits.
/// Using uint8 type, which cannot be higher than other bit shift operands, to avoid shift operand type warning.
uint8 public constant GAS_PRICE_BITS = 112;
struct PoolUpdate {
address token; // The IERC20 token address
address pool; // The token pool address
}
struct SourceTokenData {
// The source pool address, abi encoded. This value is trusted as it was obtained through the onRamp. It can be
// relied upon by the destination pool to validate the source pool.
bytes sourcePoolAddress;
// The address of the destination token, abi encoded in the case of EVM chains
// This value is UNTRUSTED as any pool owner can return whatever value they want.
bytes destTokenAddress;
// Optional pool data to be transferred to the destination chain. Be default this is capped at
// CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
// has to be set for the specific token.
bytes extraData;
uint32 destGasAmount; // The amount of gas available for the releaseOrMint and balanceOf calls on the offRamp
}
/// @notice Report that is submitted by the execution DON at the execution phase. (including chain selector data)
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct ExecutionReportSingleChain {
uint64 sourceChainSelector; // Source chain selector for which the report is submitted
Any2EVMRampMessage[] messages;
// Contains a bytes array for each message, each inner bytes array contains bytes per transferred token
bytes[][] offchainTokenData;
bytes32[] proofs;
uint256 proofFlagBits;
}
/// @notice Report that is submitted by the execution DON at the execution phase.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct ExecutionReport {
EVM2EVMMessage[] messages;
// Contains a bytes array for each message, each inner bytes array contains bytes per transferred token
bytes[][] offchainTokenData;
bytes32[] proofs;
uint256 proofFlagBits;
}
/// @notice The cross chain message that gets committed to EVM chains.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct EVM2EVMMessage {
uint64 sourceChainSelector; // --------? the chain selector of the source chain, note: not chainId
address sender; // --------------------? sender address on the source chain
address receiver; // ------------------? receiver address on the destination chain
uint64 sequenceNumber; // -------------? sequence number, not unique across lanes
uint256 gasLimit; // user supplied maximum gas amount available for dest chain execution
bool strict; // -----------------------? DEPRECATED
uint64 nonce; // ¦ nonce for this lane for this sender, not unique across senders/lanes
address feeToken; // ------------------? fee token
uint256 feeTokenAmount; // fee token amount
bytes data; // arbitrary data payload supplied by the message sender
Client.EVMTokenAmount[] tokenAmounts; // array of tokens and amounts to transfer
bytes[] sourceTokenData; // array of token data, one per token
bytes32 messageId; // a hash of the message data
}
/// @dev EVM2EVMMessage struct has 13 fields, including 3 variable arrays.
/// Each variable array takes 1 more slot to store its length.
/// When abi encoded, excluding array contents,
/// EVM2EVMMessage takes up a fixed number of 16 lots, 32 bytes each.
/// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 17.
uint256 public constant MESSAGE_FIXED_BYTES = 32 * 17;
/// @dev Each token transfer adds 1 EVMTokenAmount and 3 bytes at 3 slots each and one slot for the destGasAmount.
/// When abi encoded, each EVMTokenAmount takes 2 slots, each bytes takes 1 slot for length, one slot of data and one
/// slot for the offset. This results in effectively 3*3 slots per SourceTokenData.
/// 0x20
/// destGasAmount
/// sourcePoolAddress_offset
/// destTokenAddress_offset
/// extraData_offset
/// sourcePoolAddress_length
/// sourcePoolAddress_content // assume 1 slot
/// destTokenAddress_length
/// destTokenAddress_content // assume 1 slot
/// extraData_length // contents billed separately
uint256 public constant MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * ((1 + 3 * 3) + 2);
/// @dev Any2EVMRampMessage struct has 10 fields, including 3 variable unnested arrays (data, receiver and tokenAmounts).
/// Each variable array takes 1 more slot to store its length.
/// When abi encoded, excluding array contents,
/// Any2EVMMessage takes up a fixed number of 13 slots, 32 bytes each.
/// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 14.
/// The fixed bytes does not cover struct data (this is represented by ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN)
uint256 public constant ANY_2_EVM_MESSAGE_FIXED_BYTES = 32 * 14;
/// @dev Each token transfer adds 1 RampTokenAmount
/// RampTokenAmount has 4 fields, including 3 bytes.
/// Each bytes takes 1 more slot to store its length, and one slot to store the offset.
/// When abi encoded, each token transfer takes up 10 slots, excl bytes contents.
uint256 public constant ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * 10;
bytes32 internal constant EVM_2_EVM_MESSAGE_HASH = keccak256("EVM2EVMMessageHashV2");
/// @dev Used to hash messages for single-lane ramps.
/// OnRamp hash(EVM2EVMMessage) = OffRamp hash(EVM2EVMMessage)
/// The EVM2EVMMessage's messageId is expected to be the output of this hash function
/// @param original Message to hash
/// @param metadataHash Immutable metadata hash representing a lane with a fixed OnRamp
/// @return hashedMessage hashed message as a keccak256
function _hash(EVM2EVMMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) {
// Fixed-size message fields are included in nested hash to reduce stack pressure.
// This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers.
return keccak256(
abi.encode(
MerkleMultiProof.LEAF_DOMAIN_SEPARATOR,
metadataHash,
keccak256(
abi.encode(
original.sender,
original.receiver,
original.sequenceNumber,
original.gasLimit,
original.strict,
original.nonce,
original.feeToken,
original.feeTokenAmount
)
),
keccak256(original.data),
keccak256(abi.encode(original.tokenAmounts)),
keccak256(abi.encode(original.sourceTokenData))
)
);
}
bytes32 internal constant ANY_2_EVM_MESSAGE_HASH = keccak256("Any2EVMMessageHashV1");
bytes32 internal constant EVM_2_ANY_MESSAGE_HASH = keccak256("EVM2AnyMessageHashV1");
/// @dev Used to hash messages for multi-lane family-agnostic OffRamps.
/// OnRamp hash(EVM2AnyMessage) != Any2EVMRampMessage.messageId
/// OnRamp hash(EVM2AnyMessage) != OffRamp hash(Any2EVMRampMessage)
/// @param original OffRamp message to hash
/// @param onRamp OnRamp to hash the message with - used to compute the metadataHash
/// @return hashedMessage hashed message as a keccak256
function _hash(Any2EVMRampMessage memory original, bytes memory onRamp) internal pure returns (bytes32) {
// Fixed-size message fields are included in nested hash to reduce stack pressure.
// This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers.
return keccak256(
abi.encode(
MerkleMultiProof.LEAF_DOMAIN_SEPARATOR,
// Implicit metadata hash
keccak256(
abi.encode(
ANY_2_EVM_MESSAGE_HASH, original.header.sourceChainSelector, original.header.destChainSelector, onRamp
)
),
keccak256(
abi.encode(
original.header.messageId,
original.sender,
original.receiver,
original.header.sequenceNumber,
original.gasLimit,
original.header.nonce
)
),
keccak256(original.data),
keccak256(abi.encode(original.tokenAmounts))
)
);
}
function _hash(EVM2AnyRampMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) {
// Fixed-size message fields are included in nested hash to reduce stack pressure.
// This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers.
return keccak256(
abi.encode(
MerkleMultiProof.LEAF_DOMAIN_SEPARATOR,
metadataHash,
keccak256(
abi.encode(
original.sender,
original.receiver,
original.header.sequenceNumber,
original.header.nonce,
original.feeToken,
original.feeTokenAmount
)
),
keccak256(original.data),
keccak256(abi.encode(original.tokenAmounts)),
keccak256(original.extraArgs)
)
);
}
/// @dev We disallow the first 1024 addresses to avoid calling into a range known for hosting precompiles. Calling
/// into precompiles probably won't cause any issues, but to be safe we can disallow this range. It is extremely
/// unlikely that anyone would ever be able to generate an address in this range. There is no official range of
/// precompiles, but EIP-7587 proposes to reserve the range 0x100 to 0x1ff. Our range is more conservative, even
/// though it might not be exhaustive for all chains, which is OK. We also disallow the zero address, which is a
/// common practice.
uint256 public constant PRECOMPILE_SPACE = 1024;
/// @notice This methods provides validation for parsing abi encoded addresses by ensuring the
/// address is within the EVM address space. If it isn't it will revert with an InvalidEVMAddress error, which
/// we can catch and handle more gracefully than a revert from abi.decode.
/// @return The address if it is valid, the function will revert otherwise.
function _validateEVMAddress(bytes memory encodedAddress) internal pure returns (address) {
if (encodedAddress.length != 32) revert InvalidEVMAddress(encodedAddress);
uint256 encodedAddressUint = abi.decode(encodedAddress, (uint256));
if (encodedAddressUint > type(uint160).max || encodedAddressUint < PRECOMPILE_SPACE) {
revert InvalidEVMAddress(encodedAddress);
}
return address(uint160(encodedAddressUint));
}
/// @notice Enum listing the possible message execution states within
/// the offRamp contract.
/// UNTOUCHED never executed
/// IN_PROGRESS currently being executed, used a replay protection
/// SUCCESS successfully executed. End state
/// FAILURE unsuccessfully executed, manual execution is now enabled.
/// @dev RMN depends on this enum, if changing, please notify the RMN maintainers.
enum MessageExecutionState {
UNTOUCHED,
IN_PROGRESS,
SUCCESS,
FAILURE
}
/// @notice CCIP OCR plugin type, used to separate execution & commit transmissions and configs
enum OCRPluginType {
Commit,
Execution
}
/// @notice Family-agnostic token amounts used for both OnRamp & OffRamp messages
struct RampTokenAmount {
// The source pool address, abi encoded. This value is trusted as it was obtained through the onRamp. It can be
// relied upon by the destination pool to validate the source pool.
bytes sourcePoolAddress;
// The address of the destination token, abi encoded in the case of EVM chains
// This value is UNTRUSTED as any pool owner can return whatever value they want.
bytes destTokenAddress;
// Optional pool data to be transferred to the destination chain. Be default this is capped at
// CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
// has to be set for the specific token.
bytes extraData;
uint256 amount; // Amount of tokens.
}
/// @notice Family-agnostic header for OnRamp & OffRamp messages.
/// The messageId is not expected to match hash(message), since it may originate from another ramp family
struct RampMessageHeader {
bytes32 messageId; // Unique identifier for the message, generated with the source chain's encoding scheme (i.e. not necessarily abi.encoded)
uint64 sourceChainSelector; // --? the chain selector of the source chain, note: not chainId
uint64 destChainSelector; // | the chain selector of the destination chain, note: not chainId
uint64 sequenceNumber; // ¦ sequence number, not unique across lanes
uint64 nonce; // ----------------? nonce for this lane for this sender, not unique across senders/lanes
}
/// @notice Family-agnostic message routed to an OffRamp
/// Note: hash(Any2EVMRampMessage) != hash(EVM2AnyRampMessage), hash(Any2EVMRampMessage) != messageId
/// due to encoding & parameter differences
struct Any2EVMRampMessage {
RampMessageHeader header; // Message header
bytes sender; // sender address on the source chain
bytes data; // arbitrary data payload supplied by the message sender
address receiver; // receiver address on the destination chain
uint256 gasLimit; // user supplied maximum gas amount available for dest chain execution
RampTokenAmount[] tokenAmounts; // array of tokens and amounts to transfer
}
/// @notice Family-agnostic message emitted from the OnRamp
/// Note: hash(Any2EVMRampMessage) != hash(EVM2AnyRampMessage) due to encoding & parameter differences
/// messageId = hash(EVM2AnyRampMessage) using the source EVM chain's encoding format
struct EVM2AnyRampMessage {
RampMessageHeader header; // Message header
address sender; // sender address on the source chain
bytes data; // arbitrary data payload supplied by the message sender
bytes receiver; // receiver address on the destination chain
bytes extraArgs; // destination-chain specific extra args, such as the gasLimit for EVM chains
address feeToken; // fee token
uint256 feeTokenAmount; // fee token amount
RampTokenAmount[] tokenAmounts; // array of tokens and amounts to transfer
}
// bytes4(keccak256("CCIP ChainFamilySelector EVM"))
bytes4 public constant CHAIN_FAMILY_SELECTOR_EVM = 0x2812d52c;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
library MerkleMultiProof {
/// @notice Leaf domain separator, should be used as the first 32 bytes of a leaf's preimage.
bytes32 internal constant LEAF_DOMAIN_SEPARATOR = 0x0000000000000000000000000000000000000000000000000000000000000000;
/// @notice Internal domain separator, should be used as the first 32 bytes of an internal node's preiimage.
bytes32 internal constant INTERNAL_DOMAIN_SEPARATOR =
0x0000000000000000000000000000000000000000000000000000000000000001;
uint256 internal constant MAX_NUM_HASHES = 256;
error InvalidProof();
error LeavesCannotBeEmpty();
/// @notice Computes the root based on provided pre-hashed leaf nodes in
/// leaves, internal nodes in proofs, and using proofFlagBits' i-th bit to
/// determine if an element of proofs or one of the previously computed leafs
/// or internal nodes will be used for the i-th hash.
/// @param leaves Should be pre-hashed and the first 32 bytes of a leaf's
/// preimage should match LEAF_DOMAIN_SEPARATOR.
/// @param proofs The hashes to be used instead of a leaf hash when the proofFlagBits
/// indicates a proof should be used.
/// @param proofFlagBits A single uint256 of which each bit indicates whether a leaf or
/// a proof needs to be used in a hash operation.
/// @dev the maximum number of hash operations it set to 256. Any input that would require
/// more than 256 hashes to get to a root will revert.
/// @dev For given input `leaves` = [a,b,c] `proofs` = [D] and `proofFlagBits` = 5
/// totalHashes = 3 + 1 - 1 = 3
/// ** round 1 **
/// proofFlagBits = (5 >> 0) & 1 = true
/// hashes[0] = hashPair(a, b)
/// (leafPos, hashPos, proofPos) = (2, 0, 0);
///
/// ** round 2 **
/// proofFlagBits = (5 >> 1) & 1 = false
/// hashes[1] = hashPair(D, c)
/// (leafPos, hashPos, proofPos) = (3, 0, 1);
///
/// ** round 3 **
/// proofFlagBits = (5 >> 2) & 1 = true
/// hashes[2] = hashPair(hashes[0], hashes[1])
/// (leafPos, hashPos, proofPos) = (3, 2, 1);
///
/// i = 3 and no longer < totalHashes. The algorithm is done
/// return hashes[totalHashes - 1] = hashes[2]; the last hash we computed.
// We mark this function as internal to force it to be inlined in contracts
// that use it, but semantically it is public.
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function merkleRoot(
bytes32[] memory leaves,
bytes32[] memory proofs,
uint256 proofFlagBits
) internal pure returns (bytes32) {
unchecked {
uint256 leavesLen = leaves.length;
uint256 proofsLen = proofs.length;
if (leavesLen == 0) revert LeavesCannotBeEmpty();
if (!(leavesLen <= MAX_NUM_HASHES + 1 && proofsLen <= MAX_NUM_HASHES + 1)) revert InvalidProof();
uint256 totalHashes = leavesLen + proofsLen - 1;
if (!(totalHashes <= MAX_NUM_HASHES)) revert InvalidProof();
if (totalHashes == 0) {
return leaves[0];
}
bytes32[] memory hashes = new bytes32[](totalHashes);
(uint256 leafPos, uint256 hashPos, uint256 proofPos) = (0, 0, 0);
for (uint256 i = 0; i < totalHashes; ++i) {
// Checks if the bit flag signals the use of a supplied proof or a leaf/previous hash.
bytes32 a;
if (proofFlagBits & (1 << i) == (1 << i)) {
// Use a leaf or a previously computed hash.
if (leafPos < leavesLen) {
a = leaves[leafPos++];
} else {
a = hashes[hashPos++];
}
} else {
// Use a supplied proof.
a = proofs[proofPos++];
}
// The second part of the hashed pair is never a proof as hashing two proofs would result in a
// hash that can already be computed offchain.
bytes32 b;
if (leafPos < leavesLen) {
b = leaves[leafPos++];
} else {
b = hashes[hashPos++];
}
if (!(hashPos <= i)) revert InvalidProof();
hashes[i] = _hashPair(a, b);
}
if (!(hashPos == totalHashes - 1 && leafPos == leavesLen && proofPos == proofsLen)) revert InvalidProof();
// Return the last hash.
return hashes[totalHashes - 1];
}
}
/// @notice Hashes two bytes32 objects in their given order, prepended by the
/// INTERNAL_DOMAIN_SEPARATOR.
function _hashInternalNode(bytes32 left, bytes32 right) private pure returns (bytes32 hash) {
return keccak256(abi.encode(INTERNAL_DOMAIN_SEPARATOR, left, right));
}
/// @notice Hashes two bytes32 objects. The order is taken into account,
/// using the lower value first.
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _hashInternalNode(a, b) : _hashInternalNode(b, a);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice This library contains various token pool functions to aid constructing the return data.
library Pool {
// The tag used to signal support for the pool v1 standard
// bytes4(keccak256("CCIP_POOL_V1"))
bytes4 public constant CCIP_POOL_V1 = 0xaff2afbf;
// The number of bytes in the return data for a pool v1 releaseOrMint call.
// This should match the size of the ReleaseOrMintOutV1 struct.
uint16 public constant CCIP_POOL_V1_RET_BYTES = 32;
// The default max number of bytes in the return data for a pool v1 lockOrBurn call.
// This data can be used to send information to the destination chain token pool. Can be overwritten
// in the TokenTransferFeeConfig.destBytesOverhead if more data is required.
uint32 public constant CCIP_LOCK_OR_BURN_V1_RET_BYTES = 32;
struct LockOrBurnInV1 {
bytes receiver; // The recipient of the tokens on the destination chain, abi encoded
uint64 remoteChainSelector; // -? The chain ID of the destination chain
address originalSender; // -----? The original sender of the tx on the source chain
uint256 amount; // The amount of tokens to lock or burn, denominated in the source token's decimals
address localToken; // The address on this chain of the token to lock or burn
}
struct LockOrBurnOutV1 {
// The address of the destination token, abi encoded in the case of EVM chains
// This value is UNTRUSTED as any pool owner can return whatever value they want.
bytes destTokenAddress;
// Optional pool data to be transferred to the destination chain. Be default this is capped at
// CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
// has to be set for the specific token.
bytes destPoolData;
}
struct ReleaseOrMintInV1 {
bytes originalSender; // The original sender of the tx on the source chain
uint64 remoteChainSelector; // -? The chain ID of the source chain
address receiver; // -----------? The recipient of the tokens on the destination chain.
uint256 amount; // The amount of tokens to release or mint, denominated in the source token's decimals
address localToken; // The address on this chain of the token to release or mint
/// @dev WARNING: sourcePoolAddress should be checked prior to any processing of funds. Make sure it matches the
/// expected pool address for the given remoteChainSelector.
bytes sourcePoolAddress; // The address of the source pool, abi encoded in the case of EVM chains
bytes sourcePoolData; // The data received from the source pool to process the release or mint
/// @dev WARNING: offchainTokenData is untrusted data.
bytes offchainTokenData; // The offchain data to process the release or mint
}
struct ReleaseOrMintOutV1 {
// The number of tokens released or minted on the destination chain, denominated in the local token's decimals.
// This value is expected to be equal to the ReleaseOrMintInV1.amount in the case where the source and destination
// chain have the same number of decimals.
uint256 destinationAmount;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
/// @notice Implements Token Bucket rate limiting.
/// @dev uint128 is safe for rate limiter state.
/// For USD value rate limiting, it can adequately store USD value in 18 decimals.
/// For ERC20 token amount rate limiting, all tokens that will be listed will have at most
/// a supply of uint128.max tokens, and it will therefore not overflow the bucket.
/// In exceptional scenarios where tokens consumed may be larger than uint128,
/// e.g. compromised issuer, an enabled RateLimiter will check and revert.
library RateLimiter {
error BucketOverfilled();
error OnlyCallableByAdminOrOwner();
error TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress);
error TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress);
error AggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested);
error AggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available);
error InvalidRateLimitRate(Config rateLimiterConfig);
error DisabledNonZeroRateLimit(Config config);
error RateLimitMustBeDisabled();
event TokensConsumed(uint256 tokens);
event ConfigChanged(Config config);
struct TokenBucket {
uint128 tokens; // ------? Current number of tokens that are in the bucket.
uint32 lastUpdated; // ¦ Timestamp in seconds of the last token refill, good for 100+ years.
bool isEnabled; // ------? Indication whether the rate limiting is enabled or not
uint128 capacity; // ----? Maximum number of tokens that can be in the bucket.
uint128 rate; // --------? Number of tokens per second that the bucket is refilled.
}
struct Config {
bool isEnabled; // Indication whether the rate limiting should be enabled
uint128 capacity; // ----? Specifies the capacity of the rate limiter
uint128 rate; // -------? Specifies the rate of the rate limiter
}
/// @notice _consume removes the given tokens from the pool, lowering the
/// rate tokens allowed to be consumed for subsequent calls.
/// @param requestTokens The total tokens to be consumed from the bucket.
/// @param tokenAddress The token to consume capacity for, use 0x0 to indicate aggregate value capacity.
/// @dev Reverts when requestTokens exceeds bucket capacity or available tokens in the bucket
/// @dev emits removal of requestTokens if requestTokens is > 0
function _consume(TokenBucket storage s_bucket, uint256 requestTokens, address tokenAddress) internal {
// If there is no value to remove or rate limiting is turned off, skip this step to reduce gas usage
if (!s_bucket.isEnabled || requestTokens == 0) {
return;
}
uint256 tokens = s_bucket.tokens;
uint256 capacity = s_bucket.capacity;
uint256 timeDiff = block.timestamp - s_bucket.lastUpdated;
if (timeDiff != 0) {
if (tokens > capacity) revert BucketOverfilled();
// Refill tokens when arriving at a new block time
tokens = _calculateRefill(capacity, tokens, timeDiff, s_bucket.rate);
s_bucket.lastUpdated = uint32(block.timestamp);
}
if (capacity < requestTokens) {
// Token address 0 indicates consuming aggregate value rate limit capacity.
if (tokenAddress == address(0)) revert AggregateValueMaxCapacityExceeded(capacity, requestTokens);
revert TokenMaxCapacityExceeded(capacity, requestTokens, tokenAddress);
}
if (tokens < requestTokens) {
uint256 rate = s_bucket.rate;
// Wait required until the bucket is refilled enough to accept this value, round up to next higher second
// Consume is not guaranteed to succeed after wait time passes if there is competing traffic.
// This acts as a lower bound of wait time.
uint256 minWaitInSeconds = ((requestTokens - tokens) + (rate - 1)) / rate;
if (tokenAddress == address(0)) revert AggregateValueRateLimitReached(minWaitInSeconds, tokens);
revert TokenRateLimitReached(minWaitInSeconds, tokens, tokenAddress);
}
tokens -= requestTokens;
// Downcast is safe here, as tokens is not larger than capacity
s_bucket.tokens = uint128(tokens);
emit TokensConsumed(requestTokens);
}
/// @notice Gets the token bucket with its values for the block it was requested at.
/// @return The token bucket.
function _currentTokenBucketState(TokenBucket memory bucket) internal view returns (TokenBucket memory) {
// We update the bucket to reflect the status at the exact time of the
// call. This means we might need to refill a part of the bucket based
// on the time that has passed since the last update.
bucket.tokens =
uint128(_calculateRefill(bucket.capacity, bucket.tokens, block.timestamp - bucket.lastUpdated, bucket.rate));
bucket.lastUpdated = uint32(block.timestamp);
return bucket;
}
/// @notice Sets the rate limited config.
/// @param s_bucket The token bucket
/// @param config The new config
function _setTokenBucketConfig(TokenBucket storage s_bucket, Config memory config) internal {
// First update the bucket to make sure the proper rate is used for all the time
// up until the config change.
uint256 timeDiff = block.timestamp - s_bucket.lastUpdated;
if (timeDiff != 0) {
s_bucket.tokens = uint128(_calculateRefill(s_bucket.capacity, s_bucket.tokens, timeDiff, s_bucket.rate));
s_bucket.lastUpdated = uint32(block.timestamp);
}
s_bucket.tokens = uint128(_min(config.capacity, s_bucket.tokens));
s_bucket.isEnabled = config.isEnabled;
s_bucket.capacity = config.capacity;
s_bucket.rate = config.rate;
emit ConfigChanged(config);
}
/// @notice Validates the token bucket config
function _validateTokenBucketConfig(Config memory config, bool mustBeDisabled) internal pure {
if (config.isEnabled) {
if (config.rate >= config.capacity || config.rate == 0) {
revert InvalidRateLimitRate(config);
}
if (mustBeDisabled) {
revert RateLimitMustBeDisabled();
}
} else {
if (config.rate != 0 || config.capacity != 0) {
revert DisabledNonZeroRateLimit(config);
}
}
}
/// @notice Calculate refilled tokens
/// @param capacity bucket capacity
/// @param tokens current bucket tokens
/// @param timeDiff block time difference since last refill
/// @param rate bucket refill rate
/// @return the value of tokens after refill
function _calculateRefill(
uint256 capacity,
uint256 tokens,
uint256 timeDiff,
uint256 rate
) private pure returns (uint256) {
return _min(capacity, tokens + timeDiff * rate);
}
/// @notice Return the smallest of two integers
/// @param a first int
/// @param b second int
/// @return smallest
function _min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
library USDPriceWith18Decimals {
/// @notice Takes a price in USD, with 18 decimals per 1e18 token amount,
/// and amount of the smallest token denomination,
/// calculates the value in USD with 18 decimals.
/// @param tokenPrice The USD price of the token.
/// @param tokenAmount Amount of the smallest token denomination.
/// @return USD value with 18 decimals.
/// @dev this function assumes that no more than 1e59 US dollar worth of token is passed in.
/// If more is sent, this function will overflow and revert.
/// Since there isn't even close to 1e59 dollars, this is ok for all legit tokens.
function _calcUSDValueFromTokenAmount(uint224 tokenPrice, uint256 tokenAmount) internal pure returns (uint256) {
/// LINK Example:
/// tokenPrice: 8e18 -> $8/LINK, as 1e18 token amount is 1 LINK, worth 8 USD, or 8e18 with 18 decimals
/// tokenAmount: 2e18 -> 2 LINK
/// result: 8e18 * 2e18 / 1e18 -> 16e18 with 18 decimals = $16
/// USDC Example:
/// tokenPrice: 1e30 -> $1/USDC, as 1e18 token amount is 1e12 USDC, worth 1e12 USD, or 1e30 with 18 decimals
/// tokenAmount: 5e6 -> 5 USDC
/// result: 1e30 * 5e6 / 1e18 -> 5e18 with 18 decimals = $5
return (tokenPrice * tokenAmount) / 1e18;
}
/// @notice Takes a price in USD, with 18 decimals per 1e18 token amount,
/// and USD value with 18 decimals,
/// calculates amount of the smallest token denomination.
/// @param tokenPrice The USD price of the token.
/// @param usdValue USD value with 18 decimals.
/// @return Amount of the smallest token denomination.
function _calcTokenAmountFromUSDValue(uint224 tokenPrice, uint256 usdValue) internal pure returns (uint256) {
/// LINK Example:
/// tokenPrice: 8e18 -> $8/LINK, as 1e18 token amount is 1 LINK, worth 8 USD, or 8e18 with 18 decimals
/// usdValue: 16e18 -> $16
/// result: 16e18 * 1e18 / 8e18 -> 2e18 = 2 LINK
/// USDC Example:
/// tokenPrice: 1e30 -> $1/USDC, as 1e18 token amount is 1e12 USDC, worth 1e12 USD, or 1e30 with 18 decimals
/// usdValue: 5e18 -> $5
/// result: 5e18 * 1e18 / 1e30 -> 5e6 = 5 USDC
return (usdValue * 1e18) / tokenPrice;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
abstract contract OCR2Abstract is ITypeAndVersion {
// Maximum number of oracles the offchain reporting protocol is designed for
uint256 internal constant MAX_NUM_ORACLES = 31;
/// @notice triggers a new run of the offchain reporting protocol
/// @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis
/// @param configDigest configDigest of this configuration
/// @param configCount ordinal number of this config setting among all config settings over the life of this contract
/// @param signers ith element is address ith oracle uses to sign a report
/// @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method
/// @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
/// @param onchainConfig serialized configuration used by the contract (and possibly oracles)
/// @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter
/// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
event ConfigSet(
uint32 previousConfigBlockNumber,
bytes32 configDigest,
uint64 configCount,
address[] signers,
address[] transmitters,
uint8 f,
bytes onchainConfig,
uint64 offchainConfigVersion,
bytes offchainConfig
);
/// @notice sets offchain reporting protocol configuration incl. participating oracles
/// @param signers addresses with which oracles sign the reports
/// @param transmitters addresses oracles use to transmit the reports
/// @param f number of faulty oracles the system can tolerate
/// @param onchainConfig serialized configuration used by the contract (and possibly oracles)
/// @param offchainConfigVersion version number for offchainEncoding schema
/// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
function setOCR2Config(
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) external virtual;
/// @notice information about current offchain reporting protocol configuration
/// @return configCount ordinal number of current config, out of all configs applied to this contract so far
/// @return blockNumber block at which this config was set
/// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData)
function latestConfigDetails()
external
view
virtual
returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest);
function _configDigestFromConfigData(
uint256 chainId,
address contractAddress,
uint64 configCount,
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) internal pure returns (bytes32) {
uint256 h = uint256(
keccak256(
abi.encode(
chainId,
contractAddress,
configCount,
signers,
transmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
)
)
);
uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00
uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00
return bytes32((prefix & prefixMask) | (h & ~prefixMask));
}
/// @notice optionally emitted to indicate the latest configDigest and epoch for
/// which a report was successfully transmitted. Alternatively, the contract may
/// use latestConfigDigestAndEpoch with scanLogs set to false.
event Transmitted(bytes32 configDigest, uint32 epoch);
/// @notice optionally returns the latest configDigest and epoch for which a
/// report was successfully transmitted. Alternatively, the contract may return
/// scanLogs set to true and use Transmitted events to provide this information
/// to offchain watchers.
/// @return scanLogs indicates whether to rely on the configDigest and epoch
/// returned or whether to scan logs for the Transmitted event instead.
/// @return configDigest
/// @return epoch
function latestConfigDigestAndEpoch()
external
view
virtual
returns (bool scanLogs, bytes32 configDigest, uint32 epoch);
/// @notice transmit is called to post a new report to the contract
/// @param report serialized report, which the signatures are signing.
/// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
/// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
/// @param rawVs ith element is the the V component of the ith signature
function transmit(
// NOTE: If these parameters are changed, expectedMsgDataLength and/or
// TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
bytes32[3] calldata reportContext,
bytes calldata report,
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs // signatures
) external virtual;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol";
import {OCR2Abstract} from "./OCR2Abstract.sol";
/// @notice Onchain verification of reports from the offchain reporting protocol
/// @dev For details on its operation, see the offchain reporting protocol design
/// doc, which refers to this contract as simply the "contract".
/// @dev This contract does ***NOT*** check the supplied signatures on `transmit`
/// This is intentional.
abstract contract OCR2BaseNoChecks is OwnerIsCreator, OCR2Abstract {
error InvalidConfig(InvalidConfigErrorType errorType);
error WrongMessageLength(uint256 expected, uint256 actual);
error ConfigDigestMismatch(bytes32 expected, bytes32 actual);
error ForkedChain(uint256 expected, uint256 actual);
error UnauthorizedTransmitter();
error OracleCannotBeZeroAddress();
enum InvalidConfigErrorType {
F_MUST_BE_POSITIVE,
TOO_MANY_TRANSMITTERS,
REPEATED_ORACLE_ADDRESS
}
// Packing these fields used on the hot path in a ConfigInfo variable reduces the
// retrieval of all of them to a minimum number of SLOADs.
struct ConfigInfo {
bytes32 latestConfigDigest;
uint8 f;
uint8 n;
}
// Used for s_oracles[a].role, where a is an address, to track the purpose
// of the address, or to indicate that the address is unset.
enum Role {
// No oracle role has been set for address a
Unset,
// Unused
Signer,
// Transmission address for the s_oracles[a].index'th oracle. I.e., if a
// report is received by OCR2Aggregator.transmit in which msg.sender is
// a, it is attributed to the s_oracles[a].index'th oracle.
Transmitter
}
struct Oracle {
uint8 index; // Index of oracle in s_transmitters
Role role; // Role of the address which mapped to this struct
}
// The current config
ConfigInfo internal s_configInfo;
// incremented each time a new config is posted. This count is incorporated
// into the config digest, to prevent replay attacks.
uint32 internal s_configCount;
// makes it easier for offchain systems to extract config from logs.
uint32 internal s_latestConfigBlockNumber;
// Transmitter address
mapping(address transmitter => Oracle oracle) internal s_oracles;
// s_transmitters contains the transmission address of each oracle,
// i.e. the address the oracle actually sends transactions to the contract from
address[] internal s_transmitters;
// The constant-length components of the msg.data sent to transmit.
// See the "If we wanted to call sam" example on for example reasoning
// https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html
uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = 4 // function selector
+ 32 * 3 // 3 words containing reportContext
+ 32 // word containing start location of abiencoded report value
+ 32 // word containing location start of abiencoded rs value
+ 32 // word containing start location of abiencoded ss value
+ 32 // rawVs value
+ 32 // word containing length of report
+ 32 // word containing length rs
+ 32; // word containing length of ss
uint256 internal immutable i_chainID;
// Reverts transaction if config args are invalid
modifier checkConfigValid(uint256 numTransmitters, uint256 f) {
if (numTransmitters > MAX_NUM_ORACLES) revert InvalidConfig(InvalidConfigErrorType.TOO_MANY_TRANSMITTERS);
if (f == 0) revert InvalidConfig(InvalidConfigErrorType.F_MUST_BE_POSITIVE);
_;
}
constructor() {
i_chainID = block.chainid;
}
/// @notice sets offchain reporting protocol configuration incl. participating oracles
/// @param signers addresses with which oracles sign the reports
/// @param transmitters addresses oracles use to transmit the reports
/// @param f number of faulty oracles the system can tolerate
/// @param onchainConfig encoded on-chain contract configuration
/// @param offchainConfigVersion version number for offchainEncoding schema
/// @param offchainConfig encoded off-chain oracle configuration
function setOCR2Config(
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) external override checkConfigValid(transmitters.length, f) onlyOwner {
_beforeSetConfig(onchainConfig);
// Scoped to reduce contract size
{
uint256 oldTransmitterLength = s_transmitters.length;
for (uint256 i = 0; i < oldTransmitterLength; ++i) {
delete s_oracles[s_transmitters[i]];
}
}
uint256 newTransmitterLength = transmitters.length;
for (uint256 i = 0; i < newTransmitterLength; ++i) {
address transmitter = transmitters[i];
if (s_oracles[transmitter].role != Role.Unset) {
revert InvalidConfig(InvalidConfigErrorType.REPEATED_ORACLE_ADDRESS);
}
if (transmitter == address(0)) revert OracleCannotBeZeroAddress();
s_oracles[transmitter] = Oracle(uint8(i), Role.Transmitter);
}
s_transmitters = transmitters;
s_configInfo.f = f;
s_configInfo.n = uint8(newTransmitterLength);
s_configInfo.latestConfigDigest = _configDigestFromConfigData(
block.chainid,
address(this),
++s_configCount,
signers,
transmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
);
uint32 previousConfigBlockNumber = s_latestConfigBlockNumber;
s_latestConfigBlockNumber = uint32(block.number);
emit ConfigSet(
previousConfigBlockNumber,
s_configInfo.latestConfigDigest,
s_configCount,
signers,
transmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
);
}
/// @dev Hook that is run from setOCR2Config() right after validating configuration.
/// Empty by default, please provide an implementation in a child contract if you need additional configuration processing
function _beforeSetConfig(bytes memory _onchainConfig) internal virtual;
/// @return list of addresses permitted to transmit reports to this contract
/// @dev The list will match the order used to specify the transmitter during setConfig
function getTransmitters() external view returns (address[] memory) {
return s_transmitters;
}
/// @notice transmit is called to post a new report to the contract
/// @param report serialized report, which the signatures are signing.
/// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
/// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
function transmit(
// NOTE: If these parameters are changed, expectedMsgDataLength and/or
// TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
bytes32[3] calldata reportContext,
bytes calldata report,
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 // signatures
) external override {
_report(report);
// reportContext consists of:
// reportContext[0]: ConfigDigest
// reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round
// reportContext[2]: ExtraHash
bytes32 configDigest = reportContext[0];
bytes32 latestConfigDigest = s_configInfo.latestConfigDigest;
if (latestConfigDigest != configDigest) revert ConfigDigestMismatch(latestConfigDigest, configDigest);
_checkChainForked();
emit Transmitted(configDigest, uint32(uint256(reportContext[1]) >> 8));
// Scoping this reduces stack pressure and gas usage
{
Oracle memory transmitter = s_oracles[msg.sender];
// Check that sender is authorized to report
if (!(transmitter.role == Role.Transmitter && msg.sender == s_transmitters[transmitter.index])) {
revert UnauthorizedTransmitter();
}
}
uint256 expectedDataLength = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + report.length // one byte pure entry in _report
+ rs.length * 32 // 32 bytes per entry in _rs
+ ss.length * 32; // 32 bytes per entry in _ss)
if (msg.data.length != expectedDataLength) revert WrongMessageLength(expectedDataLength, msg.data.length);
}
function _checkChainForked() internal view {
// If the cached chainID at time of deployment doesn't match the current chainID, we reject all signed reports.
// This avoids a (rare) scenario where chain A forks into chain A and A', A' still has configDigest
// calculated from chain A and so OCR reports will be valid on both forks.
if (i_chainID != block.chainid) revert ForkedChain(i_chainID, block.chainid);
}
/// @notice information about current offchain reporting protocol configuration
/// @return configCount ordinal number of current config, out of all configs applied to this contract so far
/// @return blockNumber block at which this config was set
/// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData)
function latestConfigDetails()
external
view
override
returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest)
{
return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest);
}
/// @inheritdoc OCR2Abstract
function latestConfigDigestAndEpoch()
external
view
virtual
override
returns (bool scanLogs, bytes32 configDigest, uint32 epoch)
{
return (true, bytes32(0), uint32(0));
}
function _report(bytes calldata report) internal virtual;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
// solhint-disable-next-line gas-custom-errors
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Allows an owner to begin transferring ownership to a new address.
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice validate, transfer ownership, and emit relevant events
function _transferOwnership(address to) private {
// solhint-disable-next-line gas-custom-errors
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice validate access
function _validateOwnership() internal view {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_owner, "Only callable by owner");
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwner} from "./ConfirmedOwner.sol";
/// @title The OwnerIsCreator contract
/// @notice A contract with helpers for basic contract ownership.
contract OwnerIsCreator is ConfirmedOwner {
constructor() ConfirmedOwner(msg.sender) {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice This library contains various callWithExactGas functions. All of them are
/// safe from gas bomb attacks.
/// @dev There is code duplication in this library. This is done to not leave the assembly
/// the blocks.
library CallWithExactGas {
error NoContract();
error NoGasForCallExactCheck();
error NotEnoughGasForCall();
bytes4 internal constant NO_CONTRACT_SIG = 0x0c3b563c;
bytes4 internal constant NO_GAS_FOR_CALL_EXACT_CHECK_SIG = 0xafa32a2c;
bytes4 internal constant NOT_ENOUGH_GAS_FOR_CALL_SIG = 0x37c3be29;
/// @notice calls target address with exactly gasAmount gas and payload as calldata.
/// Accounts for gasForCallExactCheck gas that will be used by this function. Will revert
/// if the target is not a contact. Will revert when there is not enough gas to call the
/// target with gasAmount gas.
/// @dev Ignores the return data, which makes it immune to gas bomb attacks.
/// @return success whether the call succeeded
function _callWithExactGas(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck
) internal returns (bool success) {
assembly {
// solidity calls check that a contract actually exists at the destination, so we do the same
// Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion")
// doesn't need to account for it.
if iszero(extcodesize(target)) {
mstore(0x0, NO_CONTRACT_SIG)
revert(0x0, 0x4)
}
let g := gas()
// Compute g -= gasForCallExactCheck and check for underflow
// The gas actually passed to the callee is _min(gasAmount, 63//64*gas available).
// We want to ensure that we revert if gasAmount > 63//64*gas available
// as we do not want to provide them with less, however that check itself costs
// gas. gasForCallExactCheck ensures we have at least enough gas to be able
// to revert if gasAmount > 63//64*gas available.
if lt(g, gasForCallExactCheck) {
mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
revert(0x0, 0x4)
}
g := sub(g, gasForCallExactCheck)
// if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150
if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
revert(0x0, 0x4)
}
// call and return whether we succeeded. ignore return data
// call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
}
return success;
}
/// @notice calls target address with exactly gasAmount gas and payload as calldata.
/// Account for gasForCallExactCheck gas that will be used by this function. Will revert
/// if the target is not a contact. Will revert when there is not enough gas to call the
/// target with gasAmount gas.
/// @dev Caps the return data length, which makes it immune to gas bomb attacks.
/// @dev Return data cap logic borrowed from
/// https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol.
/// @return success whether the call succeeded
/// @return retData the return data from the call, capped at maxReturnBytes bytes
/// @return gasUsed the gas used by the external call. Does not include the overhead of this function.
function _callWithExactGasSafeReturnData(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck,
uint16 maxReturnBytes
) internal returns (bool success, bytes memory retData, uint256 gasUsed) {
// allocate retData memory ahead of time
retData = new bytes(maxReturnBytes);
assembly {
// solidity calls check that a contract actually exists at the destination, so we do the same
// Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion")
// doesn't need to account for it.
if iszero(extcodesize(target)) {
mstore(0x0, NO_CONTRACT_SIG)
revert(0x0, 0x4)
}
let g := gas()
// Compute g -= gasForCallExactCheck and check for underflow
// The gas actually passed to the callee is _min(gasAmount, 63//64*gas available).
// We want to ensure that we revert if gasAmount > 63//64*gas available
// as we do not want to provide them with less, however that check itself costs
// gas. gasForCallExactCheck ensures we have at least enough gas to be able
// to revert if gasAmount > 63//64*gas available.
if lt(g, gasForCallExactCheck) {
mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
revert(0x0, 0x4)
}
g := sub(g, gasForCallExactCheck)
// if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150
if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
revert(0x0, 0x4)
}
// We save the gas before the call so we can calculate how much gas the call used
let gasBeforeCall := gas()
// call and return whether we succeeded. ignore return data
// call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
gasUsed := sub(gasBeforeCall, gas())
// limit our copy to maxReturnBytes bytes
let toCopy := returndatasize()
if gt(toCopy, maxReturnBytes) {
toCopy := maxReturnBytes
}
// Store the length of the copied bytes
mstore(retData, toCopy)
// copy the bytes from retData[0:_toCopy]
returndatacopy(add(retData, 0x20), 0x0, toCopy)
}
return (success, retData, gasUsed);
}
/// @notice Calls target address with exactly gasAmount gas and payload as calldata
/// or reverts if at least gasLimit gas is not available.
/// @dev Does not check if target is a contract. If it is not a contract, the low-level
/// call will still be made and it will succeed.
/// @dev Ignores the return data, which makes it immune to gas bomb attacks.
/// @return success whether the call succeeded
/// @return sufficientGas Whether there was enough gas to make the call
function _callWithExactGasEvenIfTargetIsNoContract(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck
) internal returns (bool success, bool sufficientGas) {
assembly {
let g := gas()
// Compute g -= CALL_WITH_EXACT_GAS_CUSHION and check for underflow. We
// need the cushion since the logic following the above call to gas also
// costs gas which we cannot account for exactly. So cushion is a
// conservative upper bound for the cost of this logic.
if iszero(lt(g, gasForCallExactCheck)) {
g := sub(g, gasForCallExactCheck)
// If g - g//64 <= gasAmount, we don't have enough gas. We subtract g//64 because of EIP-150.
if gt(sub(g, div(g, 64)), gasLimit) {
// Call and ignore success/return data. Note that we did not check
// whether a contract actually exists at the target address.
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
sufficientGas := true
}
}
}
return (success, sufficientGas);
}
}// SPDX-License-Identifier: MIT
/* solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore */
pragma solidity ^0.8.0;
import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol";
import {EnumerableMapBytes32} from "./EnumerableMapBytes32.sol";
// TODO: the lib can be replaced with OZ v5.1 post-upgrade, which has AddressToAddressMap and AddressToBytes32Map
library EnumerableMapAddresses {
using EnumerableMap for EnumerableMap.UintToAddressMap;
using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map;
using EnumerableMapBytes32 for EnumerableMapBytes32.Bytes32ToBytesMap;
struct AddressToAddressMap {
EnumerableMap.UintToAddressMap _inner;
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function set(AddressToAddressMap storage map, address key, address value) internal returns (bool) {
return map._inner.set(uint256(uint160(key)), value);
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function remove(AddressToAddressMap storage map, address key) internal returns (bool) {
return map._inner.remove(uint256(uint160(key)));
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function contains(AddressToAddressMap storage map, address key) internal view returns (bool) {
return map._inner.contains(uint256(uint160(key)));
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function length(AddressToAddressMap storage map) internal view returns (uint256) {
return map._inner.length();
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function at(AddressToAddressMap storage map, uint256 index) internal view returns (address, address) {
(uint256 key, address value) = map._inner.at(index);
return (address(uint160(key)), value);
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function tryGet(AddressToAddressMap storage map, address key) internal view returns (bool, address) {
return map._inner.tryGet(uint256(uint160(key)));
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function get(AddressToAddressMap storage map, address key) internal view returns (address) {
return map._inner.get(uint256(uint160(key)));
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function get(
AddressToAddressMap storage map,
address key,
string memory errorMessage
) internal view returns (address) {
return map._inner.get(uint256(uint160(key)), errorMessage);
}
struct AddressToBytes32Map {
EnumerableMap.Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function set(AddressToBytes32Map storage map, address key, bytes32 value) internal returns (bool) {
return map._inner.set(bytes32(uint256(uint160(key))), value);
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function remove(AddressToBytes32Map storage map, address key) internal returns (bool) {
return map._inner.remove(bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function contains(AddressToBytes32Map storage map, address key) internal view returns (bool) {
return map._inner.contains(bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function length(AddressToBytes32Map storage map) internal view returns (uint256) {
return map._inner.length();
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function at(AddressToBytes32Map storage map, uint256 index) internal view returns (address, bytes32) {
(bytes32 key, bytes32 value) = map._inner.at(index);
return (address(uint160(uint256(key))), value);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function tryGet(AddressToBytes32Map storage map, address key) internal view returns (bool, bytes32) {
(bool success, bytes32 value) = map._inner.tryGet(bytes32(uint256(uint160(key))));
return (success, value);
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) {
return map._inner.get(bytes32(uint256(uint160(key))));
}
struct AddressToBytesMap {
EnumerableMapBytes32.Bytes32ToBytesMap _inner;
}
/**
* @dev Sets the value for `key` in the map. Returns true if the key was added to the map, that is if it was not already present.
* @param map The map where the value will be set
* @param key The key to set the value for
* @param value The value to set for the key
* @return bool indicating whether the key was added to the map
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function set(AddressToBytesMap storage map, address key, bytes memory value) internal returns (bool) {
return map._inner.set(bytes32(uint256(uint160(key))), value);
}
/**
* @dev Removes the value for `key` in the map. Returns true if the key was removed from the map, that is if it was present.
* @param map The map where the value will be removed
* @param key The key to remove the value for
* @return bool indicating whether the key was removed from the map
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function remove(AddressToBytesMap storage map, address key) internal returns (bool) {
return map._inner.remove(bytes32(uint256(uint160(key))));
}
/**
* @dev Checks if the map contains the `key`. Returns true if the key is in the map.
* @param map The map to check for the presence of the key
* @param key The key to check for presence in the map
* @return bool indicating whether the key is in the map
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function contains(AddressToBytesMap storage map, address key) internal view returns (bool) {
return map._inner.contains(bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map.
* @param map The map to check the length of
* @return uint256 indicating the number of elements in the map
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function length(AddressToBytesMap storage map) internal view returns (uint256) {
return map._inner.length();
}
/**
* @dev Returns the element stored at position `index` in the map. Note that there are no guarantees on the ordering of values inside the array, and it may change when more values are added or removed.
* @param map The map to retrieve the element from
* @param index The index to retrieve the element at
* @return address The key of the element at the specified index
* @return bytes The value of the element at the specified index
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function at(AddressToBytesMap storage map, uint256 index) internal view returns (address, bytes memory) {
(bytes32 key, bytes memory value) = map._inner.at(index);
return (address(uint160(uint256(key))), value);
}
/**
* @dev Tries to return the value associated with `key`. Does not revert if `key` is not in the map.
* @param map The map to retrieve the value from
* @param key The key to retrieve the value for
* @return bool indicating whether the key was in the map
* @return bytes The value associated with the key
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function tryGet(AddressToBytesMap storage map, address key) internal view returns (bool, bytes memory) {
return map._inner.tryGet(bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the value associated with `key`.
* @param map The map to retrieve the value from
* @param key The key to retrieve the value for
* @return bytes The value associated with the key
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function get(AddressToBytesMap storage map, address key) internal view returns (bytes memory) {
return map._inner.get(bytes32(uint256(uint160(key))));
}
}// SPDX-License-Identifier: MIT
/* solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore */
pragma solidity ^0.8.0;
import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableMapBytes32 for EnumerableMapBytes32.Bytes32ToBytesMap;
*
* // Declare a set state variable
* EnumerableMapBytes32.Bytes32ToBytesMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `bytes32 -> bytes` (`Bytes32ToBytes`)
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean up an EnumerableMapBytes32, you should remove all elements one by one.
* ====
*/
library EnumerableMapBytes32 {
using EnumerableSet for EnumerableSet.Bytes32Set;
error NonexistentKeyError();
struct Bytes32ToBytesMap {
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 => bytes) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function set(Bytes32ToBytesMap storage map, bytes32 key, bytes memory value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function remove(Bytes32ToBytesMap storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function contains(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function length(Bytes32ToBytesMap storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function at(Bytes32ToBytesMap storage map, uint256 index) internal view returns (bytes32, bytes memory) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function tryGet(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bool, bytes memory) {
bytes memory value = map._values[key];
if (value.length == 0) {
return (contains(map, key), bytes(""));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function get(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bytes memory) {
bytes memory value = map._values[key];
if (value.length == 0 && !contains(map, key)) {
revert NonexistentKeyError();
}
return value;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITypeAndVersion {
function typeAndVersion() external pure returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
pragma solidity ^0.8.0;
import "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
// The Map implementation uses private functions, and user-facing
// implementations (such as Uint256ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct Bytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(
Bytes32ToBytes32Map storage map,
bytes32 key,
bytes32 value
) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key");
return value;
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
Bytes32ToBytes32Map storage map,
bytes32 key,
string memory errorMessage
) internal view returns (bytes32) {
bytes32 value = map._values[key];
require(value != 0 || contains(map, key), errorMessage);
return value;
}
// UintToUintMap
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(
UintToUintMap storage map,
uint256 key,
uint256 value
) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
UintToUintMap storage map,
uint256 key,
string memory errorMessage
) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key), errorMessage));
}
// UintToAddressMap
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(
UintToAddressMap storage map,
uint256 key,
address value
) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
UintToAddressMap storage map,
uint256 key,
string memory errorMessage
) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
}
// AddressToUintMap
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(
AddressToUintMap storage map,
address key,
uint256 value
) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
AddressToUintMap storage map,
address key,
string memory errorMessage
) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
}
// Bytes32ToUintMap
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(
Bytes32ToUintMap storage map,
bytes32 key,
uint256 value
) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (key, uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, key);
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
Bytes32ToUintMap storage map,
bytes32 key,
string memory errorMessage
) internal view returns (uint256) {
return uint256(get(map._inner, key, errorMessage));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// 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;
}
}// 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);
}{
"evmVersion": "paris",
"libraries": {},
"metadata": {
"appendCBOR": true,
"bytecodeHash": "none",
"useLiteralContent": false
},
"optimizer": {
"enabled": true,
"runs": 18000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"forge-std/=src/v0.8/vendor/forge-std/src/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@arbitrum/=node_modules/@arbitrum/",
"hardhat/=node_modules/hardhat/",
"@eth-optimism/=node_modules/@eth-optimism/",
"@scroll-tech/=node_modules/@scroll-tech/",
"@chainlink/=node_modules/@chainlink/"
],
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"address","name":"onRamp","type":"address"},{"internalType":"address","name":"prevOffRamp","type":"address"},{"internalType":"address","name":"rmnProxy","type":"address"},{"internalType":"address","name":"tokenAdminRegistry","type":"address"}],"internalType":"struct EVM2EVMOffRamp.StaticConfig","name":"staticConfig","type":"tuple"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"rateLimiterConfig","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"}],"name":"AggregateValueMaxCapacityExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"minWaitInSeconds","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"AggregateValueRateLimitReached","type":"error"},{"inputs":[],"name":"BucketOverfilled","type":"error"},{"inputs":[],"name":"CanOnlySelfCall","type":"error"},{"inputs":[],"name":"CommitStoreAlreadyInUse","type":"error"},{"inputs":[{"internalType":"bytes32","name":"expected","type":"bytes32"},{"internalType":"bytes32","name":"actual","type":"bytes32"}],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"CursedByRMN","type":"error"},{"inputs":[{"internalType":"bytes32","name":"messageId","type":"bytes32"},{"internalType":"uint64","name":"sequenceNumber","type":"uint64"}],"name":"DestinationGasAmountCountMismatch","type":"error"},{"inputs":[],"name":"EmptyReport","type":"error"},{"inputs":[{"internalType":"bytes","name":"err","type":"bytes"}],"name":"ExecutionError","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"ForkedChain","type":"error"},{"inputs":[{"internalType":"enum OCR2BaseNoChecks.InvalidConfigErrorType","name":"errorType","type":"uint8"}],"name":"InvalidConfig","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"got","type":"uint256"}],"name":"InvalidDataLength","type":"error"},{"inputs":[{"internalType":"bytes","name":"encodedAddress","type":"bytes"}],"name":"InvalidEVMAddress","type":"error"},{"inputs":[{"internalType":"bytes32","name":"messageId","type":"bytes32"},{"internalType":"uint256","name":"oldLimit","type":"uint256"},{"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"InvalidManualExecutionGasLimit","type":"error"},{"inputs":[],"name":"InvalidMessageId","type":"error"},{"inputs":[{"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"internalType":"enum Internal.MessageExecutionState","name":"newState","type":"uint8"}],"name":"InvalidNewState","type":"error"},{"inputs":[{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"}],"name":"InvalidSourceChain","type":"error"},{"inputs":[{"internalType":"bytes32","name":"messageId","type":"bytes32"},{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"oldLimit","type":"uint256"},{"internalType":"uint256","name":"tokenGasOverride","type":"uint256"}],"name":"InvalidTokenGasOverride","type":"error"},{"inputs":[],"name":"ManualExecutionGasLimitMismatch","type":"error"},{"inputs":[],"name":"ManualExecutionNotYetEnabled","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxSize","type":"uint256"},{"internalType":"uint256","name":"actualSize","type":"uint256"}],"name":"MessageTooLarge","type":"error"},{"inputs":[{"internalType":"address","name":"notPool","type":"address"}],"name":"NotACompatiblePool","type":"error"},{"inputs":[],"name":"OnlyCallableByAdminOrOwner","type":"error"},{"inputs":[],"name":"OracleCannotBeZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"PriceNotFoundForToken","type":"error"},{"inputs":[{"internalType":"bytes","name":"err","type":"bytes"}],"name":"ReceiverError","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountReleased","type":"uint256"},{"internalType":"uint256","name":"balancePre","type":"uint256"},{"internalType":"uint256","name":"balancePost","type":"uint256"}],"name":"ReleaseOrMintBalanceMismatch","type":"error"},{"inputs":[],"name":"RootNotCommitted","type":"error"},{"inputs":[{"internalType":"uint64","name":"sequenceNumber","type":"uint64"}],"name":"TokenDataMismatch","type":"error"},{"inputs":[{"internalType":"bytes","name":"err","type":"bytes"}],"name":"TokenHandlingError","type":"error"},{"inputs":[{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"TokenMaxCapacityExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"minWaitInSeconds","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"TokenRateLimitReached","type":"error"},{"inputs":[],"name":"UnauthorizedTransmitter","type":"error"},{"inputs":[],"name":"UnexpectedTokenData","type":"error"},{"inputs":[{"internalType":"uint64","name":"sequenceNumber","type":"uint64"}],"name":"UnsupportedNumberOfTokens","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"WrongMessageLength","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"sequenceNumber","type":"uint64"}],"name":"AlreadyAttempted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"indexed":false,"internalType":"struct RateLimiter.Config","name":"config","type":"tuple"}],"name":"ConfigChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"previousConfigBlockNumber","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"configCount","type":"uint64"},{"indexed":false,"internalType":"address[]","name":"signers","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"uint8","name":"f","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"onchainConfig","type":"bytes"},{"indexed":false,"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"address","name":"onRamp","type":"address"},{"internalType":"address","name":"prevOffRamp","type":"address"},{"internalType":"address","name":"rmnProxy","type":"address"},{"internalType":"address","name":"tokenAdminRegistry","type":"address"}],"indexed":false,"internalType":"struct EVM2EVMOffRamp.StaticConfig","name":"staticConfig","type":"tuple"},{"components":[{"internalType":"uint32","name":"permissionLessExecutionThresholdSeconds","type":"uint32"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"priceRegistry","type":"address"}],"indexed":false,"internalType":"struct EVM2EVMOffRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"indexed":true,"internalType":"bytes32","name":"messageId","type":"bytes32"},{"indexed":false,"internalType":"enum Internal.MessageExecutionState","name":"state","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"returnData","type":"bytes"}],"name":"ExecutionStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"sequenceNumber","type":"uint64"}],"name":"SkippedAlreadyExecutedMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"nonce","type":"uint64"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"SkippedIncorrectNonce","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"nonce","type":"uint64"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"SkippedSenderWithPreviousRampMessageInflight","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sourceToken","type":"address"},{"indexed":false,"internalType":"address","name":"destToken","type":"address"}],"name":"TokenAggregateRateLimitAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sourceToken","type":"address"},{"indexed":false,"internalType":"address","name":"destToken","type":"address"}],"name":"TokenAggregateRateLimitRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokens","type":"uint256"}],"name":"TokensConsumed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"}],"name":"Transmitted","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"messageId","type":"bytes32"},{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"bytes","name":"sender","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Client.EVMTokenAmount[]","name":"destTokenAmounts","type":"tuple[]"}],"internalType":"struct Client.Any2EVMMessage","name":"","type":"tuple"}],"name":"ccipReceive","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"currentRateLimiterState","outputs":[{"components":[{"internalType":"uint128","name":"tokens","type":"uint128"},{"internalType":"uint32","name":"lastUpdated","type":"uint32"},{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.TokenBucket","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"bool","name":"strict","type":"bool"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Client.EVMTokenAmount[]","name":"tokenAmounts","type":"tuple[]"},{"internalType":"bytes[]","name":"sourceTokenData","type":"bytes[]"},{"internalType":"bytes32","name":"messageId","type":"bytes32"}],"internalType":"struct Internal.EVM2EVMMessage","name":"message","type":"tuple"},{"internalType":"bytes[]","name":"offchainTokenData","type":"bytes[]"},{"internalType":"uint32[]","name":"tokenGasOverrides","type":"uint32[]"}],"name":"executeSingleMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllRateLimitTokens","outputs":[{"internalType":"address[]","name":"sourceTokens","type":"address[]"},{"internalType":"address[]","name":"destTokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDynamicConfig","outputs":[{"components":[{"internalType":"uint32","name":"permissionLessExecutionThresholdSeconds","type":"uint32"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"priceRegistry","type":"address"}],"internalType":"struct EVM2EVMOffRamp.DynamicConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"sequenceNumber","type":"uint64"}],"name":"getExecutionState","outputs":[{"internalType":"enum Internal.MessageExecutionState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"getSenderNonce","outputs":[{"internalType":"uint64","name":"nonce","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStaticConfig","outputs":[{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"address","name":"onRamp","type":"address"},{"internalType":"address","name":"prevOffRamp","type":"address"},{"internalType":"address","name":"rmnProxy","type":"address"},{"internalType":"address","name":"tokenAdminRegistry","type":"address"}],"internalType":"struct EVM2EVMOffRamp.StaticConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenLimitAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTransmitters","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestConfigDetails","outputs":[{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"bytes32","name":"configDigest","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestConfigDigestAndEpoch","outputs":[{"internalType":"bool","name":"scanLogs","type":"bool"},{"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"internalType":"uint32","name":"epoch","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"bool","name":"strict","type":"bool"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Client.EVMTokenAmount[]","name":"tokenAmounts","type":"tuple[]"},{"internalType":"bytes[]","name":"sourceTokenData","type":"bytes[]"},{"internalType":"bytes32","name":"messageId","type":"bytes32"}],"internalType":"struct Internal.EVM2EVMMessage[]","name":"messages","type":"tuple[]"},{"internalType":"bytes[][]","name":"offchainTokenData","type":"bytes[][]"},{"internalType":"bytes32[]","name":"proofs","type":"bytes32[]"},{"internalType":"uint256","name":"proofFlagBits","type":"uint256"}],"internalType":"struct Internal.ExecutionReport","name":"report","type":"tuple"},{"components":[{"internalType":"uint256","name":"receiverExecutionGasLimit","type":"uint256"},{"internalType":"uint32[]","name":"tokenGasOverrides","type":"uint32[]"}],"internalType":"struct EVM2EVMOffRamp.GasLimitOverride[]","name":"gasLimitOverrides","type":"tuple[]"}],"name":"manuallyExecute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfig","type":"bytes"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setOCR2Config","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"config","type":"tuple"}],"name":"setRateLimiterConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"report","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sourceToken","type":"address"},{"internalType":"address","name":"destToken","type":"address"}],"internalType":"struct EVM2EVMOffRamp.RateLimitToken[]","name":"removes","type":"tuple[]"},{"components":[{"internalType":"address","name":"sourceToken","type":"address"},{"internalType":"address","name":"destToken","type":"address"}],"internalType":"struct EVM2EVMOffRamp.RateLimitToken[]","name":"adds","type":"tuple[]"}],"name":"updateRateLimitTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101a06040523480156200001257600080fd5b506040516200660a3803806200660a8339810160408190526200003591620004ec565b8033806000816200008d5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c057620000c081620002ca565b50506040805160a081018252602084810180516001600160801b039081168085524263ffffffff169385018490528751151585870181905292518216606080870182905298909601519091166080948501819052600380546001600160a01b031916909217600160801b9485021760ff60a01b1916600160a01b90930292909217905502909117600455469052508201516001600160a01b031615806200016f575081516001600160a01b0316155b8062000186575060c08201516001600160a01b0316155b15620001a5576040516342bcdf7f60e11b815260040160405180910390fd5b81600001516001600160a01b0316634120fccd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001e8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200020e9190620005b5565b6001600160401b03166001146200023857604051636fc2a20760e11b815260040160405180910390fd5b81516001600160a01b0390811660a090815260408401516001600160401b0390811660c0908152602086015190911660e05260608501518316610100526080850151831661014052908401518216610160528301511661018052620002bd7f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b362000375565b6101205250620005da9050565b336001600160a01b03821603620003245760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000084565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008160c05160e05161010051604051602001620003bf94939291909384526001600160401b039283166020850152911660408301526001600160a01b0316606082015260800190565b604051602081830303815290604052805190602001209050919050565b60405160e081016001600160401b03811182821017156200040d57634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200042b57600080fd5b919050565b80516001600160401b03811681146200042b57600080fd5b80516001600160801b03811681146200042b57600080fd5b6000606082840312156200047357600080fd5b604051606081016001600160401b0381118282101715620004a457634e487b7160e01b600052604160045260246000fd5b806040525080915082518015158114620004bd57600080fd5b8152620004cd6020840162000448565b6020820152620004e06040840162000448565b60408201525092915050565b6000808284036101408112156200050257600080fd5b60e08112156200051157600080fd5b506200051c620003dc565b620005278462000413565b8152620005376020850162000430565b60208201526200054a6040850162000430565b60408201526200055d6060850162000413565b6060820152620005706080850162000413565b60808201526200058360a0850162000413565b60a08201526200059660c0850162000413565b60c08201529150620005ac8460e0850162000460565b90509250929050565b600060208284031215620005c857600080fd5b620005d38262000430565b9392505050565b60805160a05160c05160e0516101005161012051610140516101605161018051615f41620006c9600039600081816102ec01528181611c6a01526133920152600081816102bd01528181611c420152611f2701526000818161028e01528181610d8d01528181610df201528181611c18015281816124a4015261250e015260006120c601526000818161025f0152611bee0152600081816101ff0152611b9201526000818161022f01528181611bc601528181611ee40152818161303b01526134bf0152600081816101d001528181611b6d01526121ac015260008181611e3e0152611e8a0152615f416000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c806385572ffb116100d8578063afcb95d71161008c578063c92b283211610066578063c92b2832146105f3578063f077b59214610606578063f2fde38b1461061c57600080fd5b8063afcb95d7146105ad578063b1dc65a4146105cd578063b6113fce146105e057600080fd5b8063873504d7116100bd578063873504d7146105765780638926c4ee146105895780638da5cb5b1461059c57600080fd5b806385572ffb1461053c578063856c82471461054a57600080fd5b8063599f64311161013a5780637437ff9f116101145780637437ff9f1461046157806379ba50971461050457806381ff70481461050c57600080fd5b8063599f643114610414578063666cab8d14610439578063704b6c021461044e57600080fd5b8063181f5a771161016b578063181f5a77146103525780631ef381741461039b578063546719cd146103b057600080fd5b806306285c6914610187578063142a98fc14610332575b600080fd5b61031c6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091526040518060e001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b6040516103299190614312565b60405180910390f35b6103456103403660046143a8565b61062f565b6040516103299190614408565b61038e6040518060400160405280601481526020017f45564d3245564d4f666652616d7020312e352e3000000000000000000000000081525081565b6040516103299190614466565b6103ae6103a936600461468f565b6106aa565b005b6103b8610a9e565b604051610329919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b6002546001600160a01b03165b6040516001600160a01b039091168152602001610329565b610441610b53565b60405161032991906147a1565b6103ae61045c3660046147b4565b610bb5565b6104f76040805160a081018252600080825260208201819052918101829052606081018290526080810191909152506040805160a081018252600a5463ffffffff8082168352640100000000820416602083015268010000000000000000810461ffff16928201929092526a01000000000000000000009091046001600160a01b039081166060830152600b5416608082015290565b60405161032991906147d1565b6103ae610c7e565b6007546005546040805163ffffffff80851682526401000000009094049093166020840152820152606001610329565b6103ae610182366004614827565b61055d6105583660046147b4565b610d61565b60405167ffffffffffffffff9091168152602001610329565b6103ae6105843660046148f3565b610e64565b6103ae610597366004614e1e565b611037565b6000546001600160a01b0316610421565b604080516001815260006020820181905291810191909152606001610329565b6103ae6105db366004614f75565b61129c565b6103ae6105ee36600461505a565b6114a7565b6103ae610601366004615111565b6117e7565b61060e611852565b60405161032992919061517f565b6103ae61062a3660046147b4565b611978565b600061063d600160046151d3565b600261064a608085615215565b67ffffffffffffffff1661065e919061523c565b6010600061066d608087615253565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002054901c1660038111156106a4576106a46143c5565b92915050565b84518460ff16601f8211156106f75760016040517f367f56a20000000000000000000000000000000000000000000000000000000081526004016106ee919061527a565b60405180910390fd5b806000036107345760006040517f367f56a20000000000000000000000000000000000000000000000000000000081526004016106ee919061527a565b61073c611989565b610745856119ff565b60095460005b818110156107bc57600860006009838154811061076a5761076a615294565b60009182526020808320909101546001600160a01b03168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905560010161074b565b5050865160005b8181101561095f5760008982815181106107df576107df615294565b60200260200101519050600060028111156107fc576107fc6143c5565b6001600160a01b038216600090815260086020526040902054610100900460ff16600281111561082e5761082e6143c5565b146108685760026040517f367f56a20000000000000000000000000000000000000000000000000000000081526004016106ee919061527a565b6001600160a01b0381166108a8576040517fd6c62c9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805180820190915260ff8316815260208101600290526001600160a01b03821660009081526008602090815260409091208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561094b5761094b6143c5565b0217905550905050508060010190506107c3565b5087516109739060099060208b0190614280565b506006805460ff838116610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909216908a1617179055600780546109f99146913091906000906109cb9063ffffffff166152c3565b91906101000a81548163ffffffff021916908363ffffffff160217905563ffffffff168c8c8c8c8c8c611cc9565b6005819055600780544363ffffffff9081166401000000009081027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff841681179094556040519083048216947f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0594610a8a9487949293918316921691909117908f908f908f908f908f908f906152e6565b60405180910390a150505050505050505050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040805160a0810182526003546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff1660208501527401000000000000000000000000000000000000000090920460ff161515938301939093526004548084166060840152049091166080820152610b4e90611d56565b905090565b60606009805480602002602001604051908101604052809291908181526020018280548015610bab57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b8d575b5050505050905090565b6000546001600160a01b03163314801590610bdb57506002546001600160a01b03163314155b15610c12576040517ff6cd562000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f8fe72c3e0020beb3234e76ae6676fa576fbfcae600af1c4fea44784cf0db329c9060200160405180910390a150565b6001546001600160a01b03163314610cf2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016106ee565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6001600160a01b0381166000908152600f602052604081205467ffffffffffffffff168082036106a4577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316156106a4576040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063856c824790602401602060405180830381865afa158015610e39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5d919061537c565b9392505050565b610e6c611989565b60005b8251811015610f3f57610ea9838281518110610e8d57610e8d615294565b602002602001015160200151600c611e0890919063ffffffff16565b15610f37577fcbf3cbeaed4ac1d605ed30f4af06c35acaeff2379db7f6146c9cceee83d58782838281518110610ee157610ee1615294565b602002602001015160000151848381518110610eff57610eff615294565b602002602001015160200151604051610f2e9291906001600160a01b0392831681529116602082015260400190565b60405180910390a15b600101610e6f565b5060005b815181101561103257610f9c828281518110610f6157610f61615294565b602002602001015160200151838381518110610f7f57610f7f615294565b602002602001015160000151600c611e1d9092919063ffffffff16565b1561102a577ffc23abf7ddbd3c02b1420dafa2355c56c1a06fbb8723862ac14d6bd74177361a828281518110610fd457610fd4615294565b602002602001015160000151838381518110610ff257610ff2615294565b6020026020010151602001516040516110219291906001600160a01b0392831681529116602082015260400190565b60405180910390a15b600101610f43565b505050565b61103f611e3b565b8151518151811461107c576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156112915760008460000151828151811061109f5761109f615294565b6020026020010151905060008483815181106110bd576110bd615294565b60209081029190910101518051909150801561112c57826080015181101561112c5761018083015160808401516040517f9c6db58d00000000000000000000000000000000000000000000000000000000815260048101929092526024820152604481018290526064016106ee565b816020015151836101400151511461118e5761018083015160608401516040517f85d2e5bf000000000000000000000000000000000000000000000000000000008152600481019290925267ffffffffffffffff1660248201526044016106ee565b61016083015160005b846101400151518110156112815760008287815181106111b9576111b9615294565b60200260200101518060200190518101906111d491906153de565b90506000856020015183815181106111ee576111ee615294565b602002602001015163ffffffff169050806000141580156112185750816060015163ffffffff1681105b156112775761018087015160608301516040517fef0c635200000000000000000000000000000000000000000000000000000000815260048101929092526024820185905263ffffffff166044820152606481018290526084016106ee565b5050600101611197565b505050505080600101905061107f565b506110328383611ebc565b6112a6878761290f565b6005548835908082146112ef576040517f93df584c00000000000000000000000000000000000000000000000000000000815260048101829052602481018390526044016106ee565b6112f7611e3b565b6040805183815260208c81013560081c63ffffffff16908201527fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a13360009081526008602090815260408083208151808301909252805460ff8082168452929391929184019161010090910416600281111561137f5761137f6143c5565b6002811115611390576113906143c5565b90525090506002816020015160028111156113ad576113ad6143c5565b1480156113e757506009816000015160ff16815481106113cf576113cf615294565b6000918252602090912001546001600160a01b031633145b61141d576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600061142b85602061523c565b61143688602061523c565b6114428b6101446154aa565b61144c91906154aa565b61145691906154aa565b905036811461149a576040517f8e1192e1000000000000000000000000000000000000000000000000000000008152600481018290523660248201526044016106ee565b5050505050505050505050565b3330146114e0576040517f371a732800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516000808252602082019092528161151d565b60408051808201909152600080825260208201528152602001906001900390816114f65790505b50905060006115306101408701876154bd565b905011156115a7576115a46115496101408701876154bd565b6115596040890160208a016147b4565b604080516001600160a01b0390921660208301520160408051601f1981840301815291815261158e9060608b01908b016147b4565b61159c6101608b018b615525565b8a8a8a612966565b90505b6115b561012086018661558d565b15905080156115c657506080850135155b806115e857506115dc60608601604087016147b4565b6001600160a01b03163b155b8061163357506116317f85572ffb0000000000000000000000000000000000000000000000000000000061162260608801604089016147b4565b6001600160a01b031690612b8f565b155b1561163e57506117e1565b600a546040805160a08101909152610180870135815260009182916a01000000000000000000009091046001600160a01b031690633cf979839060208082019061168a908c018c6143a8565b67ffffffffffffffff1681526020018a60200160208101906116ac91906147b4565b604080516001600160a01b0390921660208301520160408051601f1981840301815291905281526020016116e46101208c018c61558d565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200186905261138860808b013561173960608d0160408e016147b4565b6040518563ffffffff1660e01b81526004016117589493929190615637565b6000604051808303816000875af1158015611777573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261179f91908101906156fc565b5091509150816117dd57806040517f0a8d6e8c0000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b5050505b50505050565b6000546001600160a01b0316331480159061180d57506002546001600160a01b03163314155b15611844576040517ff6cd562000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61184f600382612bab565b50565b6060806000611861600c612d90565b90508067ffffffffffffffff81111561187c5761187c614479565b6040519080825280602002602001820160405280156118a5578160200160208202803683370190505b5092508067ffffffffffffffff8111156118c1576118c1614479565b6040519080825280602002602001820160405280156118ea578160200160208202803683370190505b50915060005b8181101561197257600080611906600c84612d9b565b915091508086848151811061191d5761191d615294565b60200260200101906001600160a01b031690816001600160a01b0316815250508185848151811061195057611950615294565b6001600160a01b039092166020928302919091019091015250506001016118f0565b50509091565b611980611989565b61184f81612db9565b6000546001600160a01b031633146119fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016106ee565b565b600081806020019051810190611a159190615756565b60608101519091506001600160a01b0316611a5c576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600a805460208085015160408087015160608089015163ffffffff9889167fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909716969096176401000000009890941697909702929092177fffff00000000000000000000000000000000000000000000ffffffffffffffff166801000000000000000061ffff909316929092027fffff0000000000000000000000000000000000000000ffffffffffffffffffff16919091176a01000000000000000000006001600160a01b039485160217909355608080860151600b80547fffffffffffffffffffffffff000000000000000000000000000000000000000016918516919091179055835160e0810185527f0000000000000000000000000000000000000000000000000000000000000000841681527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff908116938201939093527f0000000000000000000000000000000000000000000000000000000000000000909216828501527f00000000000000000000000000000000000000000000000000000000000000008316948201949094527f00000000000000000000000000000000000000000000000000000000000000008216938101939093527f0000000000000000000000000000000000000000000000000000000000000000811660a08401527f00000000000000000000000000000000000000000000000000000000000000001660c0830152517f7879e20bb60a503429de4a2c912b5904f08a39f2af054c10fb46434b5d61126091611cbd9184906157f5565b60405180910390a15050565b6000808a8a8a8a8a8a8a8a8a604051602001611ced999897969594939291906158b7565b60408051601f1981840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611de482606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611dc891906151d3565b85608001516fffffffffffffffffffffffffffffffff16612e94565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b6000610e5d836001600160a01b038416612ebc565b6000611e33846001600160a01b03851684612ec8565b949350505050565b467f0000000000000000000000000000000000000000000000000000000000000000146119fd576040517f0f01ce850000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201524660248201526044016106ee565b6040517f2cbc26bb0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060801b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa158015611f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9a919061593f565b15611fd1576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815151600081900361200e576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015151811461204c576040517f57e0e08300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008167ffffffffffffffff81111561206757612067614479565b604051908082528060200260200182016040528015612090578160200160208202803683370190505b50905060005b82811015612168576000856000015182815181106120b6576120b6615294565b602002602001015190506120ea817f0000000000000000000000000000000000000000000000000000000000000000612ede565b8383815181106120fc576120fc615294565b60200260200101818152505080610180015183838151811061212057612120615294565b60200260200101511461215f576040517f7185cf6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600101612096565b508251604080860151606087015191517f32048875000000000000000000000000000000000000000000000000000000008152921515926000926001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016926332048875926121e29288929160040161598d565b602060405180830381865afa1580156121ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222391906159c3565b90508060000361225f576040517fea75680100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b848110156117dd5760008760000151828151811061228257612282615294565b60200260200101519050600061229b826060015161062f565b905060008160038111156122b1576122b16143c5565b14806122ce575060038160038111156122cc576122cc6143c5565b145b61231457816060015167ffffffffffffffff167fe3dd0bec917c965a133ddb2c84874725ee1e2fd8d763c19efa36d6a11cd82b1f60405160405180910390a25050612907565b606085156123fa5788848151811061232e5761232e615294565b6020908102919091018101510151600a5490915060009063ffffffff1661235587426151d3565b119050808061237557506003836003811115612373576123736143c5565b145b6123ab576040517f6358b0d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8985815181106123bd576123bd615294565b6020026020010151600001516000146123f4578985815181106123e2576123e2615294565b60209081029190910101515160808501525b5061245e565b600082600381111561240e5761240e6143c5565b1461245e57606083015160405167ffffffffffffffff90911681527f67d9ba0f63d427c482c2736300e6d5a34c6691dbcdea8ad35828a1f1ba47e8729060200160405180910390a1505050612907565b60c083015167ffffffffffffffff16156126df576020808401516001600160a01b03166000908152600f909152604081205467ffffffffffffffff1690819003612649577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316156126495760208401516040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201527f00000000000000000000000000000000000000000000000000000000000000009091169063856c824790602401602060405180830381865afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b919061537c565b60c085015190915067ffffffffffffffff166125988260016159dc565b67ffffffffffffffff16146125f95783602001516001600160a01b03168460c0015167ffffffffffffffff167fe44a20935573a783dd0d5991c92d7b6a0eb3173566530364db3ec10e9a990b5d60405160405180910390a350505050612907565b6020848101516001600160a01b03166000908152600f9091526040902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff83161790555b600083600381111561265d5761265d6143c5565b036126dd5760c084015167ffffffffffffffff1661267c8260016159dc565b67ffffffffffffffff16146126dd5783602001516001600160a01b03168460c0015167ffffffffffffffff167fd32ddb11d71e3d63411d37b09f9a8b28664f1cb1338bfd1413c173b0ebf4123760405160405180910390a350505050612907565b505b60008a6020015185815181106126f7576126f7615294565b602002602001015190506127238460600151856000015186610140015151876101200151518551613039565b612732846060015160016131ba565b600080612740868486613264565b915091506127528660600151836131ba565b88156127be57600382600381111561276c5761276c6143c5565b036127be576000856003811115612785576127856143c5565b146127be57806040517fcf19edfd0000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b60028260038111156127d2576127d26143c5565b1461282a5760038260038111156127eb576127eb6143c5565b1461282a578560600151826040517f9e2616030000000000000000000000000000000000000000000000000000000081526004016106ee9291906159fd565b60c086015167ffffffffffffffff16156128b2576000856003811115612852576128526143c5565b036128b2576020808701516001600160a01b03166000908152600f90915260408120805467ffffffffffffffff169161288a83615a1b565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b856101800151866060015167ffffffffffffffff167fd4f851956a5d67c3997d1c9205045fef79bae2947fdee7e9e2641abc7391ef6584846040516128f8929190615a38565b60405180910390a35050505050505b600101612262565b61296261291e82840184615a58565b604080516000808252602082019092529061295c565b6040805180820190915260008152606060208201528152602001906001900390816129345790505b50611ebc565b5050565b60608989808060200260200160405190810160405280939291908181526020016000905b828210156129b6576129a760408302860136819003810190615a8d565b8152602001906001019061298a565b505050505090506000805b8a811015612b715760008888838181106129dd576129dd615294565b90506020028101906129ef919061558d565b8101906129fc9190615aa9565b90508451600014612a5757848281518110612a1957612a19615294565b602002602001015163ffffffff16600014612a5757848281518110612a4057612a40615294565b602090810291909101015163ffffffff1660608201525b612ad78d8d84818110612a6c57612a6c615294565b905060400201602001358c8c848b8b88818110612a8b57612a8b615294565b9050602002810190612a9d919061558d565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061333192505050565b848381518110612ae957612ae9615294565b6020026020010181905250612b25848381518110612b0957612b09615294565b602002602001015160000151600c6136fd90919063ffffffff16565b15612b6857612b5b848381518110612b3f57612b3f615294565b6020908102919091010151600b546001600160a01b0316613712565b612b6590846154aa565b92505b506001016129c1565b508015612b8157612b8181613833565b509998505050505050505050565b6000612b9a83613840565b8015610e5d5750610e5d83836138a4565b8154600090612bd490700100000000000000000000000000000000900463ffffffff16426151d3565b90508015612c765760018301548354612c1c916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612e94565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612c9c916fffffffffffffffffffffffffffffffff9081169116613974565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990612d839084908151151581526020808301516fffffffffffffffffffffffffffffffff90811691830191909152604092830151169181019190915260600190565b60405180910390a1505050565b60006106a48261398a565b6000808080612daa8686613995565b909450925050505b9250929050565b336001600160a01b03821603612e2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016106ee565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000612eb385612ea4848661523c565b612eae90876154aa565b613974565b95945050505050565b6000610e5d83836139a4565b6000611e3384846001600160a01b0385166139c1565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b6101000151604051602001612f749897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b6040516020818303038152906040528051906020012085610120015180519060200120866101400151604051602001612fad9190615b65565b60405160208183030381529060405280519060200120876101600151604051602001612fd99190615bd2565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff168467ffffffffffffffff16146130b2576040517f1279ec8a00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024016106ee565b600a5468010000000000000000900461ffff1683111561310a576040517f099d3f7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016106ee565b80831461314f576040517f8808f8e700000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016106ee565b600a54640100000000900463ffffffff168211156131b357600a546040517f8693378900000000000000000000000000000000000000000000000000000000815264010000000090910463ffffffff166004820152602481018390526044016106ee565b5050505050565b600060026131c9608085615215565b67ffffffffffffffff166131dd919061523c565b905060006010816131ef608087615253565b67ffffffffffffffff168152602081019190915260400160002054905081613219600160046151d3565b901b191681836003811115613230576132306143c5565b901b178060106000613243608088615253565b67ffffffffffffffff16815260208101919091526040016000205550505050565b6040517fb6113fce000000000000000000000000000000000000000000000000000000008152600090606090309063b6113fce906132aa90889088908890600401615c1c565b600060405180830381600087803b1580156132c457600080fd5b505af19250505080156132d5575060015b613314573d808015613303576040519150601f19603f3d011682016040523d82523d6000602084013e613308565b606091505b50600392509050613329565b50506040805160208101909152600081526002905b935093915050565b6040805180820190915260008082526020820152600061335484602001516139de565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063bbe4f6db90602401602060405180830381865afa1580156133d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133fd9190615da5565b90506001600160a01b038116158061344557506134436001600160a01b0382167faff2afbf00000000000000000000000000000000000000000000000000000000612b8f565b155b15613487576040517fae9b4ce90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ee565b60008061349f8885896060015163ffffffff16613a84565b91509150600080600061359d6040518061010001604052808e81526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020018d6001600160a01b031681526020018f8152602001896001600160a01b031681526020018c6000015181526020018c6040015181526020018b8152506040516024016135399190615dc2565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f390775370000000000000000000000000000000000000000000000000000000017905287866113886084613bc7565b925092509250826135dc57816040517fe1cd55090000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b81516020146136245781516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016106ee565b60008280602001905181019061363a91906159c3565b9050866001600160a01b03168c6001600160a01b0316146136cf57600061366b8d8a613666868a6151d3565b613a84565b5090508681108061368557508161368288836151d3565b14155b156136cd576040517fa966e21f0000000000000000000000000000000000000000000000000000000081526004810183905260248101889052604481018290526064016106ee565b505b604080518082019091526001600160a01b039098168852602088015250949550505050505095945050505050565b6000610e5d836001600160a01b038416613ced565b81516040517fd02641a00000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260009182919084169063d02641a0906024016040805180830381865afa158015613778573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379c9190615e8f565b5190507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81166000036138055783516040517f9a655f7b0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016106ee565b6020840151611e33907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff831690613cf9565b61184f6003826000613d36565b600061386c827f01ffc9a7000000000000000000000000000000000000000000000000000000006138a4565b80156106a4575061389d827fffffffff000000000000000000000000000000000000000000000000000000006138a4565b1592915050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000082166024820152600090819060440160408051601f19818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825192935060009283928392909183918a617530fa92503d9150600051905082801561395d575060208210155b80156139695750600081115b979650505050505050565b60008183106139835781610e5d565b5090919050565b60006106a482614085565b6000808080612daa8686614090565b60008181526002830160205260408120819055610e5d83836140bb565b60008281526002840160205260408120829055611e3384846140c7565b60008151602014613a1d57816040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b600082806020019051810190613a3391906159c3565b90506001600160a01b03811180613a4b575061040081105b156106a457826040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b6000806000806000613b1388604051602401613aaf91906001600160a01b0391909116815260200190565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a082310000000000000000000000000000000000000000000000000000000017905288886113886084613bc7565b92509250925082613b5257816040517fe1cd55090000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b6020825114613b9a5781516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016106ee565b81806020019051810190613bae91906159c3565b613bb882886151d3565b94509450505050935093915050565b6000606060008361ffff1667ffffffffffffffff811115613bea57613bea614479565b6040519080825280601f01601f191660200182016040528015613c14576020820181803683370190505b509150863b613c47577f0c3b563c0000000000000000000000000000000000000000000000000000000060005260046000fd5b5a85811015613c7a577fafa32a2c0000000000000000000000000000000000000000000000000000000060005260046000fd5b8590036040810481038710613cb3577f37c3be290000000000000000000000000000000000000000000000000000000060005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d84811115613cd65750835b808352806000602085013e50955095509592505050565b6000610e5d83836140d3565b6000670de0b6b3a7640000613d2c837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff861661523c565b610e5d9190615ef1565b825474010000000000000000000000000000000000000000900460ff161580613d5d575081155b15613d6757505050565b825460018401546fffffffffffffffffffffffffffffffff80831692911690600090613dad90700100000000000000000000000000000000900463ffffffff16426151d3565b90508015613e6d5781831115613def576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001860154613e299083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612e94565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b84821015613f0a576001600160a01b038416613ebf576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016106ee565b6040517f1a76572a00000000000000000000000000000000000000000000000000000000815260048101839052602481018690526001600160a01b03851660448201526064016106ee565b848310156140035760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16906000908290613f4e90826151d3565b613f58878a6151d3565b613f6291906154aa565b613f6c9190615ef1565b90506001600160a01b038616613fb8576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016106ee565b6040517fd0c8d23a00000000000000000000000000000000000000000000000000000000815260048101829052602481018690526001600160a01b03871660448201526064016106ee565b61400d85846151d3565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b60006106a4826140df565b6000808061409e85856140e9565b600081815260029690960160205260409095205494959350505050565b6000610e5d83836140f5565b6000610e5d83836141ef565b6000610e5d838361423e565b60006106a4825490565b6000610e5d8383614256565b600081815260018301602052604081205480156141de5760006141196001836151d3565b855490915060009061412d906001906151d3565b905081811461419257600086600001828154811061414d5761414d615294565b906000526020600020015490508087600001848154811061417057614170615294565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806141a3576141a3615f05565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506106a4565b60009150506106a4565b5092915050565b6000818152600183016020526040812054614236575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556106a4565b5060006106a4565b60008181526001830160205260408120541515610e5d565b600082600001828154811061426d5761426d615294565b9060005260206000200154905092915050565b8280548282559060005260206000209081019282156142ed579160200282015b828111156142ed57825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039091161782556020909201916001909101906142a0565b506142f99291506142fd565b5090565b5b808211156142f957600081556001016142fe565b60e081016106a482846001600160a01b03808251168352602082015167ffffffffffffffff808216602086015280604085015116604086015250508060608301511660608401528060808301511660808401528060a08301511660a08401528060c08301511660c0840152505050565b67ffffffffffffffff8116811461184f57600080fd5b80356143a381614382565b919050565b6000602082840312156143ba57600080fd5b8135610e5d81614382565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110614404576144046143c5565b9052565b602081016106a482846143f4565b60005b83811015614431578181015183820152602001614419565b50506000910152565b60008151808452614452816020860160208601614416565b601f01601f19169290920160200192915050565b602081526000610e5d602083018461443a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156144cb576144cb614479565b60405290565b6040516080810167ffffffffffffffff811182821017156144cb576144cb614479565b6040516101a0810167ffffffffffffffff811182821017156144cb576144cb614479565b604051601f8201601f1916810167ffffffffffffffff8111828210171561454157614541614479565b604052919050565b600067ffffffffffffffff82111561456357614563614479565b5060051b60200190565b6001600160a01b038116811461184f57600080fd5b80356143a38161456d565b600082601f83011261459e57600080fd5b813560206145b36145ae83614549565b614518565b8083825260208201915060208460051b8701019350868411156145d557600080fd5b602086015b848110156145fa5780356145ed8161456d565b83529183019183016145da565b509695505050505050565b803560ff811681146143a357600080fd5b600067ffffffffffffffff82111561463057614630614479565b50601f01601f191660200190565b600082601f83011261464f57600080fd5b813561465d6145ae82614616565b81815284602083860101111561467257600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c087890312156146a857600080fd5b863567ffffffffffffffff808211156146c057600080fd5b6146cc8a838b0161458d565b975060208901359150808211156146e257600080fd5b6146ee8a838b0161458d565b96506146fc60408a01614605565b9550606089013591508082111561471257600080fd5b61471e8a838b0161463e565b945061472c60808a01614398565b935060a089013591508082111561474257600080fd5b5061474f89828a0161463e565b9150509295509295509295565b60008151808452602080850194506020840160005b838110156147965781516001600160a01b031687529582019590820190600101614771565b509495945050505050565b602081526000610e5d602083018461475c565b6000602082840312156147c657600080fd5b8135610e5d8161456d565b60a081016106a4828463ffffffff8082511683528060208301511660208401525061ffff604082015116604083015260608101516001600160a01b03808216606085015280608084015116608085015250505050565b60006020828403121561483957600080fd5b813567ffffffffffffffff81111561485057600080fd5b820160a08185031215610e5d57600080fd5b600082601f83011261487357600080fd5b813560206148836145ae83614549565b82815260069290921b840181019181810190868411156148a257600080fd5b8286015b848110156145fa57604081890312156148bf5760008081fd5b6148c76144a8565b81356148d28161456d565b8152818501356148e18161456d565b818601528352918301916040016148a6565b6000806040838503121561490657600080fd5b823567ffffffffffffffff8082111561491e57600080fd5b61492a86838701614862565b9350602085013591508082111561494057600080fd5b5061494d85828601614862565b9150509250929050565b801515811461184f57600080fd5b80356143a381614957565b60006040828403121561498257600080fd5b61498a6144a8565b905081356149978161456d565b808252506020820135602082015292915050565b600082601f8301126149bc57600080fd5b813560206149cc6145ae83614549565b8083825260208201915060208460061b8701019350868411156149ee57600080fd5b602086015b848110156145fa57614a058882614970565b8352918301916040016149f3565b600082601f830112614a2457600080fd5b81356020614a346145ae83614549565b82815260059290921b84018101918181019086841115614a5357600080fd5b8286015b848110156145fa57803567ffffffffffffffff811115614a775760008081fd5b614a858986838b010161463e565b845250918301918301614a57565b600082601f830112614aa457600080fd5b81356020614ab46145ae83614549565b82815260059290921b84018101918181019086841115614ad357600080fd5b8286015b848110156145fa57803567ffffffffffffffff811115614af75760008081fd5b614b058986838b0101614a13565b845250918301918301614ad7565b600082601f830112614b2457600080fd5b81356020614b346145ae83614549565b8083825260208201915060208460051b870101935086841115614b5657600080fd5b602086015b848110156145fa5780358352918301918301614b5b565b600060808284031215614b8457600080fd5b614b8c6144d1565b9050813567ffffffffffffffff80821115614ba657600080fd5b818401915084601f830112614bba57600080fd5b81356020614bca6145ae83614549565b82815260059290921b84018101918181019088841115614be957600080fd5b8286015b84811015614d4557803586811115614c0457600080fd5b87016101a0818c03601f19011215614c1b57600080fd5b614c236144f4565b614c2e868301614398565b8152614c3c60408301614582565b86820152614c4c60608301614582565b6040820152614c5d60808301614398565b606082015260a08201356080820152614c7860c08301614965565b60a0820152614c8960e08301614398565b60c0820152610100614c9c818401614582565b60e083015261012080840135828401526101409150818401358a811115614cc257600080fd5b614cd08f8a8388010161463e565b828501525050610160808401358a811115614cea57600080fd5b614cf88f8a838801016149ab565b83850152506101809150818401358a811115614d1357600080fd5b614d218f8a83880101614a13565b91840191909152506101a09290920135918101919091528352918301918301614bed565b5086525085810135935082841115614d5c57600080fd5b614d6887858801614a93565b90850152506040840135915080821115614d8157600080fd5b50614d8e84828501614b13565b6040830152506060820135606082015292915050565b63ffffffff8116811461184f57600080fd5b600082601f830112614dc757600080fd5b81356020614dd76145ae83614549565b8083825260208201915060208460051b870101935086841115614df957600080fd5b602086015b848110156145fa578035614e1181614da4565b8352918301918301614dfe565b6000806040808486031215614e3257600080fd5b833567ffffffffffffffff80821115614e4a57600080fd5b614e5687838801614b72565b9450602091508186013581811115614e6d57600080fd5b8601601f81018813614e7e57600080fd5b8035614e8c6145ae82614549565b81815260059190911b8201840190848101908a831115614eab57600080fd5b8584015b83811015614f1e57803586811115614ec75760008081fd5b8501808d03601f1901891315614edd5760008081fd5b614ee56144a8565b8882013581528982013588811115614efd5760008081fd5b614f0b8f8b83860101614db6565b828b015250845250918601918601614eaf565b50809750505050505050509250929050565b60008083601f840112614f4257600080fd5b50813567ffffffffffffffff811115614f5a57600080fd5b6020830191508360208260051b8501011115612db257600080fd5b60008060008060008060008060e0898b031215614f9157600080fd5b606089018a811115614fa257600080fd5b8998503567ffffffffffffffff80821115614fbc57600080fd5b818b0191508b601f830112614fd057600080fd5b813581811115614fdf57600080fd5b8c6020828501011115614ff157600080fd5b6020830199508098505060808b013591508082111561500f57600080fd5b61501b8c838d01614f30565b909750955060a08b013591508082111561503457600080fd5b506150418b828c01614f30565b999c989b50969995989497949560c00135949350505050565b6000806000806060858703121561507057600080fd5b843567ffffffffffffffff8082111561508857600080fd5b908601906101a0828903121561509d57600080fd5b909450602086013590808211156150b357600080fd5b6150bf88838901614f30565b909550935060408701359150808211156150d857600080fd5b506150e587828801614db6565b91505092959194509250565b80356fffffffffffffffffffffffffffffffff811681146143a357600080fd5b60006060828403121561512357600080fd5b6040516060810181811067ffffffffffffffff8211171561514657615146614479565b604052823561515481614957565b8152615162602084016150f1565b6020820152615173604084016150f1565b60408201529392505050565b604081526000615192604083018561475c565b8281036020840152612eb3818561475c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106a4576106a46151a4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600067ffffffffffffffff80841680615230576152306151e6565b92169190910692915050565b80820281158282048414176106a4576106a46151a4565b600067ffffffffffffffff8084168061526e5761526e6151e6565b92169190910492915050565b602081016003831061528e5761528e6143c5565b91905290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600063ffffffff8083168181036152dc576152dc6151a4565b6001019392505050565b600061012063ffffffff808d1684528b6020850152808b166040850152508060608401526153168184018a61475c565b9050828103608084015261532a818961475c565b905060ff871660a084015282810360c0840152615347818761443a565b905067ffffffffffffffff851660e084015282810361010084015261536c818561443a565b9c9b505050505050505050505050565b60006020828403121561538e57600080fd5b8151610e5d81614382565b600082601f8301126153aa57600080fd5b81516153b86145ae82614616565b8181528460208386010111156153cd57600080fd5b611e33826020830160208701614416565b6000602082840312156153f057600080fd5b815167ffffffffffffffff8082111561540857600080fd5b908301906080828603121561541c57600080fd5b6154246144d1565b82518281111561543357600080fd5b61543f87828601615399565b82525060208301518281111561545457600080fd5b61546087828601615399565b60208301525060408301518281111561547857600080fd5b61548487828601615399565b6040830152506060830151925061549a83614da4565b6060810192909252509392505050565b808201808211156106a4576106a46151a4565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126154f257600080fd5b83018035915067ffffffffffffffff82111561550d57600080fd5b6020019150600681901b3603821315612db257600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261555a57600080fd5b83018035915067ffffffffffffffff82111561557557600080fd5b6020019150600581901b3603821315612db257600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126155c257600080fd5b83018035915067ffffffffffffffff8211156155dd57600080fd5b602001915036819003821315612db257600080fd5b60008151808452602080850194506020840160005b8381101561479657815180516001600160a01b031688528301518388015260409096019590820190600101615607565b608081528451608082015267ffffffffffffffff60208601511660a08201526000604086015160a060c084015261567261012084018261443a565b905060608701517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80808584030160e08601526156ae838361443a565b9250608089015191508085840301610100860152506156cd82826155f2565b925050506156e1602083018661ffff169052565b836040830152612eb360608301846001600160a01b03169052565b60008060006060848603121561571157600080fd5b835161571c81614957565b602085015190935067ffffffffffffffff81111561573957600080fd5b61574586828701615399565b925050604084015190509250925092565b600060a0828403121561576857600080fd5b60405160a0810181811067ffffffffffffffff8211171561578b5761578b614479565b604052825161579981614da4565b815260208301516157a981614da4565b6020820152604083015161ffff811681146157c357600080fd5b604082015260608301516157d68161456d565b606082015260808301516157e98161456d565b60808201529392505050565b610180810161586682856001600160a01b03808251168352602082015167ffffffffffffffff808216602086015280604085015116604086015250508060608301511660608401528060808301511660808401528060a08301511660a08401528060c08301511660c0840152505050565b825163ffffffff90811660e0840152602084015116610100830152604083015161ffff1661012083015260608301516001600160a01b03908116610140840152608084015116610160830152610e5d565b60006101208b83526001600160a01b038b16602084015267ffffffffffffffff808b1660408501528160608501526158f18285018b61475c565b91508382036080850152615905828a61475c565b915060ff881660a085015283820360c0850152615922828861443a565b90861660e0850152838103610100850152905061536c818561443a565b60006020828403121561595157600080fd5b8151610e5d81614957565b60008151808452602080850194506020840160005b8381101561479657815187529582019590820190600101615971565b6060815260006159a0606083018661595c565b82810360208401526159b2818661595c565b915050826040830152949350505050565b6000602082840312156159d557600080fd5b5051919050565b67ffffffffffffffff8181168382160190808211156141e8576141e86151a4565b67ffffffffffffffff8316815260408101610e5d60208301846143f4565b600067ffffffffffffffff8083168181036152dc576152dc6151a4565b615a4281846143f4565b604060208201526000611e33604083018461443a565b600060208284031215615a6a57600080fd5b813567ffffffffffffffff811115615a8157600080fd5b611e3384828501614b72565b600060408284031215615a9f57600080fd5b610e5d8383614970565b600060208284031215615abb57600080fd5b813567ffffffffffffffff80821115615ad357600080fd5b9083019060808286031215615ae757600080fd5b615aef6144d1565b823582811115615afe57600080fd5b615b0a8782860161463e565b825250602083013582811115615b1f57600080fd5b615b2b8782860161463e565b602083015250604083013582811115615b4357600080fd5b615b4f8782860161463e565b6040830152506060830135925061549a83614da4565b602081526000610e5d60208301846155f2565b60008282518085526020808601955060208260051b8401016020860160005b84811015615bc557601f19868403018952615bb383835161443a565b98840198925090830190600101615b97565b5090979650505050505050565b602081526000610e5d6020830184615b78565b60008151808452602080850194506020840160005b8381101561479657815163ffffffff1687529582019590820190600101615bfa565b60608152615c3760608201855167ffffffffffffffff169052565b60006020850151615c5360808401826001600160a01b03169052565b5060408501516001600160a01b03811660a084015250606085015167ffffffffffffffff811660c084015250608085015160e083015260a0850151610100615c9e8185018315159052565b60c08701519150610120615cbd8186018467ffffffffffffffff169052565b60e08801519250610140615cdb818701856001600160a01b03169052565b828901519350610160925083838701528189015193506101a091506101808281880152615d0c61020088018661443a565b9450818a015191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0808887030184890152615d4886846155f2565b948b01518886039091016101c0890152939450615d658585615b78565b9450808a01516101e0880152505050508281036020840152615d878186615b78565b90508281036040840152615d9b8185615be5565b9695505050505050565b600060208284031215615db757600080fd5b8151610e5d8161456d565b6020815260008251610100806020850152615de161012085018361443a565b91506020850151615dfe604086018267ffffffffffffffff169052565b5060408501516001600160a01b038116606086015250606085015160808501526080850151615e3860a08601826001600160a01b03169052565b5060a0850151601f19808685030160c0870152615e55848361443a565b935060c08701519150808685030160e0870152615e72848361443a565b935060e0870151915080868503018387015250615d9b838261443a565b600060408284031215615ea157600080fd5b615ea96144a8565b82517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81168114615ed557600080fd5b81526020830151615ee581614da4565b60208201529392505050565b600082615f0057615f006151e6565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000818000a0000000000000000000000003cf8eeaa60538317bc5f3da77dc004f9c59d73a00000000000000000000000000000000000000000000000002220395d5f2affe100000000000000000000000000000000000000000000000045849994fc9c7b15000000000000000000000000c5dbe2055fa233ece44c99432526f3fc46ca3fc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a1000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d54000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101825760003560e01c806385572ffb116100d8578063afcb95d71161008c578063c92b283211610066578063c92b2832146105f3578063f077b59214610606578063f2fde38b1461061c57600080fd5b8063afcb95d7146105ad578063b1dc65a4146105cd578063b6113fce146105e057600080fd5b8063873504d7116100bd578063873504d7146105765780638926c4ee146105895780638da5cb5b1461059c57600080fd5b806385572ffb1461053c578063856c82471461054a57600080fd5b8063599f64311161013a5780637437ff9f116101145780637437ff9f1461046157806379ba50971461050457806381ff70481461050c57600080fd5b8063599f643114610414578063666cab8d14610439578063704b6c021461044e57600080fd5b8063181f5a771161016b578063181f5a77146103525780631ef381741461039b578063546719cd146103b057600080fd5b806306285c6914610187578063142a98fc14610332575b600080fd5b61031c6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091526040518060e001604052807f0000000000000000000000003cf8eeaa60538317bc5f3da77dc004f9c59d73a06001600160a01b031681526020017f0000000000000000000000000000000000000000000000002220395d5f2affe167ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000045849994fc9c7b1567ffffffffffffffff1681526020017f000000000000000000000000c5dbe2055fa233ece44c99432526f3fc46ca3fc26001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a16001600160a01b031681526020017f000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d546001600160a01b0316815250905090565b6040516103299190614312565b60405180910390f35b6103456103403660046143a8565b61062f565b6040516103299190614408565b61038e6040518060400160405280601481526020017f45564d3245564d4f666652616d7020312e352e3000000000000000000000000081525081565b6040516103299190614466565b6103ae6103a936600461468f565b6106aa565b005b6103b8610a9e565b604051610329919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b6002546001600160a01b03165b6040516001600160a01b039091168152602001610329565b610441610b53565b60405161032991906147a1565b6103ae61045c3660046147b4565b610bb5565b6104f76040805160a081018252600080825260208201819052918101829052606081018290526080810191909152506040805160a081018252600a5463ffffffff8082168352640100000000820416602083015268010000000000000000810461ffff16928201929092526a01000000000000000000009091046001600160a01b039081166060830152600b5416608082015290565b60405161032991906147d1565b6103ae610c7e565b6007546005546040805163ffffffff80851682526401000000009094049093166020840152820152606001610329565b6103ae610182366004614827565b61055d6105583660046147b4565b610d61565b60405167ffffffffffffffff9091168152602001610329565b6103ae6105843660046148f3565b610e64565b6103ae610597366004614e1e565b611037565b6000546001600160a01b0316610421565b604080516001815260006020820181905291810191909152606001610329565b6103ae6105db366004614f75565b61129c565b6103ae6105ee36600461505a565b6114a7565b6103ae610601366004615111565b6117e7565b61060e611852565b60405161032992919061517f565b6103ae61062a3660046147b4565b611978565b600061063d600160046151d3565b600261064a608085615215565b67ffffffffffffffff1661065e919061523c565b6010600061066d608087615253565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002054901c1660038111156106a4576106a46143c5565b92915050565b84518460ff16601f8211156106f75760016040517f367f56a20000000000000000000000000000000000000000000000000000000081526004016106ee919061527a565b60405180910390fd5b806000036107345760006040517f367f56a20000000000000000000000000000000000000000000000000000000081526004016106ee919061527a565b61073c611989565b610745856119ff565b60095460005b818110156107bc57600860006009838154811061076a5761076a615294565b60009182526020808320909101546001600160a01b03168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905560010161074b565b5050865160005b8181101561095f5760008982815181106107df576107df615294565b60200260200101519050600060028111156107fc576107fc6143c5565b6001600160a01b038216600090815260086020526040902054610100900460ff16600281111561082e5761082e6143c5565b146108685760026040517f367f56a20000000000000000000000000000000000000000000000000000000081526004016106ee919061527a565b6001600160a01b0381166108a8576040517fd6c62c9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805180820190915260ff8316815260208101600290526001600160a01b03821660009081526008602090815260409091208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561094b5761094b6143c5565b0217905550905050508060010190506107c3565b5087516109739060099060208b0190614280565b506006805460ff838116610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909216908a1617179055600780546109f99146913091906000906109cb9063ffffffff166152c3565b91906101000a81548163ffffffff021916908363ffffffff160217905563ffffffff168c8c8c8c8c8c611cc9565b6005819055600780544363ffffffff9081166401000000009081027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff841681179094556040519083048216947f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0594610a8a9487949293918316921691909117908f908f908f908f908f908f906152e6565b60405180910390a150505050505050505050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040805160a0810182526003546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff1660208501527401000000000000000000000000000000000000000090920460ff161515938301939093526004548084166060840152049091166080820152610b4e90611d56565b905090565b60606009805480602002602001604051908101604052809291908181526020018280548015610bab57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b8d575b5050505050905090565b6000546001600160a01b03163314801590610bdb57506002546001600160a01b03163314155b15610c12576040517ff6cd562000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f8fe72c3e0020beb3234e76ae6676fa576fbfcae600af1c4fea44784cf0db329c9060200160405180910390a150565b6001546001600160a01b03163314610cf2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016106ee565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6001600160a01b0381166000908152600f602052604081205467ffffffffffffffff168082036106a4577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316156106a4576040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063856c824790602401602060405180830381865afa158015610e39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5d919061537c565b9392505050565b610e6c611989565b60005b8251811015610f3f57610ea9838281518110610e8d57610e8d615294565b602002602001015160200151600c611e0890919063ffffffff16565b15610f37577fcbf3cbeaed4ac1d605ed30f4af06c35acaeff2379db7f6146c9cceee83d58782838281518110610ee157610ee1615294565b602002602001015160000151848381518110610eff57610eff615294565b602002602001015160200151604051610f2e9291906001600160a01b0392831681529116602082015260400190565b60405180910390a15b600101610e6f565b5060005b815181101561103257610f9c828281518110610f6157610f61615294565b602002602001015160200151838381518110610f7f57610f7f615294565b602002602001015160000151600c611e1d9092919063ffffffff16565b1561102a577ffc23abf7ddbd3c02b1420dafa2355c56c1a06fbb8723862ac14d6bd74177361a828281518110610fd457610fd4615294565b602002602001015160000151838381518110610ff257610ff2615294565b6020026020010151602001516040516110219291906001600160a01b0392831681529116602082015260400190565b60405180910390a15b600101610f43565b505050565b61103f611e3b565b8151518151811461107c576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156112915760008460000151828151811061109f5761109f615294565b6020026020010151905060008483815181106110bd576110bd615294565b60209081029190910101518051909150801561112c57826080015181101561112c5761018083015160808401516040517f9c6db58d00000000000000000000000000000000000000000000000000000000815260048101929092526024820152604481018290526064016106ee565b816020015151836101400151511461118e5761018083015160608401516040517f85d2e5bf000000000000000000000000000000000000000000000000000000008152600481019290925267ffffffffffffffff1660248201526044016106ee565b61016083015160005b846101400151518110156112815760008287815181106111b9576111b9615294565b60200260200101518060200190518101906111d491906153de565b90506000856020015183815181106111ee576111ee615294565b602002602001015163ffffffff169050806000141580156112185750816060015163ffffffff1681105b156112775761018087015160608301516040517fef0c635200000000000000000000000000000000000000000000000000000000815260048101929092526024820185905263ffffffff166044820152606481018290526084016106ee565b5050600101611197565b505050505080600101905061107f565b506110328383611ebc565b6112a6878761290f565b6005548835908082146112ef576040517f93df584c00000000000000000000000000000000000000000000000000000000815260048101829052602481018390526044016106ee565b6112f7611e3b565b6040805183815260208c81013560081c63ffffffff16908201527fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a13360009081526008602090815260408083208151808301909252805460ff8082168452929391929184019161010090910416600281111561137f5761137f6143c5565b6002811115611390576113906143c5565b90525090506002816020015160028111156113ad576113ad6143c5565b1480156113e757506009816000015160ff16815481106113cf576113cf615294565b6000918252602090912001546001600160a01b031633145b61141d576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600061142b85602061523c565b61143688602061523c565b6114428b6101446154aa565b61144c91906154aa565b61145691906154aa565b905036811461149a576040517f8e1192e1000000000000000000000000000000000000000000000000000000008152600481018290523660248201526044016106ee565b5050505050505050505050565b3330146114e0576040517f371a732800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516000808252602082019092528161151d565b60408051808201909152600080825260208201528152602001906001900390816114f65790505b50905060006115306101408701876154bd565b905011156115a7576115a46115496101408701876154bd565b6115596040890160208a016147b4565b604080516001600160a01b0390921660208301520160408051601f1981840301815291815261158e9060608b01908b016147b4565b61159c6101608b018b615525565b8a8a8a612966565b90505b6115b561012086018661558d565b15905080156115c657506080850135155b806115e857506115dc60608601604087016147b4565b6001600160a01b03163b155b8061163357506116317f85572ffb0000000000000000000000000000000000000000000000000000000061162260608801604089016147b4565b6001600160a01b031690612b8f565b155b1561163e57506117e1565b600a546040805160a08101909152610180870135815260009182916a01000000000000000000009091046001600160a01b031690633cf979839060208082019061168a908c018c6143a8565b67ffffffffffffffff1681526020018a60200160208101906116ac91906147b4565b604080516001600160a01b0390921660208301520160408051601f1981840301815291905281526020016116e46101208c018c61558d565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200186905261138860808b013561173960608d0160408e016147b4565b6040518563ffffffff1660e01b81526004016117589493929190615637565b6000604051808303816000875af1158015611777573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261179f91908101906156fc565b5091509150816117dd57806040517f0a8d6e8c0000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b5050505b50505050565b6000546001600160a01b0316331480159061180d57506002546001600160a01b03163314155b15611844576040517ff6cd562000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61184f600382612bab565b50565b6060806000611861600c612d90565b90508067ffffffffffffffff81111561187c5761187c614479565b6040519080825280602002602001820160405280156118a5578160200160208202803683370190505b5092508067ffffffffffffffff8111156118c1576118c1614479565b6040519080825280602002602001820160405280156118ea578160200160208202803683370190505b50915060005b8181101561197257600080611906600c84612d9b565b915091508086848151811061191d5761191d615294565b60200260200101906001600160a01b031690816001600160a01b0316815250508185848151811061195057611950615294565b6001600160a01b039092166020928302919091019091015250506001016118f0565b50509091565b611980611989565b61184f81612db9565b6000546001600160a01b031633146119fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016106ee565b565b600081806020019051810190611a159190615756565b60608101519091506001600160a01b0316611a5c576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600a805460208085015160408087015160608089015163ffffffff9889167fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909716969096176401000000009890941697909702929092177fffff00000000000000000000000000000000000000000000ffffffffffffffff166801000000000000000061ffff909316929092027fffff0000000000000000000000000000000000000000ffffffffffffffffffff16919091176a01000000000000000000006001600160a01b039485160217909355608080860151600b80547fffffffffffffffffffffffff000000000000000000000000000000000000000016918516919091179055835160e0810185527f0000000000000000000000003cf8eeaa60538317bc5f3da77dc004f9c59d73a0841681527f0000000000000000000000000000000000000000000000002220395d5f2affe167ffffffffffffffff908116938201939093527f00000000000000000000000000000000000000000000000045849994fc9c7b15909216828501527f000000000000000000000000c5dbe2055fa233ece44c99432526f3fc46ca3fc28316948201949094527f00000000000000000000000000000000000000000000000000000000000000008216938101939093527f00000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a1811660a08401527f000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d541660c0830152517f7879e20bb60a503429de4a2c912b5904f08a39f2af054c10fb46434b5d61126091611cbd9184906157f5565b60405180910390a15050565b6000808a8a8a8a8a8a8a8a8a604051602001611ced999897969594939291906158b7565b60408051601f1981840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611de482606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611dc891906151d3565b85608001516fffffffffffffffffffffffffffffffff16612e94565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b6000610e5d836001600160a01b038416612ebc565b6000611e33846001600160a01b03851684612ec8565b949350505050565b467f00000000000000000000000000000000000000000000000000000000000b67d2146119fd576040517f0f01ce850000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000b67d260048201524660248201526044016106ee565b6040517f2cbc26bb0000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000045849994fc9c7b1560801b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a16001600160a01b031690632cbc26bb90602401602060405180830381865afa158015611f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9a919061593f565b15611fd1576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815151600081900361200e576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015151811461204c576040517f57e0e08300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008167ffffffffffffffff81111561206757612067614479565b604051908082528060200260200182016040528015612090578160200160208202803683370190505b50905060005b82811015612168576000856000015182815181106120b6576120b6615294565b602002602001015190506120ea817f5f48dec56228e535a354c719542a1d4a3cbe73ae15ba623b47568def37c0ad11612ede565b8383815181106120fc576120fc615294565b60200260200101818152505080610180015183838151811061212057612120615294565b60200260200101511461215f576040517f7185cf6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600101612096565b508251604080860151606087015191517f32048875000000000000000000000000000000000000000000000000000000008152921515926000926001600160a01b037f0000000000000000000000003cf8eeaa60538317bc5f3da77dc004f9c59d73a016926332048875926121e29288929160040161598d565b602060405180830381865afa1580156121ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222391906159c3565b90508060000361225f576040517fea75680100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b848110156117dd5760008760000151828151811061228257612282615294565b60200260200101519050600061229b826060015161062f565b905060008160038111156122b1576122b16143c5565b14806122ce575060038160038111156122cc576122cc6143c5565b145b61231457816060015167ffffffffffffffff167fe3dd0bec917c965a133ddb2c84874725ee1e2fd8d763c19efa36d6a11cd82b1f60405160405180910390a25050612907565b606085156123fa5788848151811061232e5761232e615294565b6020908102919091018101510151600a5490915060009063ffffffff1661235587426151d3565b119050808061237557506003836003811115612373576123736143c5565b145b6123ab576040517f6358b0d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8985815181106123bd576123bd615294565b6020026020010151600001516000146123f4578985815181106123e2576123e2615294565b60209081029190910101515160808501525b5061245e565b600082600381111561240e5761240e6143c5565b1461245e57606083015160405167ffffffffffffffff90911681527f67d9ba0f63d427c482c2736300e6d5a34c6691dbcdea8ad35828a1f1ba47e8729060200160405180910390a1505050612907565b60c083015167ffffffffffffffff16156126df576020808401516001600160a01b03166000908152600f909152604081205467ffffffffffffffff1690819003612649577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316156126495760208401516040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201527f00000000000000000000000000000000000000000000000000000000000000009091169063856c824790602401602060405180830381865afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b919061537c565b60c085015190915067ffffffffffffffff166125988260016159dc565b67ffffffffffffffff16146125f95783602001516001600160a01b03168460c0015167ffffffffffffffff167fe44a20935573a783dd0d5991c92d7b6a0eb3173566530364db3ec10e9a990b5d60405160405180910390a350505050612907565b6020848101516001600160a01b03166000908152600f9091526040902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff83161790555b600083600381111561265d5761265d6143c5565b036126dd5760c084015167ffffffffffffffff1661267c8260016159dc565b67ffffffffffffffff16146126dd5783602001516001600160a01b03168460c0015167ffffffffffffffff167fd32ddb11d71e3d63411d37b09f9a8b28664f1cb1338bfd1413c173b0ebf4123760405160405180910390a350505050612907565b505b60008a6020015185815181106126f7576126f7615294565b602002602001015190506127238460600151856000015186610140015151876101200151518551613039565b612732846060015160016131ba565b600080612740868486613264565b915091506127528660600151836131ba565b88156127be57600382600381111561276c5761276c6143c5565b036127be576000856003811115612785576127856143c5565b146127be57806040517fcf19edfd0000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b60028260038111156127d2576127d26143c5565b1461282a5760038260038111156127eb576127eb6143c5565b1461282a578560600151826040517f9e2616030000000000000000000000000000000000000000000000000000000081526004016106ee9291906159fd565b60c086015167ffffffffffffffff16156128b2576000856003811115612852576128526143c5565b036128b2576020808701516001600160a01b03166000908152600f90915260408120805467ffffffffffffffff169161288a83615a1b565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b856101800151866060015167ffffffffffffffff167fd4f851956a5d67c3997d1c9205045fef79bae2947fdee7e9e2641abc7391ef6584846040516128f8929190615a38565b60405180910390a35050505050505b600101612262565b61296261291e82840184615a58565b604080516000808252602082019092529061295c565b6040805180820190915260008152606060208201528152602001906001900390816129345790505b50611ebc565b5050565b60608989808060200260200160405190810160405280939291908181526020016000905b828210156129b6576129a760408302860136819003810190615a8d565b8152602001906001019061298a565b505050505090506000805b8a811015612b715760008888838181106129dd576129dd615294565b90506020028101906129ef919061558d565b8101906129fc9190615aa9565b90508451600014612a5757848281518110612a1957612a19615294565b602002602001015163ffffffff16600014612a5757848281518110612a4057612a40615294565b602090810291909101015163ffffffff1660608201525b612ad78d8d84818110612a6c57612a6c615294565b905060400201602001358c8c848b8b88818110612a8b57612a8b615294565b9050602002810190612a9d919061558d565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061333192505050565b848381518110612ae957612ae9615294565b6020026020010181905250612b25848381518110612b0957612b09615294565b602002602001015160000151600c6136fd90919063ffffffff16565b15612b6857612b5b848381518110612b3f57612b3f615294565b6020908102919091010151600b546001600160a01b0316613712565b612b6590846154aa565b92505b506001016129c1565b508015612b8157612b8181613833565b509998505050505050505050565b6000612b9a83613840565b8015610e5d5750610e5d83836138a4565b8154600090612bd490700100000000000000000000000000000000900463ffffffff16426151d3565b90508015612c765760018301548354612c1c916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612e94565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612c9c916fffffffffffffffffffffffffffffffff9081169116613974565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990612d839084908151151581526020808301516fffffffffffffffffffffffffffffffff90811691830191909152604092830151169181019190915260600190565b60405180910390a1505050565b60006106a48261398a565b6000808080612daa8686613995565b909450925050505b9250929050565b336001600160a01b03821603612e2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016106ee565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000612eb385612ea4848661523c565b612eae90876154aa565b613974565b95945050505050565b6000610e5d83836139a4565b6000611e3384846001600160a01b0385166139c1565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b6101000151604051602001612f749897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b6040516020818303038152906040528051906020012085610120015180519060200120866101400151604051602001612fad9190615b65565b60405160208183030381529060405280519060200120876101600151604051602001612fd99190615bd2565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b7f00000000000000000000000000000000000000000000000045849994fc9c7b1567ffffffffffffffff168467ffffffffffffffff16146130b2576040517f1279ec8a00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024016106ee565b600a5468010000000000000000900461ffff1683111561310a576040517f099d3f7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016106ee565b80831461314f576040517f8808f8e700000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016106ee565b600a54640100000000900463ffffffff168211156131b357600a546040517f8693378900000000000000000000000000000000000000000000000000000000815264010000000090910463ffffffff166004820152602481018390526044016106ee565b5050505050565b600060026131c9608085615215565b67ffffffffffffffff166131dd919061523c565b905060006010816131ef608087615253565b67ffffffffffffffff168152602081019190915260400160002054905081613219600160046151d3565b901b191681836003811115613230576132306143c5565b901b178060106000613243608088615253565b67ffffffffffffffff16815260208101919091526040016000205550505050565b6040517fb6113fce000000000000000000000000000000000000000000000000000000008152600090606090309063b6113fce906132aa90889088908890600401615c1c565b600060405180830381600087803b1580156132c457600080fd5b505af19250505080156132d5575060015b613314573d808015613303576040519150601f19603f3d011682016040523d82523d6000602084013e613308565b606091505b50600392509050613329565b50506040805160208101909152600081526002905b935093915050565b6040805180820190915260008082526020820152600061335484602001516139de565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301529192506000917f000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d54169063bbe4f6db90602401602060405180830381865afa1580156133d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133fd9190615da5565b90506001600160a01b038116158061344557506134436001600160a01b0382167faff2afbf00000000000000000000000000000000000000000000000000000000612b8f565b155b15613487576040517fae9b4ce90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ee565b60008061349f8885896060015163ffffffff16613a84565b91509150600080600061359d6040518061010001604052808e81526020017f00000000000000000000000000000000000000000000000045849994fc9c7b1567ffffffffffffffff1681526020018d6001600160a01b031681526020018f8152602001896001600160a01b031681526020018c6000015181526020018c6040015181526020018b8152506040516024016135399190615dc2565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f390775370000000000000000000000000000000000000000000000000000000017905287866113886084613bc7565b925092509250826135dc57816040517fe1cd55090000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b81516020146136245781516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016106ee565b60008280602001905181019061363a91906159c3565b9050866001600160a01b03168c6001600160a01b0316146136cf57600061366b8d8a613666868a6151d3565b613a84565b5090508681108061368557508161368288836151d3565b14155b156136cd576040517fa966e21f0000000000000000000000000000000000000000000000000000000081526004810183905260248101889052604481018290526064016106ee565b505b604080518082019091526001600160a01b039098168852602088015250949550505050505095945050505050565b6000610e5d836001600160a01b038416613ced565b81516040517fd02641a00000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260009182919084169063d02641a0906024016040805180830381865afa158015613778573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379c9190615e8f565b5190507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81166000036138055783516040517f9a655f7b0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016106ee565b6020840151611e33907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff831690613cf9565b61184f6003826000613d36565b600061386c827f01ffc9a7000000000000000000000000000000000000000000000000000000006138a4565b80156106a4575061389d827fffffffff000000000000000000000000000000000000000000000000000000006138a4565b1592915050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000082166024820152600090819060440160408051601f19818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825192935060009283928392909183918a617530fa92503d9150600051905082801561395d575060208210155b80156139695750600081115b979650505050505050565b60008183106139835781610e5d565b5090919050565b60006106a482614085565b6000808080612daa8686614090565b60008181526002830160205260408120819055610e5d83836140bb565b60008281526002840160205260408120829055611e3384846140c7565b60008151602014613a1d57816040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b600082806020019051810190613a3391906159c3565b90506001600160a01b03811180613a4b575061040081105b156106a457826040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b6000806000806000613b1388604051602401613aaf91906001600160a01b0391909116815260200190565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a082310000000000000000000000000000000000000000000000000000000017905288886113886084613bc7565b92509250925082613b5257816040517fe1cd55090000000000000000000000000000000000000000000000000000000081526004016106ee9190614466565b6020825114613b9a5781516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016106ee565b81806020019051810190613bae91906159c3565b613bb882886151d3565b94509450505050935093915050565b6000606060008361ffff1667ffffffffffffffff811115613bea57613bea614479565b6040519080825280601f01601f191660200182016040528015613c14576020820181803683370190505b509150863b613c47577f0c3b563c0000000000000000000000000000000000000000000000000000000060005260046000fd5b5a85811015613c7a577fafa32a2c0000000000000000000000000000000000000000000000000000000060005260046000fd5b8590036040810481038710613cb3577f37c3be290000000000000000000000000000000000000000000000000000000060005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d84811115613cd65750835b808352806000602085013e50955095509592505050565b6000610e5d83836140d3565b6000670de0b6b3a7640000613d2c837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff861661523c565b610e5d9190615ef1565b825474010000000000000000000000000000000000000000900460ff161580613d5d575081155b15613d6757505050565b825460018401546fffffffffffffffffffffffffffffffff80831692911690600090613dad90700100000000000000000000000000000000900463ffffffff16426151d3565b90508015613e6d5781831115613def576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001860154613e299083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612e94565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b84821015613f0a576001600160a01b038416613ebf576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016106ee565b6040517f1a76572a00000000000000000000000000000000000000000000000000000000815260048101839052602481018690526001600160a01b03851660448201526064016106ee565b848310156140035760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16906000908290613f4e90826151d3565b613f58878a6151d3565b613f6291906154aa565b613f6c9190615ef1565b90506001600160a01b038616613fb8576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016106ee565b6040517fd0c8d23a00000000000000000000000000000000000000000000000000000000815260048101829052602481018690526001600160a01b03871660448201526064016106ee565b61400d85846151d3565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b60006106a4826140df565b6000808061409e85856140e9565b600081815260029690960160205260409095205494959350505050565b6000610e5d83836140f5565b6000610e5d83836141ef565b6000610e5d838361423e565b60006106a4825490565b6000610e5d8383614256565b600081815260018301602052604081205480156141de5760006141196001836151d3565b855490915060009061412d906001906151d3565b905081811461419257600086600001828154811061414d5761414d615294565b906000526020600020015490508087600001848154811061417057614170615294565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806141a3576141a3615f05565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506106a4565b60009150506106a4565b5092915050565b6000818152600183016020526040812054614236575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556106a4565b5060006106a4565b60008181526001830160205260408120541515610e5d565b600082600001828154811061426d5761426d615294565b9060005260206000200154905092915050565b8280548282559060005260206000209081019282156142ed579160200282015b828111156142ed57825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039091161782556020909201916001909101906142a0565b506142f99291506142fd565b5090565b5b808211156142f957600081556001016142fe565b60e081016106a482846001600160a01b03808251168352602082015167ffffffffffffffff808216602086015280604085015116604086015250508060608301511660608401528060808301511660808401528060a08301511660a08401528060c08301511660c0840152505050565b67ffffffffffffffff8116811461184f57600080fd5b80356143a381614382565b919050565b6000602082840312156143ba57600080fd5b8135610e5d81614382565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110614404576144046143c5565b9052565b602081016106a482846143f4565b60005b83811015614431578181015183820152602001614419565b50506000910152565b60008151808452614452816020860160208601614416565b601f01601f19169290920160200192915050565b602081526000610e5d602083018461443a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156144cb576144cb614479565b60405290565b6040516080810167ffffffffffffffff811182821017156144cb576144cb614479565b6040516101a0810167ffffffffffffffff811182821017156144cb576144cb614479565b604051601f8201601f1916810167ffffffffffffffff8111828210171561454157614541614479565b604052919050565b600067ffffffffffffffff82111561456357614563614479565b5060051b60200190565b6001600160a01b038116811461184f57600080fd5b80356143a38161456d565b600082601f83011261459e57600080fd5b813560206145b36145ae83614549565b614518565b8083825260208201915060208460051b8701019350868411156145d557600080fd5b602086015b848110156145fa5780356145ed8161456d565b83529183019183016145da565b509695505050505050565b803560ff811681146143a357600080fd5b600067ffffffffffffffff82111561463057614630614479565b50601f01601f191660200190565b600082601f83011261464f57600080fd5b813561465d6145ae82614616565b81815284602083860101111561467257600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c087890312156146a857600080fd5b863567ffffffffffffffff808211156146c057600080fd5b6146cc8a838b0161458d565b975060208901359150808211156146e257600080fd5b6146ee8a838b0161458d565b96506146fc60408a01614605565b9550606089013591508082111561471257600080fd5b61471e8a838b0161463e565b945061472c60808a01614398565b935060a089013591508082111561474257600080fd5b5061474f89828a0161463e565b9150509295509295509295565b60008151808452602080850194506020840160005b838110156147965781516001600160a01b031687529582019590820190600101614771565b509495945050505050565b602081526000610e5d602083018461475c565b6000602082840312156147c657600080fd5b8135610e5d8161456d565b60a081016106a4828463ffffffff8082511683528060208301511660208401525061ffff604082015116604083015260608101516001600160a01b03808216606085015280608084015116608085015250505050565b60006020828403121561483957600080fd5b813567ffffffffffffffff81111561485057600080fd5b820160a08185031215610e5d57600080fd5b600082601f83011261487357600080fd5b813560206148836145ae83614549565b82815260069290921b840181019181810190868411156148a257600080fd5b8286015b848110156145fa57604081890312156148bf5760008081fd5b6148c76144a8565b81356148d28161456d565b8152818501356148e18161456d565b818601528352918301916040016148a6565b6000806040838503121561490657600080fd5b823567ffffffffffffffff8082111561491e57600080fd5b61492a86838701614862565b9350602085013591508082111561494057600080fd5b5061494d85828601614862565b9150509250929050565b801515811461184f57600080fd5b80356143a381614957565b60006040828403121561498257600080fd5b61498a6144a8565b905081356149978161456d565b808252506020820135602082015292915050565b600082601f8301126149bc57600080fd5b813560206149cc6145ae83614549565b8083825260208201915060208460061b8701019350868411156149ee57600080fd5b602086015b848110156145fa57614a058882614970565b8352918301916040016149f3565b600082601f830112614a2457600080fd5b81356020614a346145ae83614549565b82815260059290921b84018101918181019086841115614a5357600080fd5b8286015b848110156145fa57803567ffffffffffffffff811115614a775760008081fd5b614a858986838b010161463e565b845250918301918301614a57565b600082601f830112614aa457600080fd5b81356020614ab46145ae83614549565b82815260059290921b84018101918181019086841115614ad357600080fd5b8286015b848110156145fa57803567ffffffffffffffff811115614af75760008081fd5b614b058986838b0101614a13565b845250918301918301614ad7565b600082601f830112614b2457600080fd5b81356020614b346145ae83614549565b8083825260208201915060208460051b870101935086841115614b5657600080fd5b602086015b848110156145fa5780358352918301918301614b5b565b600060808284031215614b8457600080fd5b614b8c6144d1565b9050813567ffffffffffffffff80821115614ba657600080fd5b818401915084601f830112614bba57600080fd5b81356020614bca6145ae83614549565b82815260059290921b84018101918181019088841115614be957600080fd5b8286015b84811015614d4557803586811115614c0457600080fd5b87016101a0818c03601f19011215614c1b57600080fd5b614c236144f4565b614c2e868301614398565b8152614c3c60408301614582565b86820152614c4c60608301614582565b6040820152614c5d60808301614398565b606082015260a08201356080820152614c7860c08301614965565b60a0820152614c8960e08301614398565b60c0820152610100614c9c818401614582565b60e083015261012080840135828401526101409150818401358a811115614cc257600080fd5b614cd08f8a8388010161463e565b828501525050610160808401358a811115614cea57600080fd5b614cf88f8a838801016149ab565b83850152506101809150818401358a811115614d1357600080fd5b614d218f8a83880101614a13565b91840191909152506101a09290920135918101919091528352918301918301614bed565b5086525085810135935082841115614d5c57600080fd5b614d6887858801614a93565b90850152506040840135915080821115614d8157600080fd5b50614d8e84828501614b13565b6040830152506060820135606082015292915050565b63ffffffff8116811461184f57600080fd5b600082601f830112614dc757600080fd5b81356020614dd76145ae83614549565b8083825260208201915060208460051b870101935086841115614df957600080fd5b602086015b848110156145fa578035614e1181614da4565b8352918301918301614dfe565b6000806040808486031215614e3257600080fd5b833567ffffffffffffffff80821115614e4a57600080fd5b614e5687838801614b72565b9450602091508186013581811115614e6d57600080fd5b8601601f81018813614e7e57600080fd5b8035614e8c6145ae82614549565b81815260059190911b8201840190848101908a831115614eab57600080fd5b8584015b83811015614f1e57803586811115614ec75760008081fd5b8501808d03601f1901891315614edd5760008081fd5b614ee56144a8565b8882013581528982013588811115614efd5760008081fd5b614f0b8f8b83860101614db6565b828b015250845250918601918601614eaf565b50809750505050505050509250929050565b60008083601f840112614f4257600080fd5b50813567ffffffffffffffff811115614f5a57600080fd5b6020830191508360208260051b8501011115612db257600080fd5b60008060008060008060008060e0898b031215614f9157600080fd5b606089018a811115614fa257600080fd5b8998503567ffffffffffffffff80821115614fbc57600080fd5b818b0191508b601f830112614fd057600080fd5b813581811115614fdf57600080fd5b8c6020828501011115614ff157600080fd5b6020830199508098505060808b013591508082111561500f57600080fd5b61501b8c838d01614f30565b909750955060a08b013591508082111561503457600080fd5b506150418b828c01614f30565b999c989b50969995989497949560c00135949350505050565b6000806000806060858703121561507057600080fd5b843567ffffffffffffffff8082111561508857600080fd5b908601906101a0828903121561509d57600080fd5b909450602086013590808211156150b357600080fd5b6150bf88838901614f30565b909550935060408701359150808211156150d857600080fd5b506150e587828801614db6565b91505092959194509250565b80356fffffffffffffffffffffffffffffffff811681146143a357600080fd5b60006060828403121561512357600080fd5b6040516060810181811067ffffffffffffffff8211171561514657615146614479565b604052823561515481614957565b8152615162602084016150f1565b6020820152615173604084016150f1565b60408201529392505050565b604081526000615192604083018561475c565b8281036020840152612eb3818561475c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106a4576106a46151a4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600067ffffffffffffffff80841680615230576152306151e6565b92169190910692915050565b80820281158282048414176106a4576106a46151a4565b600067ffffffffffffffff8084168061526e5761526e6151e6565b92169190910492915050565b602081016003831061528e5761528e6143c5565b91905290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600063ffffffff8083168181036152dc576152dc6151a4565b6001019392505050565b600061012063ffffffff808d1684528b6020850152808b166040850152508060608401526153168184018a61475c565b9050828103608084015261532a818961475c565b905060ff871660a084015282810360c0840152615347818761443a565b905067ffffffffffffffff851660e084015282810361010084015261536c818561443a565b9c9b505050505050505050505050565b60006020828403121561538e57600080fd5b8151610e5d81614382565b600082601f8301126153aa57600080fd5b81516153b86145ae82614616565b8181528460208386010111156153cd57600080fd5b611e33826020830160208701614416565b6000602082840312156153f057600080fd5b815167ffffffffffffffff8082111561540857600080fd5b908301906080828603121561541c57600080fd5b6154246144d1565b82518281111561543357600080fd5b61543f87828601615399565b82525060208301518281111561545457600080fd5b61546087828601615399565b60208301525060408301518281111561547857600080fd5b61548487828601615399565b6040830152506060830151925061549a83614da4565b6060810192909252509392505050565b808201808211156106a4576106a46151a4565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126154f257600080fd5b83018035915067ffffffffffffffff82111561550d57600080fd5b6020019150600681901b3603821315612db257600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261555a57600080fd5b83018035915067ffffffffffffffff82111561557557600080fd5b6020019150600581901b3603821315612db257600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126155c257600080fd5b83018035915067ffffffffffffffff8211156155dd57600080fd5b602001915036819003821315612db257600080fd5b60008151808452602080850194506020840160005b8381101561479657815180516001600160a01b031688528301518388015260409096019590820190600101615607565b608081528451608082015267ffffffffffffffff60208601511660a08201526000604086015160a060c084015261567261012084018261443a565b905060608701517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80808584030160e08601526156ae838361443a565b9250608089015191508085840301610100860152506156cd82826155f2565b925050506156e1602083018661ffff169052565b836040830152612eb360608301846001600160a01b03169052565b60008060006060848603121561571157600080fd5b835161571c81614957565b602085015190935067ffffffffffffffff81111561573957600080fd5b61574586828701615399565b925050604084015190509250925092565b600060a0828403121561576857600080fd5b60405160a0810181811067ffffffffffffffff8211171561578b5761578b614479565b604052825161579981614da4565b815260208301516157a981614da4565b6020820152604083015161ffff811681146157c357600080fd5b604082015260608301516157d68161456d565b606082015260808301516157e98161456d565b60808201529392505050565b610180810161586682856001600160a01b03808251168352602082015167ffffffffffffffff808216602086015280604085015116604086015250508060608301511660608401528060808301511660808401528060a08301511660a08401528060c08301511660c0840152505050565b825163ffffffff90811660e0840152602084015116610100830152604083015161ffff1661012083015260608301516001600160a01b03908116610140840152608084015116610160830152610e5d565b60006101208b83526001600160a01b038b16602084015267ffffffffffffffff808b1660408501528160608501526158f18285018b61475c565b91508382036080850152615905828a61475c565b915060ff881660a085015283820360c0850152615922828861443a565b90861660e0850152838103610100850152905061536c818561443a565b60006020828403121561595157600080fd5b8151610e5d81614957565b60008151808452602080850194506020840160005b8381101561479657815187529582019590820190600101615971565b6060815260006159a0606083018661595c565b82810360208401526159b2818661595c565b915050826040830152949350505050565b6000602082840312156159d557600080fd5b5051919050565b67ffffffffffffffff8181168382160190808211156141e8576141e86151a4565b67ffffffffffffffff8316815260408101610e5d60208301846143f4565b600067ffffffffffffffff8083168181036152dc576152dc6151a4565b615a4281846143f4565b604060208201526000611e33604083018461443a565b600060208284031215615a6a57600080fd5b813567ffffffffffffffff811115615a8157600080fd5b611e3384828501614b72565b600060408284031215615a9f57600080fd5b610e5d8383614970565b600060208284031215615abb57600080fd5b813567ffffffffffffffff80821115615ad357600080fd5b9083019060808286031215615ae757600080fd5b615aef6144d1565b823582811115615afe57600080fd5b615b0a8782860161463e565b825250602083013582811115615b1f57600080fd5b615b2b8782860161463e565b602083015250604083013582811115615b4357600080fd5b615b4f8782860161463e565b6040830152506060830135925061549a83614da4565b602081526000610e5d60208301846155f2565b60008282518085526020808601955060208260051b8401016020860160005b84811015615bc557601f19868403018952615bb383835161443a565b98840198925090830190600101615b97565b5090979650505050505050565b602081526000610e5d6020830184615b78565b60008151808452602080850194506020840160005b8381101561479657815163ffffffff1687529582019590820190600101615bfa565b60608152615c3760608201855167ffffffffffffffff169052565b60006020850151615c5360808401826001600160a01b03169052565b5060408501516001600160a01b03811660a084015250606085015167ffffffffffffffff811660c084015250608085015160e083015260a0850151610100615c9e8185018315159052565b60c08701519150610120615cbd8186018467ffffffffffffffff169052565b60e08801519250610140615cdb818701856001600160a01b03169052565b828901519350610160925083838701528189015193506101a091506101808281880152615d0c61020088018661443a565b9450818a015191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0808887030184890152615d4886846155f2565b948b01518886039091016101c0890152939450615d658585615b78565b9450808a01516101e0880152505050508281036020840152615d878186615b78565b90508281036040840152615d9b8185615be5565b9695505050505050565b600060208284031215615db757600080fd5b8151610e5d8161456d565b6020815260008251610100806020850152615de161012085018361443a565b91506020850151615dfe604086018267ffffffffffffffff169052565b5060408501516001600160a01b038116606086015250606085015160808501526080850151615e3860a08601826001600160a01b03169052565b5060a0850151601f19808685030160c0870152615e55848361443a565b935060c08701519150808685030160e0870152615e72848361443a565b935060e0870151915080868503018387015250615d9b838261443a565b600060408284031215615ea157600080fd5b615ea96144a8565b82517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81168114615ed557600080fd5b81526020830151615ee581614da4565b60208201529392505050565b600082615f0057615f006151e6565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003cf8eeaa60538317bc5f3da77dc004f9c59d73a00000000000000000000000000000000000000000000000002220395d5f2affe100000000000000000000000000000000000000000000000045849994fc9c7b15000000000000000000000000c5dbe2055fa233ece44c99432526f3fc46ca3fc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a1000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d54000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : staticConfig (tuple):
Arg [1] : commitStore (address): 0x3cf8EEAa60538317BC5F3Da77dC004f9c59d73a0
Arg [2] : chainSelector (uint64): 2459028469735686113
Arg [3] : sourceChainSelector (uint64): 5009297550715157269
Arg [4] : onRamp (address): 0xc5Dbe2055Fa233ece44c99432526F3Fc46cA3FC2
Arg [5] : prevOffRamp (address): 0x0000000000000000000000000000000000000000
Arg [6] : rmnProxy (address): 0x32270E4FA459cA47ae0334488e27ffb9bC9aB4a1
Arg [7] : tokenAdminRegistry (address): 0x048B911A1AE5dD4f0aEE5241A30d3DEDa3501D54
Arg [1] : rateLimiterConfig (tuple):
Arg [1] : isEnabled (bool): False
Arg [2] : capacity (uint128): 0
Arg [3] : rate (uint128): 0
-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 0000000000000000000000003cf8eeaa60538317bc5f3da77dc004f9c59d73a0
Arg [1] : 0000000000000000000000000000000000000000000000002220395d5f2affe1
Arg [2] : 00000000000000000000000000000000000000000000000045849994fc9c7b15
Arg [3] : 000000000000000000000000c5dbe2055fa233ece44c99432526f3fc46ca3fc2
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 00000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a1
Arg [6] : 000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d54
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.