Skip to content

Commit 9862f62

Browse files
CCIP-4748 CCTP Upgradeability Fix (smartcontractkit#16140)
* CCTPMessageTransmitterProxy + test * fix exiting test * Add test for new version of USDCTokenPool.sol * update wrappers * add changeset * fix integration tests * update wrapper dependency * [Bot] Update changeset file with jira issues * fix imports in deployments * update comments * merge * change version to dev * remove extra geth wrapper * configure mapping for allowed callers * update comments for USDC pool burn * replace allowedCallers mapping with EnumerableSet * fixing comments --------- Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com>
1 parent aea1824 commit 9862f62

18 files changed

+402
-57
lines changed
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@chainlink/contracts': patch
3+
---
4+
5+
#internal Add Proxy contract for resolving problem with USDC upgradeability
6+
7+
PR issue: CCIP-4748
8+
9+
Solidity Review issue: CCIP-3966

contracts/gas-snapshots/ccip.gas-snapshot

+29-24
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ CCIPHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_mu
2323
CCIPHome_revokeCandidate:test_revokeCandidate() (gas: 30647)
2424
CCIPHome_setCandidate:test_setCandidate() (gas: 1365392)
2525
CCIPHome_supportsInterface:test_supportsInterface() (gas: 9885)
26+
CCTPMessageTransmitterProxy_configureAllowedCallers:test_configureAllowedCallers() (gas: 65308)
27+
CCTPMessageTransmitterProxy_getAllowedCallers:test_configureAllowedCallers() (gas: 75076)
28+
CCTPMessageTransmitterProxy_getCCTPTransmitter:test_getCCTPTransmitter() (gas: 10536)
29+
CCTPMessageTransmitterProxy_receiveMesssage:test_receiveMesssage() (gas: 31304)
2630
DefensiveExampleTest:test_HappyPath() (gas: 200535)
2731
DefensiveExampleTest:test_Recovery() (gas: 424996)
2832
E2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1519925)
@@ -136,11 +140,11 @@ FeeQuoter_updateTokenPriceFeeds:test_SingleFeedUpdate() (gas: 53171)
136140
FeeQuoter_updateTokenPriceFeeds:test_ZeroFeeds() (gas: 12471)
137141
FeeQuoter_validateDestFamilyAddress:test_ValidEVMAddress() (gas: 6840)
138142
FeeQuoter_validateDestFamilyAddress:test_ValidSVMAddress() (gas: 6701)
139-
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_PrimaryMechanism() (gas: 130339)
140-
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism() (gas: 140169)
141-
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 202967)
142-
HybridLockReleaseUSDCTokenPool_releaseOrMint:test_OnLockReleaseMechanism() (gas: 206350)
143-
HybridLockReleaseUSDCTokenPool_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 260423)
143+
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_PrimaryMechanism() (gas: 130321)
144+
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism() (gas: 140191)
145+
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 202932)
146+
HybridLockReleaseUSDCTokenPool_releaseOrMint:test_OnLockReleaseMechanism() (gas: 206305)
147+
HybridLockReleaseUSDCTokenPool_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 265499)
144148
LockReleaseTokenPool_canAcceptLiquidity:test_CanAcceptLiquidity() (gas: 3222607)
145149
LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList() (gas: 72828)
146150
LockReleaseTokenPool_releaseOrMint:test_ReleaseOrMint() (gas: 217898)
@@ -416,23 +420,24 @@ TokenPool_parseRemoteDecimals:test_parseRemoteDecimals() (gas: 14030)
416420
TokenPool_parseRemoteDecimals:test_parseRemoteDecimals_NoDecimalsDefaultsToLocalDecimals() (gas: 9705)
417421
TokenPool_removeRemotePool:test_removeRemotePool() (gas: 188402)
418422
TokenPool_setRateLimitAdmin:test_SetRateLimitAdmin() (gas: 37630)
419-
USDCBridgeMigrator_BurnLockedUSDC:test_PrimaryMechanism() (gas: 130502)
420-
USDCBridgeMigrator_BurnLockedUSDC:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303967)
421-
USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism() (gas: 140236)
422-
USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203330)
423-
USDCBridgeMigrator_cancelMigrationProposal:test_cancelExistingCCTPMigrationProposal() (gas: 56100)
424-
USDCBridgeMigrator_provideLiquidity:test_PrimaryMechanism() (gas: 130520)
425-
USDCBridgeMigrator_provideLiquidity:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303967)
426-
USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism() (gas: 140325)
427-
USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203331)
428-
USDCBridgeMigrator_releaseOrMint:test_OnLockReleaseMechanism() (gas: 206383)
429-
USDCBridgeMigrator_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 260476)
430-
USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_destChain() (gas: 142853)
431-
USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_homeChain() (gas: 505540)
432-
USDCBridgeMigrator_updateChainSelectorMechanism:test_PrimaryMechanism() (gas: 130502)
433-
USDCBridgeMigrator_updateChainSelectorMechanism:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303949)
434-
USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism() (gas: 140325)
435-
USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203312)
436-
USDCTokenPool_lockOrBurn:test_LockOrBurn() (gas: 128094)
437-
USDCTokenPool_releaseOrMint:test_ReleaseOrMintRealTx() (gas: 260189)
423+
USDCBridgeMigrator_BurnLockedUSDC:test_PrimaryMechanism() (gas: 130484)
424+
USDCBridgeMigrator_BurnLockedUSDC:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 304002)
425+
USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism() (gas: 140258)
426+
USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203295)
427+
USDCBridgeMigrator_cancelMigrationProposal:test_cancelExistingCCTPMigrationProposal() (gas: 56135)
428+
USDCBridgeMigrator_provideLiquidity:test_PrimaryMechanism() (gas: 130503)
429+
USDCBridgeMigrator_provideLiquidity:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 304002)
430+
USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism() (gas: 140347)
431+
USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203296)
432+
USDCBridgeMigrator_releaseOrMint:test_OnLockReleaseMechanism() (gas: 206338)
433+
USDCBridgeMigrator_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 265552)
434+
USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_destChain() (gas: 142808)
435+
USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_homeChain() (gas: 510650)
436+
USDCBridgeMigrator_updateChainSelectorMechanism:test_PrimaryMechanism() (gas: 130484)
437+
USDCBridgeMigrator_updateChainSelectorMechanism:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303984)
438+
USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism() (gas: 140347)
439+
USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203277)
440+
USDCTokenPool_constructor:test_constructor() (gas: 3192321)
441+
USDCTokenPool_lockOrBurn:test_LockOrBurn() (gas: 128129)
442+
USDCTokenPool_releaseOrMint:test_ReleaseOrMintRealTx() (gas: 265265)
438443
USDCTokenPool_supportsInterface:test_SupportsInterface() (gas: 10108)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.24;
3+
4+
import {IMessageTransmitter} from "./IMessageTransmitter.sol";
5+
import {ITokenMessenger} from "./ITokenMessenger.sol";
6+
7+
import {Ownable2StepMsgSender} from "../../../shared/access/Ownable2StepMsgSender.sol";
8+
9+
import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol";
10+
11+
/// @title CCTP Message Transmitter Proxy
12+
/// @notice A proxy contract for handling messages transmitted via the Cross Chain Transfer Protocol (CCTP).
13+
/// @dev This contract is responsible for sending messages to the `IMessageTransmitter` and ensuring only allowed callers can invoke it.
14+
contract CCTPMessageTransmitterProxy is Ownable2StepMsgSender {
15+
using EnumerableSet for EnumerableSet.AddressSet;
16+
17+
/// @notice Error thrown when a function is called by an unauthorized address.
18+
error Unauthorized(address caller);
19+
20+
/// @notice Emitted when an allowed caller is added.
21+
event AllowedCallerAdded(address indexed caller);
22+
/// @notice Emitted when an allowed caller is removed.
23+
event AllowedCallerRemoved(address indexed caller);
24+
25+
struct AllowedCallerConfigArgs {
26+
address caller;
27+
bool allowed;
28+
}
29+
30+
/// @notice Immutable reference to the `IMessageTransmitter` contract.
31+
IMessageTransmitter public immutable i_cctpTransmitter;
32+
33+
/// @notice Enumerable set of addresses allowed to call `receiveMessage`.
34+
EnumerableSet.AddressSet private s_allowedCallers;
35+
36+
/// @notice One-time cyclic dependency between TokenPool and MessageTransmitter.
37+
constructor(
38+
ITokenMessenger tokenMessenger
39+
) {
40+
i_cctpTransmitter = IMessageTransmitter(tokenMessenger.localMessageTransmitter());
41+
}
42+
43+
/// @notice Receives a message from the `IMessageTransmitter` contract and validates it.
44+
/// @dev Can only be called by an allowed caller to process incoming messages.
45+
/// @param message The payload of the message being received.
46+
/// @param attestation The cryptographic proof validating the message.
47+
/// @return success A boolean indicating if the message was successfully processed.
48+
function receiveMessage(bytes calldata message, bytes calldata attestation) external returns (bool success) {
49+
if (!s_allowedCallers.contains(msg.sender)) {
50+
revert Unauthorized(msg.sender);
51+
}
52+
return i_cctpTransmitter.receiveMessage(message, attestation);
53+
}
54+
55+
/// @notice Configures the allowed callers for the `receiveMessage` function.
56+
/// @param configArgs An array of `AllowedCallerConfigArgs` structs.
57+
function configureAllowedCallers(
58+
AllowedCallerConfigArgs[] calldata configArgs
59+
) external onlyOwner {
60+
for (uint256 i = 0; i < configArgs.length; ++i) {
61+
if (configArgs[i].allowed) {
62+
if (s_allowedCallers.add(configArgs[i].caller)) {
63+
emit AllowedCallerAdded(configArgs[i].caller);
64+
}
65+
} else {
66+
if (s_allowedCallers.remove(configArgs[i].caller)) {
67+
emit AllowedCallerRemoved(configArgs[i].caller);
68+
}
69+
}
70+
}
71+
}
72+
73+
/// @notice Checks if the caller is allowed to call the `receiveMessage` function.
74+
/// @param caller The address to check.
75+
/// @return allowed A boolean indicating if the caller is allowed.
76+
function isAllowedCaller(
77+
address caller
78+
) external view returns (bool allowed) {
79+
return s_allowedCallers.contains(caller);
80+
}
81+
82+
/// @notice Returns an array of all allowed callers.
83+
/// @return allowedCallers An array of allowed caller addresses.
84+
function getAllowedCallers() external view returns (address[] memory allowedCallers) {
85+
return s_allowedCallers.values();
86+
}
87+
}

contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {ITokenMessenger} from "../USDC/ITokenMessenger.sol";
66

77
import {Pool} from "../../libraries/Pool.sol";
88
import {TokenPool} from "../TokenPool.sol";
9+
import {CCTPMessageTransmitterProxy} from "../USDC/CCTPMessageTransmitterProxy.sol";
910
import {USDCTokenPool} from "../USDC/USDCTokenPool.sol";
1011
import {USDCBridgeMigrator} from "./USDCBridgeMigrator.sol";
1112

@@ -44,11 +45,15 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator {
4445

4546
constructor(
4647
ITokenMessenger tokenMessenger,
48+
CCTPMessageTransmitterProxy cctpMessageTransmitterProxy,
4749
IERC20 token,
4850
address[] memory allowlist,
4951
address rmnProxy,
5052
address router
51-
) USDCTokenPool(tokenMessenger, token, allowlist, rmnProxy, router) USDCBridgeMigrator(address(token)) {}
53+
)
54+
USDCTokenPool(tokenMessenger, cctpMessageTransmitterProxy, token, allowlist, rmnProxy, router)
55+
USDCBridgeMigrator(address(token))
56+
{}
5257

5358
// ================================================================
5459
// │ Incoming/Outgoing Mechanisms |

contracts/src/v0.8/ccip/pools/USDC/USDCTokenPool.sol

