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

fix: admin cant interact with usdc implementation #45

Merged
merged 2 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"lint:sol-tests": "solhint -c .solhint.tests.json 'test/**/*.sol'",
"prepare": "husky install",
"test": "forge test -vvv",
"test:integration": "forge test --match-contract Integration -vvv",
"test:integration": "forge test --match-contract Integration -vvvv",
"test:unit": "forge test --match-contract Unit -vvv",
"test:unit:deep": "FOUNDRY_FUZZ_RUNS=5000 yarn test:unit"
},
Expand Down
9 changes: 7 additions & 2 deletions src/contracts/L2OpUSDCBridgeAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.25;

import {OpUSDCBridgeAdapter} from 'contracts/universal/OpUSDCBridgeAdapter.sol';
import {FallbackProxyAdmin} from 'contracts/utils/FallbackProxyAdmin.sol';
import {IL2OpUSDCBridgeAdapter} from 'interfaces/IL2OpUSDCBridgeAdapter.sol';
import {ICrossDomainMessenger} from 'interfaces/external/ICrossDomainMessenger.sol';
import {IUSDC} from 'interfaces/external/IUSDC.sol';
Expand All @@ -13,6 +14,8 @@ contract L2OpUSDCBridgeAdapter is IL2OpUSDCBridgeAdapter, OpUSDCBridgeAdapter {
bytes4 internal constant _TRANSFER_OWNERSHIP_SELECTOR = 0xf2fde38b;
bytes4 internal constant _CHANGE_ADMIN_SELECTOR = 0x8f283970;

FallbackProxyAdmin public immutable FALLBACK_PROXY_ADMIN;

/// @inheritdoc IL2OpUSDCBridgeAdapter
bool public isMessagingDisabled;

Expand All @@ -39,7 +42,9 @@ contract L2OpUSDCBridgeAdapter is IL2OpUSDCBridgeAdapter, OpUSDCBridgeAdapter {
address _messenger,
address _linkedAdapter,
address _owner
) OpUSDCBridgeAdapter(_usdc, _messenger, _linkedAdapter, _owner) {}
) OpUSDCBridgeAdapter(_usdc, _messenger, _linkedAdapter, _owner) {
FALLBACK_PROXY_ADMIN = new FallbackProxyAdmin(_usdc);
}
/* solhint-enable no-unused-vars */

/*///////////////////////////////////////////////////////////////
Expand All @@ -58,7 +63,7 @@ contract L2OpUSDCBridgeAdapter is IL2OpUSDCBridgeAdapter, OpUSDCBridgeAdapter {
IUSDC(USDC).transferOwnership(_newOwner);

//Transfer proxy admin ownership to circle
IUSDC(USDC).changeAdmin(_newOwner);
FALLBACK_PROXY_ADMIN.changeAdmin(_newOwner);

uint256 _burnAmount = IUSDC(USDC).totalSupply();

Expand Down
4 changes: 3 additions & 1 deletion src/contracts/L2OpUSDCFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ contract L2OpUSDCFactory is IL2OpUSDCFactory {
revert IL2OpUSDCFactory_DeploymentsFailed();
}

// Deploy the FallbackProxyAdmin internally in the L2 Adapter to keep it unique
address _fallbackProxyAdmin = address(L2OpUSDCBridgeAdapter(_l2Adapter).FALLBACK_PROXY_ADMIN());
// Change the USDC admin so the init txs can be executed over the proxy from this contract
IUSDC(_usdcProxy).changeAdmin(_l2Adapter);
IUSDC(_usdcProxy).changeAdmin(_fallbackProxyAdmin);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here


// Execute the USDC initialization transactions
_executeInitTxs(_usdcImplementation, _usdcInitTxs, _usdcInitTxs.length);
Expand Down
27 changes: 27 additions & 0 deletions src/contracts/utils/FallbackProxyAdmin.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {IUSDC} from 'interfaces/external/IUSDC.sol';

contract FallbackProxyAdmin is Ownable {
/// @notice USDC address
address public immutable USDC;

/**
* @notice Construct the FallbackProxyAdmin contract
*/
constructor(address _usdc) Ownable(msg.sender) {
USDC = _usdc;
}

