55pragma solidity ^ 0.8.19 ;
66
77import "./SpokePool.sol " ;
8+ import "./libraries/CircleCCTPAdapter.sol " ;
89import { IMessageService, ITokenBridge, IUSDCBridge } from "./external/interfaces/LineaInterfaces.sol " ;
910import "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
1011import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
@@ -13,7 +14,7 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
1314 * @notice Linea specific SpokePool.
1415 * @custom:security-contact bugs@across.to
1516 */
16- contract Linea_SpokePool is SpokePool {
17+ contract Linea_SpokePool is SpokePool , CircleCCTPAdapter {
1718 using SafeERC20 for IERC20 ;
1819
1920 /**
@@ -29,14 +30,13 @@ contract Linea_SpokePool is SpokePool {
2930 /**
3031 * @notice Address of Linea's USDC Bridge contract on L2.
3132 */
32- IUSDCBridge public l2UsdcBridge ;
33+ IUSDCBridge private DEPRECATED_l2UsdcBridge ;
3334
3435 /**************************************
3536 * EVENTS *
3637 **************************************/
3738 event SetL2TokenBridge (address indexed newTokenBridge , address oldTokenBridge );
3839 event SetL2MessageService (address indexed newMessageService , address oldMessageService );
39- event SetL2UsdcBridge (address indexed newUsdcBridge , address oldUsdcBridge );
4040
4141 /**
4242 * @notice Construct Linea-specific SpokePool.
@@ -50,16 +50,20 @@ contract Linea_SpokePool is SpokePool {
5050 constructor (
5151 address _wrappedNativeTokenAddress ,
5252 uint32 _depositQuoteTimeBuffer ,
53- uint32 _fillDeadlineBuffer
54- ) SpokePool (_wrappedNativeTokenAddress, _depositQuoteTimeBuffer, _fillDeadlineBuffer) {} // solhint-disable-line no-empty-blocks
53+ uint32 _fillDeadlineBuffer ,
54+ IERC20 _l2Usdc ,
55+ ITokenMessenger _cctpTokenMessenger
56+ )
57+ SpokePool (_wrappedNativeTokenAddress, _depositQuoteTimeBuffer, _fillDeadlineBuffer)
58+ CircleCCTPAdapter (_l2Usdc, _cctpTokenMessenger, CircleDomainIds.Ethereum)
59+ {} // solhint-disable-line no-empty-blocks
5560
5661 /**
5762 * @notice Initialize Linea-specific SpokePool.
5863 * @param _initialDepositId Starting deposit ID. Set to 0 unless this is a re-deployment in order to mitigate
5964 * relay hash collisions.
6065 * @param _l2MessageService Address of Canonical Message Service. Can be reset by admin.
6166 * @param _l2TokenBridge Address of Canonical Token Bridge. Can be reset by admin.
62- * @param _l2UsdcBridge Address of USDC Bridge. Can be reset by admin.
6367 * @param _crossDomainAdmin Cross domain admin to set. Can be changed by admin.
6468 * @param _withdrawalRecipient Address which receives token withdrawals. Can be changed by admin. For Spoke Pools on L2, this will
6569 * likely be the hub pool.
@@ -68,14 +72,12 @@ contract Linea_SpokePool is SpokePool {
6872 uint32 _initialDepositId ,
6973 IMessageService _l2MessageService ,
7074 ITokenBridge _l2TokenBridge ,
71- IUSDCBridge _l2UsdcBridge ,
7275 address _crossDomainAdmin ,
7376 address _withdrawalRecipient
7477 ) public initializer {
7578 __SpokePool_init (_initialDepositId, _crossDomainAdmin, _withdrawalRecipient);
7679 _setL2TokenBridge (_l2TokenBridge);
7780 _setL2MessageService (_l2MessageService);
78- _setL2UsdcBridge (_l2UsdcBridge);
7981 }
8082
8183 /**
@@ -106,14 +108,6 @@ contract Linea_SpokePool is SpokePool {
106108 _setL2MessageService (_l2MessageService);
107109 }
108110
109- /**
110- * @notice Change L2 USDC bridge address. Callable only by admin.
111- * @param _l2UsdcBridge New address of L2 USDC bridge.
112- */
113- function setL2UsdcBridge (IUSDCBridge _l2UsdcBridge ) public onlyAdmin nonReentrant {
114- _setL2UsdcBridge (_l2UsdcBridge);
115- }
116-
117111 /**************************************
118112 * INTERNAL FUNCTIONS *
119113 **************************************/
@@ -139,27 +133,32 @@ contract Linea_SpokePool is SpokePool {
139133 function _bridgeTokensToHubPool (uint256 amountToReturn , address l2TokenAddress ) internal override {
140134 // Linea's L2 Canonical Message Service, requires a minimum fee to be set.
141135 uint256 minFee = minimumFeeInWei ();
142- // We require that the caller pass in the fees as msg.value instead of pulling ETH out of this contract's balance.
143- // Using the contract's balance would require a separate accounting system to keep LP funds separated from system funds
144- // used to pay for L2->L1 messages.
145- require (msg .value == minFee, "MESSAGE_FEE_MISMATCH " );
146136
147137 // SpokePool is expected to receive ETH from the L1 HubPool, then we need to first unwrap it to ETH and then
148138 // send ETH directly via the Canonical Message Service.
149139 if (l2TokenAddress == address (wrappedNativeToken)) {
140+ // We require that the caller pass in the fees as msg.value instead of pulling ETH out of this contract's balance.
141+ // Using the contract's balance would require a separate accounting system to keep LP funds separated from system funds
142+ // used to pay for L2->L1 messages.
143+ require (msg .value == minFee, "MESSAGE_FEE_MISMATCH " );
144+
150145 // msg.value is added here because the entire native balance (including msg.value) is auto-wrapped
151146 // before the execution of any wrapped token refund leaf. So it must be unwrapped before being sent as a
152147 // fee to the l2MessageService.
153148 WETH9Interface (l2TokenAddress).withdraw (amountToReturn + msg .value ); // Unwrap into ETH.
154149 l2MessageService.sendMessage { value: amountToReturn + msg .value }(withdrawalRecipient, msg .value , "" );
155150 }
156151 // If the l1Token is USDC, then we need sent it via the USDC Bridge.
157- else if (l2TokenAddress == l2UsdcBridge.usdc ()) {
158- IERC20 (l2TokenAddress).safeIncreaseAllowance (address (l2UsdcBridge), amountToReturn);
159- l2UsdcBridge.depositTo { value: msg .value }(amountToReturn, withdrawalRecipient);
152+ else if (l2TokenAddress == address (usdcToken) && _isCCTPEnabled ()) {
153+ _transferUsdc (withdrawalRecipient, amountToReturn);
160154 }
161155 // For other tokens, we can use the Canonical Token Bridge.
162156 else {
157+ // We require that the caller pass in the fees as msg.value instead of pulling ETH out of this contract's balance.
158+ // Using the contract's balance would require a separate accounting system to keep LP funds separated from system funds
159+ // used to pay for L2->L1 messages.
160+ require (msg .value == minFee, "MESSAGE_FEE_MISMATCH " );
161+
163162 IERC20 (l2TokenAddress).safeIncreaseAllowance (address (l2TokenBridge), amountToReturn);
164163 l2TokenBridge.bridgeToken { value: msg .value }(l2TokenAddress, amountToReturn, withdrawalRecipient);
165164 }
@@ -178,12 +177,6 @@ contract Linea_SpokePool is SpokePool {
178177 emit SetL2TokenBridge (address (_l2TokenBridge), oldTokenBridge);
179178 }
180179
181- function _setL2UsdcBridge (IUSDCBridge _l2UsdcBridge ) internal {
182- address oldUsdcBridge = address (l2UsdcBridge);
183- l2UsdcBridge = _l2UsdcBridge;
184- emit SetL2UsdcBridge (address (_l2UsdcBridge), oldUsdcBridge);
185- }
186-
187180 function _setL2MessageService (IMessageService _l2MessageService ) internal {
188181 address oldMessageService = address (l2MessageService);
189182 l2MessageService = _l2MessageService;
0 commit comments