Skip to content

Commit

Permalink
Merge branch 'ccip-develop' into feat/transmitter-validation
Browse files Browse the repository at this point in the history
  • Loading branch information
elatoskinas committed Sep 13, 2024
2 parents 291f0fa + a847f78 commit a4255bb
Show file tree
Hide file tree
Showing 33 changed files with 2,293 additions and 1,686 deletions.
2 changes: 1 addition & 1 deletion contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ single_line_statement_blocks = "preserve"
solc_version = '0.8.24'
src = 'src/v0.8/ccip'
test = 'src/v0.8/ccip/test'
optimizer_runs = 1_925
optimizer_runs = 800
evm_version = 'paris'

[profile.functions]
Expand Down
1,985 changes: 996 additions & 989 deletions contracts/gas-snapshots/ccip.gas-snapshot

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions contracts/scripts/native_solc_compile_all_ccip
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ compileContract ccip/test/mocks/MockE2EUSDCTokenMessenger.sol
compileContract ccip/test/mocks/MockE2EUSDCTransmitter.sol
compileContract ccip/test/WETH9.sol


# Encoding Utils
compileContract ccip/CCIPEncodingUtils.sol

# Customer contracts
compileContract ccip/pools/USDC/USDCTokenPool.sol

Expand Down
18 changes: 18 additions & 0 deletions contracts/src/v0.8/ccip/CCIPEncodingUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {RMNRemote} from "./rmn/RMNRemote.sol";

/// @dev this file exposes structs that are otherwise internal to the CCIP codebase
/// doing this allows those structs to be encoded and decoded with type safety in offchain code
/// and tests because generated wrappers are available
contract CCIPEncodingUtils {
error DoNotDeploy();

constructor() {
revert DoNotDeploy();
}

/// @dev the RMN Report struct is used in integration / E2E tests
function _rmnReport(bytes32 rmnReportVersion, RMNRemote.Report memory rmnReport) external {}
}
97 changes: 68 additions & 29 deletions contracts/src/v0.8/ccip/FeeQuoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
event FeeTokenRemoved(address indexed feeToken);
event UsdPerUnitGasUpdated(uint64 indexed destChain, uint256 value, uint256 timestamp);
event UsdPerTokenUpdated(address indexed token, uint256 value, uint256 timestamp);
event PriceFeedPerTokenUpdated(address indexed token, IFeeQuoter.TokenPriceFeedConfig priceFeedConfig);
event PriceFeedPerTokenUpdated(address indexed token, TokenPriceFeedConfig priceFeedConfig);
event TokenTransferFeeConfigUpdated(
uint64 indexed destChainSelector, address indexed token, TokenTransferFeeConfig tokenTransferFeeConfig
);
Expand All @@ -58,10 +58,16 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
event DestChainConfigUpdated(uint64 indexed destChainSelector, DestChainConfig destChainConfig);
event DestChainAdded(uint64 indexed destChainSelector, DestChainConfig destChainConfig);

/// @dev Token price data feed configuration
struct TokenPriceFeedConfig {
address dataFeedAddress; // ──╮ AggregatorV3Interface contract (0 - feed is unset)
uint8 tokenDecimals; // ──────╯ Decimals of the token that the feed represents
}

/// @dev Token price data feed update
struct TokenPriceFeedUpdate {
address sourceToken; // Source token to update feed for
IFeeQuoter.TokenPriceFeedConfig feedConfig; // Feed config update data
TokenPriceFeedConfig feedConfig; // Feed config update data
}

/// @dev Struct that contains the static configuration
Expand Down Expand Up @@ -177,7 +183,7 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
mapping(address token => Internal.TimestampedPackedUint224 price) private s_usdPerToken;

/// @dev Stores the price data feed configurations per token.
mapping(address token => IFeeQuoter.TokenPriceFeedConfig dataFeedAddress) private s_usdPriceFeedsPerToken;
mapping(address token => TokenPriceFeedConfig dataFeedAddress) private s_usdPriceFeedsPerToken;

/// @dev The multiplier for destination chain specific premiums that can be set by the owner or fee admin
mapping(address token => uint64 premiumMultiplierWeiPerEth) private s_premiumMultiplierWeiPerEth;
Expand Down Expand Up @@ -239,7 +245,7 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
return tokenPrice;
}

IFeeQuoter.TokenPriceFeedConfig memory priceFeedConfig = s_usdPriceFeedsPerToken[token];
TokenPriceFeedConfig memory priceFeedConfig = s_usdPriceFeedsPerToken[token];