/**
* @notice Changes the admin of the USDC proxy
* @param newAdmin Address to transfer proxy administration to
* @dev Owner should always be the L2 Adapter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here

* @dev USDC admin cant interact proxy with implementation so we use this contract as the middleman
*/
function changeAdmin(address newAdmin) external onlyOwner {
IUSDC(USDC).changeAdmin(newAdmin);
}
}
14 changes: 14 additions & 0 deletions src/interfaces/external/IUSDC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,23 @@ interface IUSDC is IERC20 {
*/
function changeAdmin(address newAdmin) external;

/**
* @dev Function to add/update a new minter
* @param _minter The address of the minter
* @param _minterAllowedAmount The minting amount allowed for the minter
* @return _result True if the operation was successful.
*/
function configureMinter(address _minter, uint256 _minterAllowedAmount) external returns (bool _result);
0xDiscotech marked this conversation as resolved.
Show resolved Hide resolved

/**
* @notice Returns the current implementation address
* @return _implementation Address of the current implementation
*/
function implementation() external view returns (address _implementation);

/**
* @notice Returns the current master minter address
* @return _masterMinter Address of the current master minter
*/
function masterMinter() external view returns (address _masterMinter);
}
15 changes: 11 additions & 4 deletions test/integration/IntegrationBase.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {L1OpUSDCBridgeAdapter} from 'contracts/L1OpUSDCBridgeAdapter.sol';
import {IUSDC} from 'interfaces/external/IUSDC.sol';
import {AddressAliasHelper} from 'test/utils/AddressAliasHelper.sol';
import {USDC_IMPLEMENTATION_CREATION_CODE} from 'test/utils/USDCImplementationCreationCode.sol';
import {IMockCrossDomainMessenger} from 'test/utils/interfaces/IMockCrossDomainMessenger.sol';

import {L1OpUSDCBridgeAdapter} from 'contracts/L1OpUSDCBridgeAdapter.sol';

import {IL1OpUSDCFactory, L1OpUSDCFactory} from 'contracts/L1OpUSDCFactory.sol';

