Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-OffRamp - per-chain configs #761

Merged
merged 20 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
223632f
chore: create multi chain copies for ARL and offramp
elatoskinas Apr 19, 2024
5cb8014
chore: create multi chain copy for multi ARL tests
elatoskinas Apr 23, 2024
16278f0
feat: implement multi-chain source config for offramp
elatoskinas Apr 25, 2024
a282597
test: helpers and setup for multiofframp
elatoskinas Apr 25, 2024
d45250f
test: base test setup with metadatahash tests
elatoskinas Apr 25, 2024
9b9b0dd
test: release or mint tests
elatoskinas Apr 25, 2024
9707007
test: apply source chain updates tests
elatoskinas Apr 26, 2024
2e78cac
style: apply formatting
elatoskinas Apr 26, 2024
f861c71
chore: generate snapshots and wrappers
elatoskinas Apr 26, 2024
2f2a57f
feat: implement multi-offramp constructor
elatoskinas Apr 26, 2024
72f1170
refactor: convert source config update to struct args
elatoskinas Apr 29, 2024
f24383a
test: add fuzz test for metadata hash collisions
elatoskinas Apr 29, 2024
2318faa
refactor: remove source chain selectors getter and add additional todos
elatoskinas May 1, 2024
cadf65f
Merge branch 'ccip-develop' into feat/multi-offramp-source-config
elatoskinas May 3, 2024
498249e
chore: fix warnings and add todo for isEnabled
elatoskinas May 7, 2024
d1bbaa4
Merge branch 'ccip-develop' into feat/multi-offramp-source-config
elatoskinas May 7, 2024
c51dab9
docs: add changeset
elatoskinas May 7, 2024
3dafce4
chore: fix formatting
elatoskinas May 7, 2024
8556b4f
style: rename source chain config structs and functions
elatoskinas May 7, 2024
31e765b
chore: update gas snapshots and wrappers
elatoskinas May 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 33 additions & 1 deletion contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,31 @@ CommitStore_verify:test_TooManyLeaves_Revert() (gas: 36807)
DefensiveExampleTest:test_HappyPath_Success() (gas: 207503)
DefensiveExampleTest:test_Recovery() (gas: 431738)
E2E:test_E2E_3MessagesSuccess_gas() (gas: 1036239)
EVM2EVMMultiOffRamp__releaseOrMintTokens:test_PriceNotFoundForToken_Reverts() (gas: 132616)
EVM2EVMMultiOffRamp__releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 154975)
EVM2EVMMultiOffRamp__releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAContract_Reverts() (gas: 37960)
EVM2EVMMultiOffRamp__releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 40130)
EVM2EVMMultiOffRamp__releaseOrMintTokens:test_fuzz__releaseOrMintTokens_AnyRevertIsCaught_Success(uint256) (runs: 10024, μ: 30105, ~: 30806)
EVM2EVMMultiOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 184928)
EVM2EVMMultiOffRamp_applySoureConfigUpdates:test_AddMultipleChains_Success() (gas: 312936)
EVM2EVMMultiOffRamp_applySoureConfigUpdates:test_AddNewChain_Success() (gas: 143178)
EVM2EVMMultiOffRamp_applySoureConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 16851)
EVM2EVMMultiOffRamp_applySoureConfigUpdates:test_MismatchingUpdateLenghts_Revert() (gas: 15840)
EVM2EVMMultiOffRamp_applySoureConfigUpdates:test_ReplaceExistingChain_Success() (gas: 152317)
EVM2EVMMultiOffRamp_applySoureConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 15530)
EVM2EVMMultiOffRamp_metadataHash:test_MetadataHashChangesOnOnRampAddress_Success() (gas: 11015)
EVM2EVMMultiOffRamp_metadataHash:test_MetadataHashChangesOnSourceChain_Success() (gas: 11074)
EVM2EVMMultiOffRamp_metadataHash:test_MetadataHash_Success() (gas: 9162)
EVM2EVMMultiOffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 40366)
EVM2EVMMultiOffRamp_setDynamicConfig:test_RouterZeroAddress_Revert() (gas: 38552)
EVM2EVMMultiOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 142101)
EVM2EVMOffRamp__releaseOrMintTokens:test_OverValueWithARLOff_Success() (gas: 325154)
EVM2EVMOffRamp__releaseOrMintTokens:test_PriceNotFoundForToken_Reverts() (gas: 125872)
EVM2EVMOffRamp__releaseOrMintTokens:test_RateLimitErrors_Reverts() (gas: 647365)
EVM2EVMOffRamp__releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 148120)
EVM2EVMOffRamp__releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAContract_Reverts() (gas: 27226)
EVM2EVMOffRamp__releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 29377)
EVM2EVMOffRamp__releaseOrMintTokens:test_fuzz__releaseOrMintTokens_AnyRevertIsCaught_Success(uint256) (runs: 256, μ: 23191, ~: 23925)
EVM2EVMOffRamp__releaseOrMintTokens:test_fuzz__releaseOrMintTokens_AnyRevertIsCaught_Success(uint256) (runs: 10024, μ: 23227, ~: 23925)
EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 177373)
EVM2EVMOffRamp__report:test_Report_Success() (gas: 127403)
EVM2EVMOffRamp__trialExecute:test_RateLimitError_Success() (gas: 200330)
Expand Down Expand Up @@ -303,6 +321,20 @@ MerkleMultiProofTest:test_EmptyLeaf_Revert() (gas: 3574)
MerkleMultiProofTest:test_MerkleRoot256() (gas: 411772)
MerkleMultiProofTest:test_MerkleRootSingleLeaf_Success() (gas: 3649)
MerkleMultiProofTest:test_SpecSync_gas() (gas: 34123)
MultieAggregateRateLimiter__getTokenValue:test_GetTokenValue_Success() (gas: 17308)
MultieAggregateRateLimiter__getTokenValue:test_NoTokenPrice_Reverts() (gas: 18887)
MultieAggregateRateLimiter__rateLimitValue:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 16391)
MultieAggregateRateLimiter__rateLimitValue:test_RateLimitValueSuccess_gas() (gas: 18288)
MultieAggregateRateLimiter_constructor:test_Constructor_Success() (gas: 26914)
MultieAggregateRateLimiter_getTokenBucket:test_GetTokenBucket_Success() (gas: 19685)
MultieAggregateRateLimiter_getTokenBucket:test_Refill_Success() (gas: 40881)
MultieAggregateRateLimiter_getTokenBucket:test_TimeUnderflow_Revert() (gas: 15347)
MultieAggregateRateLimiter_getTokenLimitAdmin:test_GetTokenLimitAdmin_Success() (gas: 10531)
MultieAggregateRateLimiter_setAdmin:test_OnlyOwnerOrAdmin_Revert() (gas: 13047)
MultieAggregateRateLimiter_setAdmin:test_Owner_Success() (gas: 18989)
MultieAggregateRateLimiter_setRateLimiterConfig:test_OnlyOnlyCallableByAdminOrOwner_Revert() (gas: 17479)
MultieAggregateRateLimiter_setRateLimiterConfig:test_Owner_Success() (gas: 30043)
MultieAggregateRateLimiter_setRateLimiterConfig:test_TokenLimitAdmin_Success() (gas: 32052)
OCR2BaseNoChecks_setOCR2Config:test_FMustBePositive_Revert() (gas: 12278)
OCR2BaseNoChecks_setOCR2Config:test_RepeatAddress_Revert() (gas: 42395)
OCR2BaseNoChecks_setOCR2Config:test_SetConfigSuccess_gas() (gas: 84878)
Expand Down
1 change: 1 addition & 0 deletions contracts/scripts/native_solc_compile_all_ccip
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ compileContractLowOpts () {
# Solc produces and overwrites intermediary contracts.
# Contracts should be ordered in reverse-import-complexity-order to minimize overwrite risks.
compileContract ccip/offRamp/EVM2EVMOffRamp.sol
compileContract ccip/offRamp/EVM2EVMMultiOffRamp.sol
compileContract ccip/applications/PingPongDemo.sol
compileContract ccip/applications/SelfFundedPingPong.sol
compileContract ccip/applications/EtherSenderReceiver.sol
Expand Down
93 changes: 93 additions & 0 deletions contracts/src/v0.8/ccip/MultiAggregateRateLimiter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 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";

/// @notice The aggregate rate limiter is a wrapper of the token bucket rate limiter
/// which permits rate limiting based on the aggregate value of a group of
/// token transfers, using a price registry to convert to a numeraire asset (e.g. USD).
contract MultiAggregateRateLimiter is OwnerIsCreator {
using RateLimiter for RateLimiter.TokenBucket;
using USDPriceWith18Decimals for uint224;

error PriceNotFoundForToken(address token);

event AdminSet(address newAdmin);

// The address of the token limit admin that has the same permissions as the owner.
address internal s_admin;

// The token bucket object that contains the bucket state.
RateLimiter.TokenBucket private s_rateLimiter;

/// @param config The RateLimiter.Config 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.
function _rateLimitValue(uint256 value) internal {
s_rateLimiter._consume(value, address(0));
}

function _getTokenValue(
Client.EVMTokenAmount memory tokenAmount,
IPriceRegistry priceRegistry
) internal view returns (uint256) {
// not fetching validated price, as price staleness is not important for value-based rate limiting
// we only need to verify the price is not 0
uint224 pricePerToken = priceRegistry.getTokenPrice(tokenAmount.token).value;
if (pricePerToken == 0) revert PriceNotFoundForToken(tokenAmount.token);
return pricePerToken._calcUSDValueFromTokenAmount(tokenAmount.amount);
}

/// @notice Gets the token bucket with its values for the block it was requested at.
/// @return The token bucket.
function currentRateLimiterState() external view returns (RateLimiter.TokenBucket memory) {
return s_rateLimiter._currentTokenBucketState();
}

/// @notice Sets the rate limited config.
/// @param config The new rate limiter config.
/// @dev should only be callable by the owner or token limit admin.
function setRateLimiterConfig(RateLimiter.Config memory config) external onlyAdminOrOwner {
s_rateLimiter._setTokenBucketConfig(config);
}

// ================================================================
// │ Access │
// ================================================================

/// @notice Gets the token limit admin address.
/// @return the token limit admin address.
function getTokenLimitAdmin() external view returns (address) {
return s_admin;
}

/// @notice Sets the token limit admin address.
/// @param newAdmin the address of the new admin.
/// @dev setting this to address(0) indicates there is no active admin.
function setAdmin(address newAdmin) external onlyAdminOrOwner {
s_admin = newAdmin;
emit AdminSet(newAdmin);
}

/// @notice a modifier that allows the owner or the s_tokenLimitAdmin call the functions
/// it is applied to.
modifier onlyAdminOrOwner() {
if (msg.sender != owner() && msg.sender != s_admin) revert RateLimiter.OnlyCallableByAdminOrOwner();
_;
}
}