// If the token price feed is not set, return the stale price
if (priceFeedConfig.dataFeedAddress == address(0)) {
Expand All @@ -251,15 +257,17 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
return _getTokenPriceFromDataFeed(priceFeedConfig);
}

/// @inheritdoc IPriceRegistry
function getValidatedTokenPrice(address token) external view override returns (uint224) {
/// @notice Get the `tokenPrice` for a given token, checks if the price is valid.
/// @param token The token to get the price for.
/// @return tokenPrice The tokenPrice for the given token if it exists and is valid.
function getValidatedTokenPrice(address token) external view returns (uint224) {
return _getValidatedTokenPrice(token);
}

/// @inheritdoc IPriceRegistry
function getTokenPrices(
address[] calldata tokens
) external view override returns (Internal.TimestampedPackedUint224[] memory) {
/// @notice Get the `tokenPrice` for an array of tokens.
/// @param tokens The tokens to get prices for.
/// @return tokenPrices The tokenPrices for the given tokens.
function getTokenPrices(address[] calldata tokens) external view returns (Internal.TimestampedPackedUint224[] memory) {
uint256 length = tokens.length;
Internal.TimestampedPackedUint224[] memory tokenPrices = new Internal.TimestampedPackedUint224[](length);
for (uint256 i = 0; i < length; ++i) {
Expand All @@ -268,25 +276,36 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
return tokenPrices;
}

/// @inheritdoc IFeeQuoter
function getTokenPriceFeedConfig(
address token
) external view override returns (IFeeQuoter.TokenPriceFeedConfig memory) {
/// @notice Returns the token price data feed configuration
/// @param token The token to retrieve the feed config for
/// @return tokenPriceFeedConfig The token price data feed config (if feed address is 0, the feed config is disabled)
function getTokenPriceFeedConfig(address token) external view returns (TokenPriceFeedConfig memory) {
return s_usdPriceFeedsPerToken[token];
}

/// @inheritdoc IPriceRegistry
/// @notice Get an encoded `gasPrice` for a given destination chain ID.
/// The 224-bit result encodes necessary gas price components.
/// On L1 chains like Ethereum or Avax, the only component is the gas price.
/// On Optimistic Rollups, there are two components - the L2 gas price, and L1 base fee for data availability.
/// On future chains, there could be more or differing price components.
/// PriceRegistry does not contain chain-specific logic to parse destination chain price components.
/// @param destChainSelector The destination chain to get the price for.
/// @return gasPrice The encoded gasPrice for the given destination chain ID.
function getDestinationChainGasPrice(
uint64 destChainSelector
) external view override returns (Internal.TimestampedPackedUint224 memory) {
) external view returns (Internal.TimestampedPackedUint224 memory) {
return s_usdPerUnitGasByDestChainSelector[destChainSelector];
}

/// @inheritdoc IPriceRegistry
/// @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 gasPriceValue The price of gas in 1e18 dollars per base unit.
function getTokenAndGasPrices(
address token,
uint64 destChainSelector
) public view override returns (uint224 tokenPrice, uint224 gasPriceValue) {
) public view returns (uint224 tokenPrice, uint224 gasPriceValue) {
Internal.TimestampedPackedUint224 memory gasPrice = s_usdPerUnitGasByDestChainSelector[destChainSelector];
// We do allow a gas price of 0, but no stale or unset gas prices
if (gasPrice.timestamp == 0) revert ChainNotSupported(destChainSelector);
Expand All @@ -296,15 +315,19 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
return (_getValidatedTokenPrice(token), gasPrice.value);
}

/// @inheritdoc IPriceRegistry
/// @notice Convert a given token amount to target token amount.
/// @dev this function assumes that no more than 1e59 dollars are sent as payment.
/// If more is sent, the multiplication of feeTokenAmount and feeTokenValue will overflow.
/// Since there isn't even close to 1e59 dollars in the world economy this is safe.
/// @param fromToken The given token address.
/// @param fromTokenAmount The given token amount.
/// @param toToken The target token address.
/// @return toTokenAmount The target token amount.
function convertTokenAmount(
address fromToken,
uint256 fromTokenAmount,
address toToken
) public view override returns (uint256) {
) public view returns (uint256) {
/// Example:
/// fromTokenAmount: 1e18 // 1 ETH
/// ETH: 2_000e18
Expand All @@ -327,7 +350,7 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
/// @param priceFeedConfig token data feed configuration with valid data feed address (used to retrieve price & timestamp)
/// @return tokenPrice data feed price answer rebased to s_usdPerToken units, with latest block timestamp
function _getTokenPriceFromDataFeed(
IFeeQuoter.TokenPriceFeedConfig memory priceFeedConfig
TokenPriceFeedConfig memory priceFeedConfig
) internal view returns (Internal.TimestampedPackedUint224 memory tokenPrice) {
AggregatorV3Interface dataFeedContract = AggregatorV3Interface(priceFeedConfig.dataFeedAddress);
(
Expand Down Expand Up @@ -428,7 +451,7 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
for (uint256 i; i < tokenPriceFeedUpdates.length; ++i) {
TokenPriceFeedUpdate memory update = tokenPriceFeedUpdates[i];
address sourceToken = update.sourceToken;
IFeeQuoter.TokenPriceFeedConfig memory tokenPriceFeedConfig = update.feedConfig;
TokenPriceFeedConfig memory tokenPriceFeedConfig = update.feedConfig;

s_usdPriceFeedsPerToken[sourceToken] = tokenPriceFeedConfig;
emit PriceFeedPerTokenUpdated(sourceToken, tokenPriceFeedConfig);
Expand Down Expand Up @@ -839,12 +862,24 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
}

/// @inheritdoc IFeeQuoter
/// @dev precondition - rampTokenAmounts and sourceTokenAmounts lengths must be equal
function processMessageArgs(
uint64 destChainSelector,
address feeToken,
uint256 feeTokenAmount,
bytes calldata extraArgs
) external view returns (uint256 msgFeeJuels, bool isOutOfOrderExecution, bytes memory convertedExtraArgs) {
bytes calldata extraArgs,
Internal.RampTokenAmount[] calldata rampTokenAmounts,
Client.EVMTokenAmount[] calldata sourceTokenAmounts
)
external
view
returns (
uint256 msgFeeJuels,
bool isOutOfOrderExecution,
bytes memory convertedExtraArgs,
bytes[] memory destExecDataPerToken
)
{
// Convert feeToken to link if not already in link
if (feeToken == i_linkToken) {
msgFeeJuels = feeTokenAmount;
Expand All @@ -859,17 +894,21 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver,
// We can parse unvalidated args since this message is called after getFee (which will already validate the params)
Client.EVMExtraArgsV2 memory parsedExtraArgs = _parseUnvalidatedEVMExtraArgsFromBytes(extraArgs, defaultTxGasLimit);
isOutOfOrderExecution = parsedExtraArgs.allowOutOfOrderExecution;
destExecDataPerToken = _processPoolReturnData(destChainSelector, rampTokenAmounts, sourceTokenAmounts);

return (msgFeeJuels, isOutOfOrderExecution, Client._argsToBytes(parsedExtraArgs));
return (msgFeeJuels, isOutOfOrderExecution, Client._argsToBytes(parsedExtraArgs), destExecDataPerToken);
}

/// @inheritdoc IFeeQuoter
/// @dev precondition - rampTokenAmounts and sourceTokenAmounts lengths must be equal
function processPoolReturnData(
/// @notice Validates pool return data
/// @param destChainSelector Destination chain selector to which the token amounts are sent to
/// @param rampTokenAmounts Token amounts with populated pool return data
/// @param sourceTokenAmounts Token amounts originally sent in a Client.EVM2AnyMessage message
/// @return destExecDataPerToken Destination chain execution data
function _processPoolReturnData(
uint64 destChainSelector,
Internal.RampTokenAmount[] calldata rampTokenAmounts,
Client.EVMTokenAmount[] calldata sourceTokenAmounts
) external view returns (bytes[] memory destExecDataPerToken) {
) internal view returns (bytes[] memory destExecDataPerToken) {
bytes4 chainFamilySelector = s_destChainConfigs[destChainSelector].chainFamilySelector;
destExecDataPerToken = new bytes[](rampTokenAmounts.length);
for (uint256 i = 0; i < rampTokenAmounts.length; ++i) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ library CCIPConfigTypes {
/// The only valid transition from "Init" is to the "Running" state - this is the first ever configuration.
/// The only valid transition from "Running" is to the "Staging" state - this is a blue/green proposal.
/// The only valid transition from "Staging" is back to the "Running" state - this is a promotion.
/// TODO: explain rollbacks?
/// In order to rollback a configuration, we must therefore do the following:
/// - Suppose that we have a correct configuration in the "Running" state (V1).
/// - We propose a new configuration and transition to the "Staging" state (V2).
/// - V2 turns out to be buggy
/// - In the same transaction, we must:
/// - Promote V2
/// - Re-propose V1
/// - Promote V1
enum ConfigState {
Init,
Running,
Expand Down
Binary file modified contracts/src/v0.8/ccip/docs/multi-chain-overview-ocr3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit a4255bb

Please sign in to comment.