import {L2OpUSDCBridgeAdapter} from 'contracts/L2OpUSDCBridgeAdapter.sol';
Expand All @@ -19,7 +19,7 @@ contract IntegrationBase is Test {
// Constants
uint256 internal constant _MAINNET_FORK_BLOCK = 20_076_176;
uint256 internal constant _OPTIMISM_FORK_BLOCK = 121_300_856;
address public constant MAINNET_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
IUSDC public constant MAINNET_USDC = IUSDC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
address public constant MAINNET_USDC_IMPLEMENTATION = 0xa2327a938Febf5FEC13baCFb16Ae10EcBc4cbDCF;
address public constant L2_CREATE2_DEPLOYER = 0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2;
IMockCrossDomainMessenger public constant L2_MESSENGER =
Expand All @@ -44,6 +44,7 @@ contract IntegrationBase is Test {
L1OpUSDCBridgeAdapter public l1Adapter;
L1OpUSDCFactory public factory;
L2OpUSDCBridgeAdapter public l2Adapter;
IUSDC public bridgedUSDC;

function setUp() public virtual {
mainnet = vm.createFork(vm.rpcUrl('mainnet'), _MAINNET_FORK_BLOCK);
Expand All @@ -62,7 +63,7 @@ contract IntegrationBase is Test {
address(1)
);

factory = new L1OpUSDCFactory(MAINNET_USDC);
factory = new L1OpUSDCFactory(address(MAINNET_USDC));

usdcInitTxns[0] = initialize;
usdcInitTxns[1] = USDCInitTxs.INITIALIZEV2;
Expand All @@ -86,6 +87,8 @@ contract IntegrationBase is Test {
// Make foundry know these two address exist on both forks
vm.makePersistent(address(l1Adapter));
vm.makePersistent(address(l2Adapter));
vm.makePersistent(address(bridgedUSDC));
vm.makePersistent(address(l2Adapter.FALLBACK_PROXY_ADMIN()));
}

function _relayL2Deployments(
Expand Down Expand Up @@ -128,6 +131,10 @@ contract IntegrationBase is Test {
vm.stopPrank();

l2Adapter = L2OpUSDCBridgeAdapter(_l2Adapter);
bridgedUSDC = IUSDC(l2Adapter.USDC());

vm.prank(bridgedUSDC.masterMinter());
bridgedUSDC.configureMinter(address(l2Adapter), type(uint256).max);
}
}

Expand Down
44 changes: 44 additions & 0 deletions test/integration/L1OpUSDCBridgeAdapter.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {IntegrationBase} from './IntegrationBase.sol';
import {AddressAliasHelper} from 'test/utils/AddressAliasHelper.sol';

contract Integration_Bridging is IntegrationBase {
/**
* @notice Test the bridging process from L1 -> L2
*/
function test_bridgeFromL1() public {
vm.selectFork(mainnet);

uint256 _amount = 1e18;
uint32 _minGasLimit = 1_000_000;

// Deal doesnt work with proxies
vm.startPrank(MAINNET_USDC.masterMinter());
MAINNET_USDC.configureMinter(MAINNET_USDC.masterMinter(), type(uint256).max);
MAINNET_USDC.mint(_user, _amount);
vm.stopPrank();

vm.startPrank(_user);
MAINNET_USDC.approve(address(l1Adapter), _amount);
l1Adapter.sendMessage(_user, _amount, _minGasLimit);
vm.stopPrank();

assertEq(MAINNET_USDC.balanceOf(_user), 0);
assertEq(MAINNET_USDC.balanceOf(address(l1Adapter)), _amount);

vm.selectFork(optimism);
uint256 _messageNonce = L2_MESSENGER.messageNonce();

vm.startPrank(AddressAliasHelper.applyL1ToL2Alias(address(OPTIMISM_L1_MESSENGER)));
L2_MESSENGER.relayMessage(
_messageNonce + 1,
address(l1Adapter),
address(l2Adapter),
0,
1_000_000,
abi.encodeWithSignature('receiveMessage(address,uint256)', _user, _amount)
);
vm.stopPrank();

assertEq(bridgedUSDC.balanceOf(address(_user)), _amount);
}
}
36 changes: 36 additions & 0 deletions test/unit/FallbackProxyAdmin.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
pragma solidity ^0.8.25;

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

import {FallbackProxyAdmin} from 'contracts/utils/FallbackProxyAdmin.sol';
import {Helpers} from 'test/utils/Helpers.sol';

abstract contract Base is Helpers {
address internal _usdc = makeAddr('usdc');
address internal _owner = makeAddr('owner');

FallbackProxyAdmin public admin;

function setUp() public virtual {
vm.prank(_owner);
admin = new FallbackProxyAdmin(_usdc);
}
}

contract FallbackProxyAdmin_Unit_ChangeAdmin is Base {
function test_revertIfNotOwner(address _newOwner) public {
vm.assume(_newOwner != _owner);

vm.prank(_newOwner);
vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, _newOwner));
admin.changeAdmin(_newOwner);
}

function test_changeAdmin(address _newOwner) public {
vm.assume(_newOwner != _owner);

_mockAndExpect(address(_usdc), abi.encodeWithSignature('changeAdmin(address)', _newOwner), abi.encode());
vm.prank(_owner);
admin.changeAdmin(_newOwner);
}
}
3 changes: 2 additions & 1 deletion test/unit/L2OpUSDCFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,15 @@ contract L2OpUSDCFactory_Unit_Deploy is Base {
// Get the adapter address
uint256 _adapterDeploymentNonce = vm.getNonce(address(factory)) + 2;
address _l2Adapter = _precalculateCreateAddress(address(factory), _adapterDeploymentNonce);
address _fallbackProxyAdmin = _precalculateCreateAddress(address(_l2Adapter), 1);

// Mock the call over `xDomainMessageSender` to return the L1 factory address
vm.mockCall(
_l2Messenger, abi.encodeWithSelector(ICrossDomainMessenger.xDomainMessageSender.selector), abi.encode(_l1Factory)
);

// Expect the call over 'changeAdmin' function
vm.expectCall(_usdcProxy, abi.encodeWithSelector(IUSDC.changeAdmin.selector, address(_l2Adapter)));
vm.expectCall(_usdcProxy, abi.encodeWithSelector(IUSDC.changeAdmin.selector, address(_fallbackProxyAdmin)));

// Execute
vm.prank(_l2Messenger);
Expand Down
Loading