+16-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {ITokenMessenger} from "./ITokenMessenger.sol";
77

88
import {Pool} from "../../libraries/Pool.sol";
99
import {TokenPool} from "../TokenPool.sol";
10+
import {CCTPMessageTransmitterProxy} from "./CCTPMessageTransmitterProxy.sol";
1011

1112
import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
1213
import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
@@ -29,6 +30,7 @@ contract USDCTokenPool is TokenPool, ITypeAndVersion {
2930
error InvalidSourceDomain(uint32 expected, uint32 got);
3031
error InvalidDestinationDomain(uint32 expected, uint32 got);
3132
error InvalidReceiver(bytes receiver);
33+
error InvalidTransmitterInProxy();
3234

3335
// This data is supplied from offchain and contains everything needed
3436
// to receive the USDC tokens.
@@ -50,19 +52,22 @@ contract USDCTokenPool is TokenPool, ITypeAndVersion {
5052
uint32 sourceDomain;
5153
}
5254

53-
string public constant override typeAndVersion = "USDCTokenPool 1.5.1";
55+
string public constant override typeAndVersion = "USDCTokenPool 1.6.1-dev";
5456

5557
// We restrict to the first version. New pool may be required for subsequent versions.
5658
uint32 public constant SUPPORTED_USDC_VERSION = 0;
5759

5860
// The local USDC config
5961
ITokenMessenger public immutable i_tokenMessenger;
60-
IMessageTransmitter public immutable i_messageTransmitter;
62+
CCTPMessageTransmitterProxy public immutable i_messageTransmitterProxy;
6163
uint32 public immutable i_localDomainIdentifier;
6264

6365
/// A domain is a USDC representation of a destination chain.
6466
/// @dev Zero is a valid domain identifier.
6567
/// @dev The address to mint on the destination chain is the corresponding USDC pool.
68+
/// @dev The allowedCaller represents the contract authorized to call receiveMessage on the destination CCTP message transmitter.
69+
/// For dest pool version 1.6.1, this is the MessageTransmitterProxy of the destination chain.
70+
/// For dest pool version 1.5.1, this is the destination chain's token pool.
6671
struct Domain {
6772
bytes32 allowedCaller; // Address allowed to mint on the domain
6873
uint32 domainIdentifier; // ─╮ Unique domain ID
@@ -74,6 +79,7 @@ contract USDCTokenPool is TokenPool, ITypeAndVersion {
7479

7580
constructor(
7681
ITokenMessenger tokenMessenger,
82+
CCTPMessageTransmitterProxy cctpMessageTransmitterProxy,
7783
IERC20 token,
7884
address[] memory allowlist,
7985
address rmnProxy,
@@ -85,17 +91,20 @@ contract USDCTokenPool is TokenPool, ITypeAndVersion {
8591
if (transmitterVersion != SUPPORTED_USDC_VERSION) revert InvalidMessageVersion(transmitterVersion);
8692
uint32 tokenMessengerVersion = tokenMessenger.messageBodyVersion();
8793
if (tokenMessengerVersion != SUPPORTED_USDC_VERSION) revert InvalidTokenMessengerVersion(tokenMessengerVersion);
94+
if (cctpMessageTransmitterProxy.i_cctpTransmitter() != transmitter) revert InvalidTransmitterInProxy();
8895

8996
i_tokenMessenger = tokenMessenger;
90-
i_messageTransmitter = transmitter;
97+
i_messageTransmitterProxy = cctpMessageTransmitterProxy;
9198
i_localDomainIdentifier = transmitter.localDomain();
9299
i_token.safeIncreaseAllowance(address(i_tokenMessenger), type(uint256).max);
93100
emit ConfigSet(address(tokenMessenger));
94101
}
95102

96-
/// @notice Burn the token in the pool
97-
/// @dev emits ITokenMessenger.DepositForBurn
98-
/// @dev Assumes caller has validated destinationReceiver
103+
/// @notice Burn tokens from the pool to initiate cross-chain transfer.
104+
/// @notice Outgoing messages (burn operations) are routed via `i_tokenMessenger.depositForBurnWithCaller`.
105+
/// The allowedCaller is preconfigured per destination domain and token pool version refer Domain struct.
106+
/// @dev Emits ITokenMessenger.DepositForBurn event.
107+
/// @dev Assumes caller has validated the destinationReceiver.
99108
function lockOrBurn(
100109
Pool.LockOrBurnInV1 calldata lockOrBurnIn
101110
) public virtual override returns (Pool.LockOrBurnOutV1 memory) {
@@ -146,7 +155,7 @@ contract USDCTokenPool is TokenPool, ITypeAndVersion {
146155

147156
_validateMessage(msgAndAttestation.message, sourceTokenDataPayload);
148157

149-
if (!i_messageTransmitter.receiveMessage(msgAndAttestation.message, msgAndAttestation.attestation)) {
158+
if (!i_messageTransmitterProxy.receiveMessage(msgAndAttestation.message, msgAndAttestation.attestation)) {
150159
revert UnlockingUSDCFailed();
151160
}
152161

contracts/src/v0.8/ccip/test/helpers/USDCTokenPoolHelper.sol

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
pragma solidity ^0.8.24;
33

44
import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol";
5-
65
import {ITokenMessenger} from "../../pools/USDC/ITokenMessenger.sol";
6+
7+
import {CCTPMessageTransmitterProxy} from "../../pools/USDC/CCTPMessageTransmitterProxy.sol";
78
import {USDCTokenPool} from "../../pools/USDC/USDCTokenPool.sol";
89

910
contract USDCTokenPoolHelper is USDCTokenPool {
1011
constructor(
1112
ITokenMessenger tokenMessenger,
13+
CCTPMessageTransmitterProxy messageTransmitterProxy,
1214
IBurnMintERC20 token,
1315
address[] memory allowlist,
1416
address rmnProxy,
1517
address router
16-
) USDCTokenPool(tokenMessenger, token, allowlist, rmnProxy, router) {}
18+
) USDCTokenPool(tokenMessenger, messageTransmitterProxy, token, allowlist, rmnProxy, router) {}
1719

1820
function validateMessage(bytes memory usdcMessage, SourceTokenDataPayload memory sourceTokenData) external view {
1921
return _validateMessage(usdcMessage, sourceTokenData);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.24;
3+
4+
import {Ownable2Step} from "../../../../../shared/access/Ownable2Step.sol";
5+
import {CCTPMessageTransmitterProxy} from "../../../../pools/USDC/CCTPMessageTransmitterProxy.sol";
6+
import {CCTPMessageTransmitterProxySetup} from "./CCTPMessageTransmitterProxySetup.t.sol";
7+
8+
contract CCTPMessageTransmitterProxy_configureAllowedCallers is CCTPMessageTransmitterProxySetup {
9+
function test_configureAllowedCallers() public {
10+
CCTPMessageTransmitterProxy.AllowedCallerConfigArgs[] memory allowedCallerParams =
11+
new CCTPMessageTransmitterProxy.AllowedCallerConfigArgs[](2);
12+
allowedCallerParams[0] =
13+
CCTPMessageTransmitterProxy.AllowedCallerConfigArgs({caller: s_usdcTokenPool, allowed: true});
14+
allowedCallerParams[1] = CCTPMessageTransmitterProxy.AllowedCallerConfigArgs({caller: msg.sender, allowed: true});
15+
s_cctpMessageTransmitterProxy.configureAllowedCallers(allowedCallerParams);
16+
assertTrue(s_cctpMessageTransmitterProxy.isAllowedCaller(s_usdcTokenPool));
17+
18+
// Remove the allowed caller
19+
allowedCallerParams = new CCTPMessageTransmitterProxy.AllowedCallerConfigArgs[](1);
20+
allowedCallerParams[0] =
21+
CCTPMessageTransmitterProxy.AllowedCallerConfigArgs({caller: s_usdcTokenPool, allowed: false});
22+
s_cctpMessageTransmitterProxy.configureAllowedCallers(allowedCallerParams);
23+
assertFalse(s_cctpMessageTransmitterProxy.isAllowedCaller(s_usdcTokenPool));
24+
25+
address[] memory allowedCallers = s_cctpMessageTransmitterProxy.getAllowedCallers();
26+
assertEq(allowedCallers.length, 1);
27+
assertEq(allowedCallers[0], msg.sender);
28+
}
29+
30+
// Revert cases
31+
function test_configureAllowedCallers_RevertWhen_NotOwner() public {
32+
changePrank(makeAddr("RANDOM"));
33+
CCTPMessageTransmitterProxy.AllowedCallerConfigArgs[] memory allowedCallerParams =
34+
new CCTPMessageTransmitterProxy.AllowedCallerConfigArgs[](1);
35+
allowedCallerParams[0] =
36+
CCTPMessageTransmitterProxy.AllowedCallerConfigArgs({caller: s_usdcTokenPool, allowed: true});
37+
vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector);
38+
s_cctpMessageTransmitterProxy.configureAllowedCallers(allowedCallerParams);
39+
}
40+
}

0 commit comments

Comments
 (0)