// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)pragmasolidity ^0.8.1;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0// for contracts in construction, since the code is only stored at the end// of the constructor execution.return account.code.length>0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
if (success) {
if (returndata.length==0) {
// only check isContract if the call was successful and the return data is empty// otherwise we already know that it was a contractrequire(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function_revert(bytesmemory returndata, stringmemory errorMessage) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Contract Source Code
File 2 of 24: AggregateRateLimiter.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity 0.8.19;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";
contractAggregateRateLimiterisOwnerIsCreator{
usingRateLimiterforRateLimiter.TokenBucket;
usingUSDPriceWith18Decimalsforuint192;
errorPriceNotFoundForToken(address token);
eventAdminSet(address newAdmin);
// The address of the token limit admin that has the same permissions as the owner.addressinternal s_admin;
// The token bucket object that contains the bucket state.
RateLimiter.TokenBucket private s_rateLimiter;
/// @param config The RateLimiter.Config containing the capacity and refill rate/// of the bucket, plus the admin address.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. First, calculate the pricesfunction_rateLimitValue(Client.EVMTokenAmount[] memory tokenAmounts, IPriceRegistry priceRegistry) internal{
uint256 numberOfTokens = tokenAmounts.length;
uint256 value =0;
for (uint256 i =0; i < numberOfTokens; ++i) {
// not fetching validated price, as price staleness is not important for value-based rate limiting// we only need to verify price is not 0uint192 pricePerToken = priceRegistry.getTokenPrice(tokenAmounts[i].token).value;
if (pricePerToken ==0) revert PriceNotFoundForToken(tokenAmounts[i].token);
value += pricePerToken._calcUSDValueFromTokenAmount(tokenAmounts[i].amount);
}
s_rateLimiter._consume(value, address(0));
}
/// @notice Gets the token bucket with its values for the block it was requested at./// @return The token bucket.functioncurrentRateLimiterState() externalviewreturns (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.functionsetRateLimiterConfig(RateLimiter.Config memory config) externalonlyAdminOrOwner{
s_rateLimiter._setTokenBucketConfig(config);
}
// ================================================================// | Access |// ================================================================/// @notice Gets the token limit admin address./// @return the token limit admin address.functiongetTokenLimitAdmin() externalviewreturns (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.functionsetAdmin(address newAdmin) externalonlyAdminOrOwner{
s_admin = newAdmin;
emit AdminSet(newAdmin);
}
/// @notice a modifier that allows the owner or the s_tokenLimitAdmin call the functions/// it is applied to.modifieronlyAdminOrOwner() {
if (msg.sender!= owner() &&msg.sender!= s_admin) revert RateLimiter.OnlyCallableByAdminOrOwner();
_;
}
}
Contract Source Code
File 3 of 24: Client.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;// End consumer library.libraryClient{
structEVMTokenAmount {
address token; // token address on the local chain.uint256 amount; // Amount of tokens.
}
structAny2EVMMessage {
bytes32 messageId; // MessageId corresponding to ccipSend on source.uint64 sourceChainSelector; // Source chain selector.bytes sender; // abi.decode(sender) if coming from an EVM chain.bytes data; // payload sent in original message.
EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.
}
// If extraArgs is empty bytes, the default is 200k gas limit and strict = false.structEVM2AnyMessage {
bytes receiver; // abi.encode(receiver address) for dest EVM chainsbytes data; // Data payload
EVMTokenAmount[] tokenAmounts; // Token transfersaddress feeToken; // Address of feeToken. address(0) means you will send msg.value.bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)
}
// extraArgs will evolve to support new features// bytes4(keccak256("CCIP EVMExtraArgsV1"));bytes4publicconstant EVM_EXTRA_ARGS_V1_TAG =0x97a657c9;
structEVMExtraArgsV1 {
uint256 gasLimit; // ATTENTION!!! MAX GAS LIMIT 4M FOR BETA TESTINGbool strict; // See strict sequencing details below.
}
function_argsToBytes(EVMExtraArgsV1 memory extraArgs) internalpurereturns (bytesmemory bts) {
returnabi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);
}
}
Contract Source Code
File 4 of 24: ConfirmedOwner.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"./ConfirmedOwnerWithProposal.sol";
/**
* @title The ConfirmedOwner contract
* @notice A contract with helpers for basic contract ownership.
*/contractConfirmedOwnerisConfirmedOwnerWithProposal{
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}
Contract Source Code
File 5 of 24: ConfirmedOwnerWithProposal.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"./interfaces/OwnableInterface.sol";
/**
* @title The ConfirmedOwner contract
* @notice A contract with helpers for basic contract ownership.
*/contractConfirmedOwnerWithProposalisOwnableInterface{
addressprivate s_owner;
addressprivate s_pendingOwner;
eventOwnershipTransferRequested(addressindexedfrom, addressindexed to);
eventOwnershipTransferred(addressindexedfrom, addressindexed to);
constructor(address newOwner, address pendingOwner) {
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,
* pending.
*/functiontransferOwnership(address to) publicoverrideonlyOwner{
_transferOwnership(to);
}
/**
* @notice Allows an ownership transfer to be completed by the recipient.
*/functionacceptOwnership() externaloverride{
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
*/functionowner() publicviewoverridereturns (address) {
return s_owner;
}
/**
* @notice validate, transfer ownership, and emit relevant events
*/function_transferOwnership(address to) private{
require(to !=msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/**
* @notice validate access
*/function_validateOwnership() internalview{
require(msg.sender== s_owner, "Only callable by owner");
}
/**
* @notice Reverts if called by anyone other than the contract owner.
*/modifieronlyOwner() {
_validateOwnership();
_;
}
}
Contract Source Code
File 6 of 24: EVM2EVMOnRamp.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity 0.8.19;import {TypeAndVersionInterface} from"../../interfaces/TypeAndVersionInterface.sol";
import {IPool} from"../interfaces/pools/IPool.sol";
import {IARM} from"../interfaces/IARM.sol";
import {IPriceRegistry} from"../interfaces/IPriceRegistry.sol";
import {IEVM2AnyOnRamp} from"../interfaces/IEVM2AnyOnRamp.sol";
import {ILinkAvailable} from"../interfaces/automation/ILinkAvailable.sol";
import {AggregateRateLimiter} from"../AggregateRateLimiter.sol";
import {Client} from"../libraries/Client.sol";
import {Internal} from"../libraries/Internal.sol";
import {RateLimiter} from"../libraries/RateLimiter.sol";
import {USDPriceWith18Decimals} from"../libraries/USDPriceWith18Decimals.sol";
import {EnumerableMapAddresses} from"../../shared/enumerable/EnumerableMapAddresses.sol";
import {SafeERC20} from"../../vendor/openzeppelin-solidity/v4.8.0/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from"../../vendor/openzeppelin-solidity/v4.8.0/token/ERC20/IERC20.sol";
import {EnumerableSet} from"../../vendor/openzeppelin-solidity/v4.8.0/utils/structs/EnumerableSet.sol";
import {EnumerableMap} from"../../vendor/openzeppelin-solidity/v4.8.0/utils/structs/EnumerableMap.sol";
/// @notice The onRamp is a contract that handles fee logic, NOP payments,/// token support and an allowList. It will always be deployed 1:1:1 with a/// commitStore and offRamp contract. These three contracts together form a/// `lane`. A lane is an upgradable set of contracts within the non-upgradable/// routers and are always deployed as complete set, even during upgrades./// This means an upgrade to an onRamp will require redeployment of the/// commitStore and offRamp as well.contractEVM2EVMOnRampisIEVM2AnyOnRamp, ILinkAvailable, AggregateRateLimiter, TypeAndVersionInterface{
usingSafeERC20forIERC20;
usingEnumerableMapforEnumerableMap.AddressToUintMap;
usingEnumerableMapAddressesforEnumerableMapAddresses.AddressToAddressMap;
usingEnumerableSetforEnumerableSet.AddressSet;
usingUSDPriceWith18Decimalsforuint192;
errorInvalidExtraArgsTag();
errorOnlyCallableByOwnerOrAdmin();
errorOnlyCallableByOwnerOrAdminOrNop();
errorInvalidWithdrawParams();
errorNoFeesToPay();
errorNoNopsToPay();
errorInsufficientBalance();
errorTooManyNops();
errorMaxFeeBalanceReached();
errorMessageTooLarge(uint256 maxSize, uint256 actualSize);
errorMessageGasLimitTooHigh();
errorUnsupportedNumberOfTokens();
errorUnsupportedToken(IERC20 token);
errorMustBeCalledByRouter();
errorRouterMustSetOriginalSender();
errorInvalidTokenPoolConfig();
errorPoolAlreadyAdded();
errorPoolDoesNotExist(address token);
errorTokenPoolMismatch();
errorSenderNotAllowed(address sender);
errorInvalidConfig();
errorInvalidAddress(bytes encodedAddress);
errorBadARMSignal();
errorLinkBalanceNotSettled();
errorInvalidNopAddress(address nop);
errorNotAFeeToken(address token);
eventAllowListAdd(address sender);
eventAllowListRemove(address sender);
eventAllowListEnabledSet(bool enabled);
eventConfigSet(StaticConfig staticConfig, DynamicConfig dynamicConfig);
eventNopPaid(addressindexed nop, uint256 amount);
eventFeeConfigSet(FeeTokenConfigArgs[] feeConfig);
eventTokenTransferFeeConfigSet(TokenTransferFeeConfigArgs[] transferFeeConfig);
eventCCIPSendRequested(Internal.EVM2EVMMessage message);
eventNopsSet(uint256 nopWeightsTotal, NopAndWeight[] nopsAndWeights);
eventPoolAdded(address token, address pool);
eventPoolRemoved(address token, address pool);
/// @dev Struct that contains the static configurationstructStaticConfig {
address linkToken; // --------┐ Link token addressuint64 chainSelector; // -----┘ Source chainSelectoruint64 destChainSelector; // -┐ Destination chainSelectoruint64 defaultTxGasLimit; // | Default gas limit for a txuint96 maxNopFeesJuels; // ---┘ Max nop fee balance onramp can haveaddress prevOnRamp; // ------- Address of previous-version OnRampaddress armProxy; // ---------- Address of ARM proxy
}
/// @dev Struct to contains the dynamic configurationstructDynamicConfig {
address router; // -------- ┐ Router addressuint16 maxTokensLength; // ┘ Maximum number of distinct ERC20 tokens that can be sent per messageaddress priceRegistry; // --┐ Price registry addressuint32 maxDataSize; // | Maximum payload data sizeuint64 maxGasLimit; // -----┘ Maximum gas limit for messages targeting EVMs
}
/// @dev Struct to hold the execution fee configuration for a fee tokenstructFeeTokenConfig {
uint96 networkFeeAmountUSD; // --┐ Flat network fee in 1e18 USDuint64 gasMultiplier; // | Price multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost.uint32 destGasOverhead; // | Extra gas charged on top of the gasLimituint16 destGasPerPayloadByte; // | Destination chain gas charged per byte of `data` payloadbool enabled; // ----------------┘ Whether this fee token is enabled
}
/// @dev Struct to hold the fee configuration for a fee token, same as the FeeTokenConfig but with/// token included so that an array of these can be passed in to setFeeTokenConfig to set the mappingstructFeeTokenConfigArgs {
address token; // ---------------┐ Token addressuint64 gasMultiplier; // --------┘ Price multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost.uint96 networkFeeAmountUSD; // --┐ Flat network fee in 1e18 USDuint32 destGasOverhead; // | Extra gas charged on top of the gasLimituint16 destGasPerPayloadByte; // | Destination chain gas charged per byte of `data` payloadbool enabled; // ----------------┘ Whether this fee token is enabled
}
/// @dev Struct to hold the transfer fee configuration for token transfersstructTokenTransferFeeConfig {
uint32 minFee; // ---┐ Minimum USD fee to charge, multiples of 1 US cent, or 0.01USDuint32 maxFee; // | Maximum USD fee to charge, multiples of 1 US cent, or 0.01USDuint16 ratio; // ----┘ Ratio of token transfer value to charge as fee, multiples of 0.1bps, or 10e-5
}
/// @dev Same as TokenTransferFeeConfig/// token included so that an array of these can be passed in to setTokenTransferFeeConfigstructTokenTransferFeeConfigArgs {
address token; // ---┐ Token addressuint32 minFee; // | Minimum USD fee to charge, multiples of 1 US cent, or 0.01USDuint32 maxFee; // | Maximum USD fee to charge, multiples of 1 US cent, or 0.01USDuint16 ratio; // ----┘ Ratio of token transfer value to charge as fee, multiples of 0.1bps, or 10e-5
}
/// @dev Nop address and weight, used to set the nops and their weightsstructNopAndWeight {
address nop; // -----┐ Address of the node operatoruint16 weight; // ---┘ Weight for nop rewards
}
// STATIC CONFIG// solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variablesstringpublicconstantoverride typeAndVersion ="EVM2EVMOnRamp 1.0.0";
/// @dev The metadata hash for this contractbytes32internalimmutable i_metadataHash;
/// @dev Default gas limit for a transactions that did not specify/// a gas limit in the extraArgs.uint64internalimmutable i_defaultTxGasLimit;
/// @dev Maximum nop fee that can accumulate in this onrampuint96internalimmutable i_maxNopFeesJuels;
/// @dev The link token address - known to pay nops for their workaddressinternalimmutable i_linkToken;
/// @dev The chain ID of the source chain that this contract is deployed touint64internalimmutable i_chainSelector;
/// @dev The chain ID of the destination chainuint64internalimmutable i_destChainSelector;
/// @dev The address of previous-version OnRamp for this laneaddressinternalimmutable i_prevOnRamp;
/// @dev The address of the arm proxyaddressinternalimmutable i_armProxy;
/// @dev the maximum number of nops that can be configured at the same time.uint256privateconstant MAX_NUMBER_OF_NOPS =64;
// DYNAMIC CONFIG/// @dev The config for the onRamp
DynamicConfig internal s_dynamicConfig;
/// @dev (address nop => uint256 weight)
EnumerableMap.AddressToUintMap internal s_nops;
/// @dev source token => token pool
EnumerableMapAddresses.AddressToAddressMap private s_poolsBySourceToken;
/// @dev A set of addresses which can make ccipSend calls.
EnumerableSet.AddressSet private s_allowList;
/// @dev The execution fee token config that can be set by the owner or fee adminmapping(address token => FeeTokenConfig feeTokenConfig) internal s_feeTokenConfig;
/// @dev The token transfer fee config that can be set by the owner or fee adminmapping(address token => TokenTransferFeeConfig tranferFeeConfig) internal s_tokenTransferFeeConfig;
// STATE/// @dev The current nonce per sendermapping(address sender =>uint64 nonce) internal s_senderNonce;
/// @dev The amount of LINK available to pay NOPSuint96internal s_nopFeesJuels;
/// @dev The combined weight of all NOPs weightsuint32internal s_nopWeightsTotal;
/// @dev The last used sequence number. This is zero in the case where no/// messages has been sent yet. 0 is not a valid sequence number for any/// real transaction.uint64internal s_sequenceNumber;
/// @dev Whether this OnRamp is paused or notboolprivate s_paused =false;
/// @dev This allowListing will be removed before public launch/// @dev Whether s_allowList is enabled or not.boolprivate s_allowlistEnabled;
constructor(
StaticConfig memory staticConfig,
DynamicConfig memory dynamicConfig,
Internal.PoolUpdate[] memory tokensAndPools,
address[] memory allowlist,
RateLimiter.Config memory rateLimiterConfig,
FeeTokenConfigArgs[] memory feeTokenConfigs,
TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs,
NopAndWeight[] memory nopsAndWeights
) AggregateRateLimiter(rateLimiterConfig) {
if (
staticConfig.linkToken ==address(0) ||
staticConfig.chainSelector ==0||
staticConfig.destChainSelector ==0||
staticConfig.defaultTxGasLimit ==0||
staticConfig.armProxy ==address(0)
) revert InvalidConfig();
i_metadataHash =keccak256(
abi.encode(
Internal.EVM_2_EVM_MESSAGE_HASH,
staticConfig.chainSelector,
staticConfig.destChainSelector,
address(this)
)
);
i_linkToken = staticConfig.linkToken;
i_chainSelector = staticConfig.chainSelector;
i_destChainSelector = staticConfig.destChainSelector;
i_defaultTxGasLimit = staticConfig.defaultTxGasLimit;
i_maxNopFeesJuels = staticConfig.maxNopFeesJuels;
i_prevOnRamp = staticConfig.prevOnRamp;
i_armProxy = staticConfig.armProxy;
_setDynamicConfig(dynamicConfig);
_setFeeTokenConfig(feeTokenConfigs);
_setTokenTransferFeeConfig(tokenTransferFeeConfigArgs);
_setNops(nopsAndWeights);
// Set new tokens and pools
_applyPoolUpdates(new Internal.PoolUpdate[](0), tokensAndPools);
if (allowlist.length>0) {
s_allowlistEnabled =true;
_applyAllowListUpdates(newaddress[](0), allowlist);
}
}
// ================================================================// | Messaging |// ================================================================/// @inheritdoc IEVM2AnyOnRampfunctiongetExpectedNextSequenceNumber() externalviewreturns (uint64) {
return s_sequenceNumber +1;
}
/// @inheritdoc IEVM2AnyOnRampfunctiongetSenderNonce(address sender) externalviewreturns (uint64) {
uint256 senderNonce = s_senderNonce[sender];
if (senderNonce ==0&& i_prevOnRamp !=address(0)) {
// If OnRamp was upgraded, check if sender has a nonce from the previous OnRamp.return IEVM2AnyOnRamp(i_prevOnRamp).getSenderNonce(sender);
}
returnuint64(senderNonce);
}
/// @inheritdoc IEVM2AnyOnRampfunctionforwardFromRouter(
Client.EVM2AnyMessage calldata message,
uint256 feeTokenAmount,
address originalSender
) externalwhenHealthyreturns (bytes32) {
// EVM destination addresses should be abi encoded and therefore always 32 bytes longif (message.receiver.length!=32) revert InvalidAddress(message.receiver);
uint256 decodedReceiver =abi.decode(message.receiver, (uint256));
// We want to disallow sending to address(0) and to precompiles, which exist on address(1) through address(9).if (decodedReceiver >type(uint160).max|| decodedReceiver <10) revert InvalidAddress(message.receiver);
Client.EVMExtraArgsV1 memory extraArgs = _fromBytes(message.extraArgs);
// Validate the message with various checks
_validateMessage(message.data.length, extraArgs.gasLimit, message.tokenAmounts.length, originalSender);
// Rate limit on aggregated token value
_rateLimitValue(message.tokenAmounts, IPriceRegistry(s_dynamicConfig.priceRegistry));
// Convert feeToken to link if not already in linkif (message.feeToken == i_linkToken) {
// Since there is only 1b link this is safe
s_nopFeesJuels +=uint96(feeTokenAmount);
} else {
// the cast from uint256 to uint96 is considered safe, uint96 can store more than max supply of link token
s_nopFeesJuels +=uint96(
IPriceRegistry(s_dynamicConfig.priceRegistry).convertTokenAmount(message.feeToken, feeTokenAmount, i_linkToken)
);
}
if (s_nopFeesJuels > i_maxNopFeesJuels) revert MaxFeeBalanceReached();
if (s_senderNonce[originalSender] ==0&& i_prevOnRamp !=address(0)) {
// If this is first time send for a sender in new OnRamp, check if they have a nonce// from the previous OnRamp and start from there instead of zero.
s_senderNonce[originalSender] = IEVM2AnyOnRamp(i_prevOnRamp).getSenderNonce(originalSender);
}
// We need the next available sequence number so we increment before we use the value
Internal.EVM2EVMMessage memory newMessage = Internal.EVM2EVMMessage({
sourceChainSelector: i_chainSelector,
sequenceNumber: ++s_sequenceNumber,
feeTokenAmount: feeTokenAmount,
sender: originalSender,
nonce: ++s_senderNonce[originalSender],
gasLimit: extraArgs.gasLimit,
strict: extraArgs.strict,
receiver: address(uint160(decodedReceiver)),
data: message.data,
tokenAmounts: message.tokenAmounts,
feeToken: message.feeToken,
messageId: ""
});
newMessage.messageId = Internal._hash(newMessage, i_metadataHash);
// Lock the tokens as last step. TokenPools may not always be trusted.// There should be no state changes after external call to TokenPools.for (uint256 i =0; i < message.tokenAmounts.length; ++i) {
Client.EVMTokenAmount memory tokenAndAmount = message.tokenAmounts[i];
getPoolBySourceToken(IERC20(tokenAndAmount.token)).lockOrBurn(
originalSender,
message.receiver,
tokenAndAmount.amount,
i_destChainSelector,
bytes("") // any future extraArgs component would be added here
);
}
// Emit message requestemit CCIPSendRequested(newMessage);
return newMessage.messageId;
}
/// @dev Convert the extra args bytes into a struct/// @param extraArgs The extra args bytes/// @return The extra args structfunction_fromBytes(bytescalldata extraArgs) internalviewreturns (Client.EVMExtraArgsV1 memory) {
if (extraArgs.length==0) {
return Client.EVMExtraArgsV1({gasLimit: i_defaultTxGasLimit, strict: false});
}
if (bytes4(extraArgs) != Client.EVM_EXTRA_ARGS_V1_TAG) revert InvalidExtraArgsTag();
returnabi.decode(extraArgs[4:], (Client.EVMExtraArgsV1));
}
/// @notice Validate the forwarded message with various checks./// @param dataLength The length of the data field of the message/// @param gasLimit The gasLimit set in message for destination execution/// @param numberOfTokens The number of tokens to be sent./// @param originalSender The original sender of the message on the router.function_validateMessage(uint256 dataLength,
uint256 gasLimit,
uint256 numberOfTokens,
address originalSender
) internalview{
if (originalSender ==address(0)) revert RouterMustSetOriginalSender();
// Router address may be zero intentionally to pause.if (msg.sender!= s_dynamicConfig.router) revert MustBeCalledByRouter();
// Check that payload is formed correctlyuint256 maxDataSize =uint256(s_dynamicConfig.maxDataSize);
if (dataLength > maxDataSize) revert MessageTooLarge(maxDataSize, dataLength);
if (gasLimit >uint256(s_dynamicConfig.maxGasLimit)) revert MessageGasLimitTooHigh();
if (numberOfTokens >uint256(s_dynamicConfig.maxTokensLength)) revert UnsupportedNumberOfTokens();
if (s_allowlistEnabled &&!s_allowList.contains(originalSender)) revert SenderNotAllowed(originalSender);
}
// ================================================================// | Config |// ================================================================/// @notice Returns the static onRamp config./// @return the configuration.functiongetStaticConfig() externalviewreturns (StaticConfig memory) {
return
StaticConfig({
linkToken: i_linkToken,
chainSelector: i_chainSelector,
destChainSelector: i_destChainSelector,
defaultTxGasLimit: i_defaultTxGasLimit,
maxNopFeesJuels: i_maxNopFeesJuels,
prevOnRamp: i_prevOnRamp,
armProxy: i_armProxy
});
}
/// @notice Returns the dynamic onRamp config./// @return dynamicConfig the configuration.functiongetDynamicConfig() externalviewreturns (DynamicConfig memory dynamicConfig) {
return s_dynamicConfig;
}
/// @notice Sets the dynamic configuration./// @param dynamicConfig The configuration.functionsetDynamicConfig(DynamicConfig memory dynamicConfig) externalonlyOwner{
_setDynamicConfig(dynamicConfig);
}
/// @notice Internal version of setDynamicConfig to allow for reuse in the constructor.function_setDynamicConfig(DynamicConfig memory dynamicConfig) internal{
// We permit router to be set to zero as a way to pause the contract.if (dynamicConfig.priceRegistry ==address(0)) revert InvalidConfig();
s_dynamicConfig = dynamicConfig;
emit ConfigSet(
StaticConfig({
linkToken: i_linkToken,
chainSelector: i_chainSelector,
destChainSelector: i_destChainSelector,
defaultTxGasLimit: i_defaultTxGasLimit,
maxNopFeesJuels: i_maxNopFeesJuels,
prevOnRamp: i_prevOnRamp,
armProxy: i_armProxy
}),
dynamicConfig
);
}
// ================================================================// | Tokens and pools |// ================================================================/// @inheritdoc IEVM2AnyOnRampfunctiongetSupportedTokens() externalviewreturns (address[] memory) {
address[] memory sourceTokens =newaddress[](s_poolsBySourceToken.length());
for (uint256 i =0; i < sourceTokens.length; ++i) {
(sourceTokens[i], ) = s_poolsBySourceToken.at(i);
}
return sourceTokens;
}
/// @inheritdoc IEVM2AnyOnRampfunctiongetPoolBySourceToken(IERC20 sourceToken) publicviewreturns (IPool) {
if (!s_poolsBySourceToken.contains(address(sourceToken))) revert UnsupportedToken(sourceToken);
return IPool(s_poolsBySourceToken.get(address(sourceToken)));
}
/// @inheritdoc IEVM2AnyOnRamp/// @dev This method can only be called by the owner of the contract.functionapplyPoolUpdates(
Internal.PoolUpdate[] memory removes,
Internal.PoolUpdate[] memory adds
) externalonlyOwner{
_applyPoolUpdates(removes, adds);
}
function_applyPoolUpdates(Internal.PoolUpdate[] memory removes, Internal.PoolUpdate[] memory adds) internal{
for (uint256 i =0; i < removes.length; ++i) {
address token = removes[i].token;
address pool = removes[i].pool;
if (!s_poolsBySourceToken.contains(token)) revert PoolDoesNotExist(token);
if (s_poolsBySourceToken.get(token) != pool) revert TokenPoolMismatch();
if (s_poolsBySourceToken.remove(token)) {
emit PoolRemoved(token, pool);
}
}
for (uint256 i =0; i < adds.length; ++i) {
address token = adds[i].token;
address pool = adds[i].pool;
if (token ==address(0) || pool ==address(0)) revert InvalidTokenPoolConfig();
if (token !=address(IPool(pool).getToken())) revert TokenPoolMismatch();
if (s_poolsBySourceToken.set(token, pool)) {
emit PoolAdded(token, pool);
} else {
revert PoolAlreadyAdded();
}
}
}
// ================================================================// | Fees |// ================================================================/// @inheritdoc IEVM2AnyOnRampfunctiongetFee(Client.EVM2AnyMessage calldata message) externalviewreturns (uint256) {
FeeTokenConfig memory feeTokenConfig = s_feeTokenConfig[message.feeToken];
if (!feeTokenConfig.enabled) revert NotAFeeToken(message.feeToken);
(uint192 feeTokenPrice, uint192 gasPrice) = IPriceRegistry(s_dynamicConfig.priceRegistry).getTokenAndGasPrices(
message.feeToken,
i_destChainSelector
);
// Total tx fee in USD with 18 decimals precision, excluding token bps// We add the message gas limit, the overhead gas and the calldata gas together.// We then multiple this destination chain gas total with the gas multiplier and// convert it into USD.uint256 executionFeeUsdValue = (gasPrice *
((_fromBytes(message.extraArgs).gasLimit +
feeTokenConfig.destGasOverhead +
message.data.length*
feeTokenConfig.destGasPerPayloadByte) * feeTokenConfig.gasMultiplier)) /1ether+
feeTokenConfig.networkFeeAmountUSD;
// Transform the execution fee into fee token amount and add the token bps fee// which is already priced in fee tokenreturn
feeTokenPrice._calcTokenAmountFromUSDValue(executionFeeUsdValue) +
_getTokenTransferFee(message.feeToken, feeTokenPrice, message.tokenAmounts);
}
/// @notice Returns the fee based on the tokens transferred. Will always be 0 if/// no tokens are transferred. The token fee is calculated based on basis points.function_getTokenTransferFee(address feeToken,
uint192 feeTokenPrice,
Client.EVMTokenAmount[] calldata tokenAmounts
) internalviewreturns (uint256 feeTokenAmount) {
uint256 numberOfTokens = tokenAmounts.length;
// short-circuit with 0 transfer fee if no token is being transferredif (numberOfTokens ==0) {
return0;
}
for (uint256 i =0; i < numberOfTokens; ++i) {
Client.EVMTokenAmount memory tokenAmount = tokenAmounts[i];
TokenTransferFeeConfig memory transferFeeConfig = s_tokenTransferFeeConfig[tokenAmount.token];
uint256 feeValue =0;
// ratio can be 0, only calculate bps fee if ratio is greater than 0if (transferFeeConfig.ratio >0) {
uint192 tokenPrice = feeTokenPrice;
if (tokenAmount.token != feeToken) {
tokenPrice = IPriceRegistry(s_dynamicConfig.priceRegistry).getValidatedTokenPrice(tokenAmount.token);
}
// calculate token transfer value, then apply fee ratio// ratio represents multiples of 0.1bps, or 10e-5
feeValue = (tokenPrice._calcUSDValueFromTokenAmount(tokenAmount.amount) * transferFeeConfig.ratio) /1e5;
}
// convert USD values with 2 decimals to 18 decimalsuint256 minFeeValue =uint256(transferFeeConfig.minFee) *1e16;
uint256 maxFeeValue =uint256(transferFeeConfig.maxFee) *1e16;
if (feeValue < minFeeValue) {
feeValue = minFeeValue;
} elseif (feeValue > maxFeeValue) {
feeValue = maxFeeValue;
}
feeTokenAmount += feeTokenPrice._calcTokenAmountFromUSDValue(feeValue);
}
return feeTokenAmount;
}
/// @notice Gets the fee configuration for a token/// @param token The token to get the fee configuration for/// @return feeTokenConfig FeeTokenConfig structfunctiongetFeeTokenConfig(address token) externalviewreturns (FeeTokenConfig memory feeTokenConfig) {
return s_feeTokenConfig[token];
}
/// @notice Sets the fee configuration for a token/// @param feeTokenConfigArgs Array of FeeTokenConfigArgs structs.functionsetFeeTokenConfig(FeeTokenConfigArgs[] memory feeTokenConfigArgs) externalonlyOwnerOrAdmin{
_setFeeTokenConfig(feeTokenConfigArgs);
}
/// @dev Set the fee config/// @param feeTokenConfigArgs The fee token configs.function_setFeeTokenConfig(FeeTokenConfigArgs[] memory feeTokenConfigArgs) internal{
for (uint256 i =0; i < feeTokenConfigArgs.length; ++i) {
FeeTokenConfigArgs memory configArg = feeTokenConfigArgs[i];
s_feeTokenConfig[configArg.token] = FeeTokenConfig({
networkFeeAmountUSD: configArg.networkFeeAmountUSD,
gasMultiplier: configArg.gasMultiplier,
destGasOverhead: configArg.destGasOverhead,
destGasPerPayloadByte: configArg.destGasPerPayloadByte,
enabled: configArg.enabled
});
}
emit FeeConfigSet(feeTokenConfigArgs);
}
/// @notice Gets the transfer fee config for a given token.functiongetTokenTransferFeeConfig(address token
) externalviewreturns (TokenTransferFeeConfig memory tokenTransferFeeConfig) {
return s_tokenTransferFeeConfig[token];
}
/// @notice Sets the transfer fee config./// @dev only callable by the owner or admin.functionsetTokenTransferFeeConfig(
TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs
) externalonlyOwnerOrAdmin{
_setTokenTransferFeeConfig(tokenTransferFeeConfigArgs);
}
/// @notice internal helper to set the token transfer fee config.function_setTokenTransferFeeConfig(TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs) internal{
for (uint256 i =0; i < tokenTransferFeeConfigArgs.length; ++i) {
TokenTransferFeeConfigArgs memory configArg = tokenTransferFeeConfigArgs[i];
s_tokenTransferFeeConfig[configArg.token] = TokenTransferFeeConfig({
minFee: configArg.minFee,
maxFee: configArg.maxFee,
ratio: configArg.ratio
});
}
emit TokenTransferFeeConfigSet(tokenTransferFeeConfigArgs);
}
// ================================================================// | NOP payments |// ================================================================/// @notice Get the total amount of fees to be paid to the Nops (in LINK)/// @return totalNopFeesfunctiongetNopFeesJuels() externalviewreturns (uint96) {
return s_nopFeesJuels;
}
/// @notice Gets the Nops and their weights/// @return nopsAndWeights Array of NopAndWeight structs/// @return weightsTotal The sum weight of all NopsfunctiongetNops() externalviewreturns (NopAndWeight[] memory nopsAndWeights, uint256 weightsTotal) {
uint256 length = s_nops.length();
nopsAndWeights =new NopAndWeight[](length);
for (uint256 i =0; i < length; ++i) {
(address nopAddress, uint256 nopWeight) = s_nops.at(i);
nopsAndWeights[i] = NopAndWeight({nop: nopAddress, weight: uint16(nopWeight)});
}
weightsTotal = s_nopWeightsTotal;
return (nopsAndWeights, weightsTotal);
}
/// @notice Sets the Nops and their weights/// @param nopsAndWeights Array of NopAndWeight structsfunctionsetNops(NopAndWeight[] calldata nopsAndWeights) externalonlyOwnerOrAdmin{
_setNops(nopsAndWeights);
}
/// @dev Clears existing nops, sets new nops and weights/// @param nopsAndWeights New set of nops and weightsfunction_setNops(NopAndWeight[] memory nopsAndWeights) internal{
uint256 numberOfNops = nopsAndWeights.length;
if (numberOfNops > MAX_NUMBER_OF_NOPS) revert TooManyNops();
// Make sure all nops have been paid before removing nops// We only have to pay when there are nops and there is enough// outstanding NOP balance to trigger a payment.if (s_nopWeightsTotal >0&& s_nopFeesJuels >= s_nopWeightsTotal) {
payNops();
}
// Remove all previous nops, move from end to start to avoid shiftingfor (uint256 i = s_nops.length(); i >0; --i) {
(address nop, ) = s_nops.at(i -1);
s_nops.remove(nop);
}
// Add newuint32 nopWeightsTotal =0;
// nopWeightsTotal is bounded by the MAX_NUMBER_OF_NOPS and the weight of// a single nop being of type uint16. This ensures nopWeightsTotal will// always fit into the uint32 type.for (uint256 i =0; i < numberOfNops; ++i) {
// Make sure the LINK token is not a nop because the link token doesn't allow// self transfers. If set as nop, payNops would always revert. Since setNops// calls payNops, we can never remove the LINK token as a nop.address nop = nopsAndWeights[i].nop;
uint16 weight = nopsAndWeights[i].weight;
if (nop == i_linkToken || nop ==address(0)) revert InvalidNopAddress(nop);
s_nops.set(nop, weight);
nopWeightsTotal += weight;
}
s_nopWeightsTotal = nopWeightsTotal;
emit NopsSet(nopWeightsTotal, nopsAndWeights);
}
/// @notice Pays the Node Ops their outstanding balances./// @dev some balance can remain after payments are done. This is at most the sum/// of the weight of all nops. Since nop weights are uint16s and we can have at/// most MAX_NUMBER_OF_NOPS NOPs, the highest possible value is 2**22 or 0.04 gjuels.functionpayNops() publiconlyOwnerOrAdminOrNop{
uint256 weightsTotal = s_nopWeightsTotal;
if (weightsTotal ==0) revert NoNopsToPay();
uint96 totalFeesToPay = s_nopFeesJuels;
if (totalFeesToPay < weightsTotal) revert NoFeesToPay();
if (_linkLeftAfterNopFees() <0) revert InsufficientBalance();
uint96 fundsLeft = totalFeesToPay;
uint256 numberOfNops = s_nops.length();
for (uint256 i =0; i < numberOfNops; ++i) {
(address nop, uint256 weight) = s_nops.at(i);
// amount can never be higher than totalFeesToPay so the cast to uint96 is safeuint96 amount =uint96((totalFeesToPay * weight) / weightsTotal);
fundsLeft -= amount;
IERC20(i_linkToken).safeTransfer(nop, amount);
emit NopPaid(nop, amount);
}
// Some funds can remain, since this is an incredibly small// amount we consider this OK.
s_nopFeesJuels = fundsLeft;
}
/// @notice Allows the owner to withdraw any ERC20 token that is not the fee token/// @param feeToken The token to withdraw/// @param to The address to send the tokens tofunctionwithdrawNonLinkFees(address feeToken, address to) externalonlyOwnerOrAdmin{
if (feeToken == i_linkToken || to ==address(0)) revert InvalidWithdrawParams();
// We require the link balance to be settled before allowing withdrawal// of non-link fees.if (_linkLeftAfterNopFees() <0) revert LinkBalanceNotSettled();
IERC20(feeToken).safeTransfer(to, IERC20(feeToken).balanceOf(address(this)));
}
// ================================================================// | Link monitoring |// ================================================================/// @notice Calculate remaining LINK balance after paying nops/// @return balance if nops were to be paidfunction_linkLeftAfterNopFees() privateviewreturns (int256) {
// Since LINK caps at uint96, casting to int256 is safereturnint256(IERC20(i_linkToken).balanceOf(address(this))) -int256(uint256(s_nopFeesJuels));
}
/// @notice Allow keeper to monitor funds available for paying nopsfunctionlinkAvailableForPayment() externalviewreturns (int256) {
return _linkLeftAfterNopFees();
}
// ================================================================// | Allowlist |// ================================================================/// @notice Gets whether the allowList functionality is enabled./// @return true is enabled, false if not.functiongetAllowListEnabled() externalviewreturns (bool) {
return s_allowlistEnabled;
}
/// @notice Enables or disabled the allowList functionality./// @param enabled Signals whether the allowlist should be enabled.functionsetAllowListEnabled(bool enabled) externalonlyOwner{
s_allowlistEnabled = enabled;
emit AllowListEnabledSet(enabled);
}
/// @notice Gets the allowed addresses./// @return The allowed addresses./// @dev May not work if allow list gets too large. Use events in that case to compute the set.functiongetAllowList() externalviewreturns (address[] memory) {
return s_allowList.values();
}
/// @notice Apply updates to the allow list./// @param removes The addresses to be removed./// @param adds The addresses to be added./// @dev allowListing will be removed before public launchfunctionapplyAllowListUpdates(address[] memory removes, address[] memory adds) externalonlyOwner{
_applyAllowListUpdates(removes, adds);
}
/// @notice Internal version of applyAllowListUpdates to allow for reuse in the constructor./// @dev allowListing will be removed before public launchfunction_applyAllowListUpdates(address[] memory removes, address[] memory adds) internal{
for (uint256 i =0; i < removes.length; ++i) {
address toRemove = removes[i];
if (s_allowList.remove(toRemove)) {
emit AllowListRemove(toRemove);
}
}
for (uint256 i =0; i < adds.length; ++i) {
address toAdd = adds[i];
if (toAdd ==address(0)) {
continue;
}
if (s_allowList.add(toAdd)) {
emit AllowListAdd(toAdd);
}
}
}
// ================================================================// | Access and ARM |// ================================================================/// @dev Require that the sender is the owner or the fee admin or a nopmodifieronlyOwnerOrAdminOrNop() {
if (msg.sender!= owner() &&msg.sender!= s_admin &&!s_nops.contains(msg.sender))
revert OnlyCallableByOwnerOrAdminOrNop();
_;
}
/// @dev Require that the sender is the owner or the fee adminmodifieronlyOwnerOrAdmin() {
if (msg.sender!= owner() &&msg.sender!= s_admin) revert OnlyCallableByOwnerOrAdmin();
_;
}
/// @notice Ensure that the ARM has not emitted a bad signal, and that the latest heartbeat is not stale.modifierwhenHealthy() {
if (IARM(i_armProxy).isCursed()) revert BadARMSignal();
_;
}
}
Contract Source Code
File 7 of 24: EnumerableMap.sol
// 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.pragmasolidity ^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.
* ====
*/libraryEnumerableMap{
usingEnumerableSetforEnumerableSet.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.structBytes32ToBytes32Map {
// 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.
*/functionset(
Bytes32ToBytes32Map storage map,
bytes32 key,
bytes32 value
) internalreturns (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.
*/functionremove(Bytes32ToBytes32Map storage map, bytes32 key) internalreturns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(Bytes32ToBytes32Map storage map, bytes32 key) internalviewreturns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/functionlength(Bytes32ToBytes32Map storage map) internalviewreturns (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}.
*/functionat(Bytes32ToBytes32Map storage map, uint256 index) internalviewreturns (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.
*/functiontryGet(Bytes32ToBytes32Map storage map, bytes32 key) internalviewreturns (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.
*/functionget(Bytes32ToBytes32Map storage map, bytes32 key) internalviewreturns (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}.
*/functionget(
Bytes32ToBytes32Map storage map,
bytes32 key,
stringmemory errorMessage
) internalviewreturns (bytes32) {
bytes32 value = map._values[key];
require(value !=0|| contains(map, key), errorMessage);
return value;
}
// UintToUintMapstructUintToUintMap {
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.
*/functionset(
UintToUintMap storage map,
uint256 key,
uint256 value
) internalreturns (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.
*/functionremove(UintToUintMap storage map, uint256 key) internalreturns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(UintToUintMap storage map, uint256 key) internalviewreturns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/functionlength(UintToUintMap storage map) internalviewreturns (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}.
*/functionat(UintToUintMap storage map, uint256 index) internalviewreturns (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.
*/functiontryGet(UintToUintMap storage map, uint256 key) internalviewreturns (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.
*/functionget(UintToUintMap storage map, uint256 key) internalviewreturns (uint256) {
returnuint256(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}.
*/functionget(
UintToUintMap storage map,
uint256 key,
stringmemory errorMessage
) internalviewreturns (uint256) {
returnuint256(get(map._inner, bytes32(key), errorMessage));
}
// UintToAddressMapstructUintToAddressMap {
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.
*/functionset(
UintToAddressMap storage map,
uint256 key,
address value
) internalreturns (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.
*/functionremove(UintToAddressMap storage map, uint256 key) internalreturns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(UintToAddressMap storage map, uint256 key) internalviewreturns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/functionlength(UintToAddressMap storage map) internalviewreturns (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}.
*/functionat(UintToAddressMap storage map, uint256 index) internalviewreturns (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.
*/functiontryGet(UintToAddressMap storage map, uint256 key) internalviewreturns (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.
*/functionget(UintToAddressMap storage map, uint256 key) internalviewreturns (address) {
returnaddress(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}.
*/functionget(
UintToAddressMap storage map,
uint256 key,
stringmemory errorMessage
) internalviewreturns (address) {
returnaddress(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
}
// AddressToUintMapstructAddressToUintMap {
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.
*/functionset(
AddressToUintMap storage map,
address key,
uint256 value
) internalreturns (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.
*/functionremove(AddressToUintMap storage map, address key) internalreturns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(AddressToUintMap storage map, address key) internalviewreturns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/functionlength(AddressToUintMap storage map) internalviewreturns (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}.
*/functionat(AddressToUintMap storage map, uint256 index) internalviewreturns (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.
*/functiontryGet(AddressToUintMap storage map, address key) internalviewreturns (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.
*/functionget(AddressToUintMap storage map, address key) internalviewreturns (uint256) {
returnuint256(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}.
*/functionget(
AddressToUintMap storage map,
address key,
stringmemory errorMessage
) internalviewreturns (uint256) {
returnuint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
}
// Bytes32ToUintMapstructBytes32ToUintMap {
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.
*/functionset(
Bytes32ToUintMap storage map,
bytes32 key,
uint256 value
) internalreturns (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.
*/functionremove(Bytes32ToUintMap storage map, bytes32 key) internalreturns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(Bytes32ToUintMap storage map, bytes32 key) internalviewreturns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/functionlength(Bytes32ToUintMap storage map) internalviewreturns (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}.
*/functionat(Bytes32ToUintMap storage map, uint256 index) internalviewreturns (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.
*/functiontryGet(Bytes32ToUintMap storage map, bytes32 key) internalviewreturns (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.
*/functionget(Bytes32ToUintMap storage map, bytes32 key) internalviewreturns (uint256) {
returnuint256(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}.
*/functionget(
Bytes32ToUintMap storage map,
bytes32 key,
stringmemory errorMessage
) internalviewreturns (uint256) {
returnuint256(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.pragmasolidity ^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.
* ====
*/libraryEnumerableSet{
// 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.structSet {
// Storage of set valuesbytes32[] _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) privatereturns (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;
returntrue;
} else {
returnfalse;
}
}
/**
* @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) privatereturns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slotuint256 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 slotdelete set._indexes[value];
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/function_contains(Set storage set, bytes32 value) privateviewreturns (bool) {
return set._indexes[value] !=0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/function_length(Set storage set) privateviewreturns (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) privateviewreturns (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) privateviewreturns (bytes32[] memory) {
return set._values;
}
// Bytes32SetstructBytes32Set {
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.
*/functionadd(Bytes32Set storage set, bytes32 value) internalreturns (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.
*/functionremove(Bytes32Set storage set, bytes32 value) internalreturns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(Bytes32Set storage set, bytes32 value) internalviewreturns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(Bytes32Set storage set) internalviewreturns (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}.
*/functionat(Bytes32Set storage set, uint256 index) internalviewreturns (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.
*/functionvalues(Bytes32Set storage set) internalviewreturns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
// AddressSetstructAddressSet {
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.
*/functionadd(AddressSet storage set, address value) internalreturns (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.
*/functionremove(AddressSet storage set, address value) internalreturns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(AddressSet storage set, address value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(AddressSet storage set) internalviewreturns (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}.
*/functionat(AddressSet storage set, uint256 index) internalviewreturns (address) {
returnaddress(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.
*/functionvalues(AddressSet storage set) internalviewreturns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
// UintSetstructUintSet {
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.
*/functionadd(UintSet storage set, uint256 value) internalreturns (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.
*/functionremove(UintSet storage set, uint256 value) internalreturns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(UintSet storage set, uint256 value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(UintSet storage set) internalviewreturns (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}.
*/functionat(UintSet storage set, uint256 index) internalviewreturns (uint256) {
returnuint256(_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.
*/functionvalues(UintSet storage set) internalviewreturns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
}
Contract Source Code
File 10 of 24: IARM.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;/// @notice This interface contains the only ARM-related functions that might be used on-chain by other CCIP contracts.interfaceIARM{
/// @notice A Merkle root tagged with the address of the commit store contract it is destined for.structTaggedRoot {
address commitStore;
bytes32 root;
}
/// @notice Callers MUST NOT cache the return value as a blessed tagged root could become unblessed.functionisBlessed(TaggedRoot calldata taggedRoot) externalviewreturns (bool);
/// @notice When the ARM is "cursed", CCIP pauses until the curse is lifted.functionisCursed() externalviewreturns (bool);
}
Contract Source Code
File 11 of 24: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed 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.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (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.
*/functiontransfer(address to, uint256 amount) externalreturns (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.
*/functionallowance(address owner, address spender) externalviewreturns (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.
*/functionapprove(address spender, uint256 amount) externalreturns (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.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) externalreturns (bool);
}
Contract Source Code
File 12 of 24: IEVM2AnyOnRamp.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;import {IPool} from"./pools/IPool.sol";
import {Client} from"../libraries/Client.sol";
import {Internal} from"../libraries/Internal.sol";
import {IERC20} from"../../vendor/openzeppelin-solidity/v4.8.0/token/ERC20/IERC20.sol";
interfaceIEVM2AnyOnRamp{
/// @notice Get the fee for a given ccip message/// @param message The message to calculate the cost for/// @return fee The calculated feefunctiongetFee(Client.EVM2AnyMessage calldata message) externalviewreturns (uint256 fee);
/// @notice Get the pool for a specific token/// @param sourceToken The source chain token to get the pool for/// @return pool Token poolfunctiongetPoolBySourceToken(IERC20 sourceToken) externalviewreturns (IPool);
/// @notice Gets a list of all supported source chain tokens./// @return tokens The addresses of all tokens that this onRamp supports for sending.functiongetSupportedTokens() externalviewreturns (address[] memory tokens);
/// @notice Gets the next sequence number to be used in the onRamp/// @return the next sequence number to be usedfunctiongetExpectedNextSequenceNumber() externalviewreturns (uint64);
/// @notice Get the next nonce for a given sender/// @param sender The sender to get the nonce for/// @return nonce The next nonce for the senderfunctiongetSenderNonce(address sender) externalviewreturns (uint64 nonce);
/// @notice Adds and removed token pools./// @param removes The tokens and pools to be removed/// @param adds The tokens and pools to be added.functionapplyPoolUpdates(Internal.PoolUpdate[] memory removes, Internal.PoolUpdate[] memory adds) external;
/// @notice Send a message to the remote chain/// @dev only callable by the Router/// @dev approve() must have already been called on the token using the this ramp address as the spender./// @dev if the contract is paused, this function will revert./// @param message Message struct to send/// @param originalSender The original initiator of the CCIP requestfunctionforwardFromRouter(
Client.EVM2AnyMessage memory message,
uint256 feeTokenAmount,
address originalSender
) externalreturns (bytes32);
}
Contract Source Code
File 13 of 24: ILinkAvailable.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;/// @notice Implement this contract so that a keeper-compatible contract can monitor/// and fund the implementation contract with LINK if it falls below a defined threshold.interfaceILinkAvailable{
functionlinkAvailableForPayment() externalviewreturns (int256 availableBalance);
}
Contract Source Code
File 14 of 24: IPool.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;import {IERC20} from"../../../vendor/openzeppelin-solidity/v4.8.0/token/ERC20/IERC20.sol";
// Shared public interface for multiple pool types.// Each pool type handles a different child token model (lock/unlock, mint/burn.)interfaceIPool{
/// @notice Lock tokens into the pool or burn the tokens./// @param originalSender Original sender of the tokens./// @param receiver Receiver of the tokens on destination chain./// @param amount Amount to lock or burn./// @param destChainSelector Destination chain Id./// @param extraArgs Additional data passed in by sender for lockOrBurn processing/// in custom pools on source chain./// @return retData Optional field that contains bytes. Unused for now but already/// implemented to allow future upgrades while preserving the interface.functionlockOrBurn(address originalSender,
bytescalldata receiver,
uint256 amount,
uint64 destChainSelector,
bytescalldata extraArgs
) externalreturns (bytesmemory);
/// @notice Releases or mints tokens to the receiver address./// @param originalSender Original sender of the tokens./// @param receiver Receiver of the tokens./// @param amount Amount to release or mint./// @param sourceChainSelector Source chain Id./// @param extraData Additional data supplied offchain for releaseOrMint processing in/// custom pools on dest chain. This could be an attestation that was retrieved through a/// third party API./// @dev offchainData can come from any untrusted source.functionreleaseOrMint(bytesmemory originalSender,
address receiver,
uint256 amount,
uint64 sourceChainSelector,
bytesmemory extraData
) external;
/// @notice Gets the IERC20 token that this pool can lock or burn./// @return token The IERC20 token representation.functiongetToken() externalviewreturns (IERC20 token);
}
Contract Source Code
File 15 of 24: IPriceRegistry.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;import {Internal} from"../libraries/Internal.sol";
interfaceIPriceRegistry{
/// @notice Update the price for given tokens and destination chain./// @param priceUpdates The price updates to apply.functionupdatePrices(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.functiongetTokenPrice(address token) externalviewreturns (Internal.TimestampedUint192Value 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.functiongetValidatedTokenPrice(address token) externalviewreturns (uint192);
/// @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.functiongetTokenPrices(address[] calldata tokens) externalviewreturns (Internal.TimestampedUint192Value[] memory);
/// @notice Get the `gasPrice` for a given destination chain ID./// @param destChainSelector The destination chain to get the price for./// @return gasPrice The gasPrice for the given destination chain ID.functiongetDestinationChainGasPrice(uint64 destChainSelector
) externalviewreturns (Internal.TimestampedUint192Value 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.functiongetTokenAndGasPrices(address token,
uint64 destChainSelector
) externalviewreturns (uint192 tokenPrice, uint192 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.functionconvertTokenAmount(address fromToken,
uint256 fromTokenAmount,
address toToken
) externalviewreturns (uint256 toTokenAmount);
}
Contract Source Code
File 16 of 24: Internal.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;import {Client} from"./Client.sol";
import {MerkleMultiProof} from"../libraries/MerkleMultiProof.sol";
// Library for CCIP internal definitions common to multiple contracts.libraryInternal{
structPriceUpdates {
TokenPriceUpdate[] tokenPriceUpdates;
uint64 destChainSelector; // --┐ Destination chain selectoruint192 usdPerUnitGas; // -----┘ 1e18 USD per smallest unit (e.g. wei) of destination chain gas
}
structTokenPriceUpdate {
address sourceToken; // Source tokenuint192 usdPerToken; // 1e18 USD per smallest unit of token
}
structTimestampedUint192Value {
uint192 value; // -------┐ The price, in 1e18 USD.uint64 timestamp; // ----┘ Timestamp of the most recent price update.
}
structPoolUpdate {
address token; // The IERC20 token addressaddress pool; // The token pool address
}
structExecutionReport {
EVM2EVMMessage[] messages;
// Contains a bytes array for each message// each inner bytes array contains bytes per transferred tokenbytes[][] offchainTokenData;
bytes32[] proofs;
uint256 proofFlagBits;
}
// @notice The cross chain message that gets committed to EVM chainsstructEVM2EVMMessage {
uint64 sourceChainSelector;
uint64 sequenceNumber;
uint256 feeTokenAmount;
address sender;
uint64 nonce;
uint256 gasLimit;
bool strict;
// User fieldsaddress receiver;
bytes data;
Client.EVMTokenAmount[] tokenAmounts;
address feeToken;
bytes32 messageId;
}
function_toAny2EVMMessage(
EVM2EVMMessage memory original,
Client.EVMTokenAmount[] memory destTokenAmounts
) internalpurereturns (Client.Any2EVMMessage memory message) {
message = Client.Any2EVMMessage({
messageId: original.messageId,
sourceChainSelector: original.sourceChainSelector,
sender: abi.encode(original.sender),
data: original.data,
destTokenAmounts: destTokenAmounts
});
}
bytes32internalconstant EVM_2_EVM_MESSAGE_HASH =keccak256("EVM2EVMMessageEvent");
function_hash(EVM2EVMMessage memory original, bytes32 metadataHash) internalpurereturns (bytes32) {
returnkeccak256(
abi.encode(
MerkleMultiProof.LEAF_DOMAIN_SEPARATOR,
metadataHash,
original.sequenceNumber,
original.nonce,
original.sender,
original.receiver,
keccak256(original.data),
keccak256(abi.encode(original.tokenAmounts)),
original.gasLimit,
original.strict,
original.feeToken,
original.feeTokenAmount
)
);
}
/// @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.enumMessageExecutionState {
UNTOUCHED,
IN_PROGRESS,
SUCCESS,
FAILURE
}
}
Contract Source Code
File 17 of 24: MerkleMultiProof.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;libraryMerkleMultiProof{
/// @notice Leaf domain separator, should be used as the first 32 bytes of a leaf's preimage.bytes32internalconstant LEAF_DOMAIN_SEPARATOR =0x0000000000000000000000000000000000000000000000000000000000000000;
/// @notice Internal domain separator, should be used as the first 32 bytes of an internal node's preiimage.bytes32internalconstant INTERNAL_DOMAIN_SEPARATOR =0x0000000000000000000000000000000000000000000000000000000000000001;
uint256internalconstant MAX_NUM_HASHES =256;
errorInvalidProof();
errorLeavesCannotBeEmpty();
/// @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-underscorefunctionmerkleRoot(bytes32[] memory leaves,
bytes32[] memory proofs,
uint256 proofFlagBits
) internalpurereturns (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 =newbytes32[](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) privatepurereturns (bytes32 hash) {
returnkeccak256(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) privatepurereturns (bytes32) {
return a < b ? _hashInternalNode(a, b) : _hashInternalNode(b, a);
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import {ConfirmedOwner} from"../../ConfirmedOwner.sol";
/// @title The OwnerIsCreator contract/// @notice A contract with helpers for basic contract ownership.contractOwnerIsCreatorisConfirmedOwner{
constructor() ConfirmedOwner(msg.sender) {}
}
Contract Source Code
File 20 of 24: RateLimiter.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^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.libraryRateLimiter{
errorBucketOverfilled();
errorOnlyCallableByAdminOrOwner();
errorTokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress);
errorTokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress);
errorAggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested);
errorAggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available);
eventTokensConsumed(uint256 tokens);
eventConfigChanged(Config config);
structTokenBucket {
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 notuint128 capacity; // ----┐ Maximum number of tokens that can be in the bucket.uint128 rate; // --------┘ Number of tokens per second that the bucket is refilled.
}
structConfig {
bool isEnabled; // Indication whether the rate limiting should be enableduint128 capacity; // ----┐ Specifies the capacity of the rate limiteruint128 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 > 0function_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 usageif (!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) internalviewreturns (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 configfunction_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 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 refillfunction_calculateRefill(uint256 capacity,
uint256 tokens,
uint256 timeDiff,
uint256 rate
) privatepurereturns (uint256) {
return _min(capacity, tokens + timeDiff * rate);
}
/// @notice Return the smallest of two integers/// @param a first int/// @param b second int/// @return smallestfunction_min(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
}
Contract Source Code
File 21 of 24: SafeERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)pragmasolidity ^0.8.0;import"../IERC20.sol";
import"../extensions/draft-IERC20Permit.sol";
import"../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/librarySafeERC20{
usingAddressforaddress;
functionsafeTransfer(
IERC20 token,
address to,
uint256 value
) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
functionsafeTransferFrom(
IERC20 token,
addressfrom,
address to,
uint256 value
) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/functionsafeApprove(
IERC20 token,
address spender,
uint256 value
) internal{
// safeApprove should only be called when setting an initial allowance,// or when resetting it to zero. To increase and decrease it, use// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'require(
(value ==0) || (token.allowance(address(this), spender) ==0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
functionsafeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal{
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
functionsafeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal{
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
functionsafePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal{
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore +1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/function_callOptionalReturn(IERC20 token, bytesmemory data) private{
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that// the target address contains contract code and also asserts for success in the low-level call.bytesmemory returndata =address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length>0) {
// Return data is optionalrequire(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;libraryUSDPriceWith18Decimals{
/// @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(uint192 tokenPrice, uint256 tokenAmount) internalpurereturns (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 = $5return (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(uint192 tokenPrice, uint256 usdValue) internalpurereturns (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 USDCreturn (usdValue *1e18) / tokenPrice;
}
}
Contract Source Code
File 24 of 24: draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/interfaceIERC20Permit{
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/functionnonces(address owner) externalviewreturns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/// solhint-disable-next-line func-name-mixedcasefunctionDOMAIN_SEPARATOR() externalviewreturns (bytes32);
}