Skip to content
Open
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
28 changes: 20 additions & 8 deletions contracts/chain-adapters/OP_Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,43 @@ contract OP_Adapter is CrossDomainEnabled, AdapterInterface, CircleCCTPAdapter {
IL1StandardBridge public immutable L1_STANDARD_BRIDGE;
IOpUSDCBridgeAdapter public immutable L1_OP_USDC_BRIDGE;

error InvalidBridgeConfig();

/**
* @notice Constructs new Adapter.
* @param _l1Weth WETH address on L1.
* @param _l1Usdc USDC address on L1.
* @param _crossDomainMessenger XDomainMessenger Destination chain system contract.
* @param _l1StandardBridge Standard bridge contract.
* @param _l1Usdc USDC address on L1.
* @param _l1USDCBridge OP USDC bridge contract.
* @param _cctpTokenMessenger CCTP token messenger contract.
* @param _recipientCircleDomainId Circle domain ID of the destination chain.
*/
constructor(
WETH9Interface _l1Weth,
IERC20 _l1Usdc,
address _crossDomainMessenger,
IL1StandardBridge _l1StandardBridge,
IOpUSDCBridgeAdapter _l1USDCBridge
IOpUSDCBridgeAdapter _l1USDCBridge,
ITokenMessenger _cctpTokenMessenger,
uint32 _recipientCircleDomainId
)
CrossDomainEnabled(_crossDomainMessenger)
CircleCCTPAdapter(
_l1Usdc,
// Hardcode cctp messenger to 0x0 to disable CCTP bridging.
ITokenMessenger(address(0)),
CircleDomainIds.UNINITIALIZED
)
CircleCCTPAdapter(_l1Usdc, _cctpTokenMessenger, _recipientCircleDomainId)
{
L1_WETH = _l1Weth;
L1_STANDARD_BRIDGE = _l1StandardBridge;
L1_OP_USDC_BRIDGE = _l1USDCBridge;

address zero = address(0);
if (address(_l1Usdc) != zero) {
bool opUSDCBridgeDisabled = address(_l1USDCBridge) == zero;
bool cctpUSDCBridgeDisabled = address(_cctpTokenMessenger) == zero;
// Bridged and Native USDC are mutually exclusive.
if (opUSDCBridgeDisabled == cctpUSDCBridgeDisabled) {
revert InvalidBridgeConfig();
}
}
}

/**
Expand Down
95 changes: 95 additions & 0 deletions test/evm/foundry/local/OP_Adapter.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { Test } from "forge-std/Test.sol";

import { ERC20, IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/ERC20.sol";
import { IL1StandardBridge } from "@eth-optimism/contracts/L1/messaging/IL1StandardBridge.sol";
import { IOpUSDCBridgeAdapter } from "../../../../contracts/external/interfaces/IOpUSDCBridgeAdapter.sol";
import { ITokenMessenger } from "../../../../contracts/external/interfaces/CCTPInterfaces.sol";

import { OP_Adapter } from "../../../../contracts/chain-adapters/OP_Adapter.sol";
import { WETH9Interface } from "../../../../contracts/external/interfaces/WETH9Interface.sol";
import { WETH9 } from "../../../../contracts/external/WETH9.sol";

contract OP_AdapterTest is Test {
ERC20 l1Usdc;
WETH9 l1Weth;

IL1StandardBridge standardBridge;
IOpUSDCBridgeAdapter opUSDCBridge;
ITokenMessenger cctpMessenger;

uint32 constant RECIPIENT_CIRCLE_DOMAIN_ID = 1;

function setUp() public {
l1Usdc = new ERC20("l1Usdc", "l1Usdc");
l1Weth = new WETH9();

standardBridge = IL1StandardBridge(makeAddr("standardBridge"));
opUSDCBridge = IOpUSDCBridgeAdapter(makeAddr("opUSDCBridge"));
cctpMessenger = ITokenMessenger(makeAddr("cctpMessenger"));
}

function testUSDCNotSet() public {
new OP_Adapter(
WETH9Interface(address(l1Weth)),
IERC20(address(0)),
address(0),
standardBridge,
IOpUSDCBridgeAdapter(address(0)),
ITokenMessenger(address(0)),
RECIPIENT_CIRCLE_DOMAIN_ID
);
}

function testL1UsdcBridgeSet() public {
new OP_Adapter(
WETH9Interface(address(l1Weth)),
IERC20(address(l1Usdc)),
address(0),
standardBridge,
opUSDCBridge,
ITokenMessenger(address(0)),
RECIPIENT_CIRCLE_DOMAIN_ID
);
}

function testCctpMessengerSet() public {
new OP_Adapter(
WETH9Interface(address(l1Weth)),
IERC20(address(0)),
address(0),
standardBridge,
IOpUSDCBridgeAdapter(address(0)),
cctpMessenger,
RECIPIENT_CIRCLE_DOMAIN_ID
);
}

function testNeitherSet() public {
vm.expectRevert(OP_Adapter.InvalidBridgeConfig.selector);
new OP_Adapter(
WETH9Interface(address(l1Weth)),
IERC20(address(l1Usdc)),
address(0),
standardBridge,
IOpUSDCBridgeAdapter(address(0)),
ITokenMessenger(address(0)),
RECIPIENT_CIRCLE_DOMAIN_ID
);
}

function testBothSet() public {
vm.expectRevert(OP_Adapter.InvalidBridgeConfig.selector);
new OP_Adapter(
WETH9Interface(address(l1Weth)),
IERC20(address(l1Usdc)),
address(0),
standardBridge,
opUSDCBridge,
cctpMessenger,
RECIPIENT_CIRCLE_DOMAIN_ID
);
}
}
6 changes: 5 additions & 1 deletion test/evm/hardhat/chain-adapters/OP_Adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import {
toWei,
getContractFactory,
seedWallet,
toBN,
} from "../../../../utils/utils";
import { hubPoolFixture, enableTokensForLP } from "../fixtures/HubPool.Fixture";
import { constructSingleChainTree } from "../MerkleLib.utils";
import { ZERO_ADDRESS } from "@uma/common";

let hubPool: Contract, adapter: Contract, weth: Contract, usdc: Contract, mockSpoke: Contract, timer: Contract;
let l2Weth: string, l2Usdc: string;
Expand Down Expand Up @@ -63,7 +65,9 @@ describe("OP Adapter", function () {
usdc.address,
l1CrossDomainMessenger.address,
l1StandardBridge.address,
opUSDCBridge.address
opUSDCBridge.address,
ZERO_ADDRESS,
toBN("322")
);
// Seed the HubPool some funds so it can send L1->L2 messages.
await hubPool.connect(liquidityProvider).loadEthForL2Calls({ value: toWei("1") });
Expand Down
Loading