Skip to content

Commit 954528a

Browse files
authored
fix: address issue with weth on boba (#159)
* fix: address issue with weth on boba Signed-off-by: chrismaree <christopher.maree@gmail.com> * wip Signed-off-by: chrismaree <christopher.maree@gmail.com> * Restore 003_deploy_optimism_spokepool.ts * Restore 013_deploy_boba_spokepool.ts * Restore 013_deploy_boba_spokepool.ts * wip Signed-off-by: chrismaree <christopher.maree@gmail.com> * wip Signed-off-by: chrismaree <christopher.maree@gmail.com> * wip Signed-off-by: chrismaree <christopher.maree@gmail.com>
1 parent 3790f7f commit 954528a

File tree

3 files changed

+185
-143
lines changed

3 files changed

+185
-143
lines changed

contracts/Boba_SpokePool.sol

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// SPDX-License-Identifier: GPL-3.0-only
22
pragma solidity ^0.8.0;
33

4-
import "./Optimism_SpokePool.sol";
4+
import "./Ovm_SpokePool.sol";
55

66
/**
7-
* @notice Boba Spoke pool. Exact copy of the Optimism_SpokePool with no modifications.
7+
* @notice Boba Spoke pool. Note that the l2ETH and l2WETH are the opposite as that in Optimism.
88
*/
9-
contract Boba_SpokePool is Optimism_SpokePool {
9+
contract Boba_SpokePool is Ovm_SpokePool {
1010
/**
1111
* @notice Construct the OVM Boba SpokePool.
1212
* @param _crossDomainAdmin Cross domain admin to set. Can be changed by admin.
@@ -17,5 +17,13 @@ contract Boba_SpokePool is Optimism_SpokePool {
1717
address _crossDomainAdmin,
1818
address _hubPool,
1919
address timerAddress
20-
) Optimism_SpokePool(_crossDomainAdmin, _hubPool, timerAddress) {}
20+
)
21+
Ovm_SpokePool(
22+
_crossDomainAdmin,
23+
_hubPool,
24+
timerAddress,
25+
0x4200000000000000000000000000000000000006,
26+
0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000
27+
)
28+
{}
2129
}

contracts/Optimism_SpokePool.sol

Lines changed: 12 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,15 @@
11
// SPDX-License-Identifier: GPL-3.0-only
22
pragma solidity ^0.8.0;
3-
4-
import "./SpokePool.sol";
5-
import "./interfaces/WETH9.sol";
6-
7-
import "@eth-optimism/contracts/libraries/bridge/CrossDomainEnabled.sol";
83
import "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
9-
import "@eth-optimism/contracts/L2/messaging/IL2ERC20Bridge.sol";
4+
5+
import "./Ovm_SpokePool.sol";
106

117
/**
12-
* @notice OVM specific SpokePool. Uses OVM cross-domain-enabled logic to implement admin only access to functions.
8+
* @notice Optimism Spoke pool.
139
*/
14-
contract Optimism_SpokePool is CrossDomainEnabled, SpokePool {
15-
// "l1Gas" parameter used in call to bridge tokens from this contract back to L1 via IL2ERC20Bridge. Currently
16-
// unused by bridge but included for future compatibility.
17-
uint32 public l1Gas = 5_000_000;
18-
19-
// ETH is an ERC20 on OVM.
20-
address public immutable l2Eth = address(Lib_PredeployAddresses.OVM_ETH);
21-
22-
// Stores alternative token bridges to use for L2 tokens that don't go over the standard bridge. This is needed
23-
// to support non-standard ERC20 tokens on Optimism, such as DIA and SNX which both use custom bridges.
24-
mapping(address => address) public tokenBridges;
25-
26-
event OptimismTokensBridged(address indexed l2Token, address target, uint256 numberOfTokensBridged, uint256 l1Gas);
27-
event SetL1Gas(uint32 indexed newL1Gas);
28-
event SetL2TokenBridge(address indexed l2Token, address indexed tokenBridge);
29-
10+
contract Optimism_SpokePool is Ovm_SpokePool {
3011
/**
31-
* @notice Construct the OVM SpokePool.
12+
* @notice Construct the OVM Optimism SpokePool.
3213
* @param _crossDomainAdmin Cross domain admin to set. Can be changed by admin.
3314
* @param _hubPool Hub pool address to set. Can be changed by admin.
3415
* @param timerAddress Timer address to set.
@@ -38,120 +19,12 @@ contract Optimism_SpokePool is CrossDomainEnabled, SpokePool {
3819
address _hubPool,
3920
address timerAddress
4021
)
41-
CrossDomainEnabled(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER)
42-
SpokePool(_crossDomainAdmin, _hubPool, 0x4200000000000000000000000000000000000006, timerAddress)
22+
Ovm_SpokePool(
23+
_crossDomainAdmin,
24+
_hubPool,
25+
Lib_PredeployAddresses.OVM_ETH,
26+
0x4200000000000000000000000000000000000006,
27+
timerAddress
28+
)
4329
{}
44-
45-
/*******************************************
46-
* OPTIMISM-SPECIFIC ADMIN FUNCTIONS *
47-
*******************************************/
48-
49-
/**
50-
* @notice Change L1 gas limit. Callable only by admin.
51-
* @param newl1Gas New L1 gas limit to set.
52-
*/
53-
function setL1GasLimit(uint32 newl1Gas) public onlyAdmin nonReentrant {
54-
l1Gas = newl1Gas;
55-
emit SetL1Gas(newl1Gas);
56-
}
57-
58-
/**
59-
* @notice Set bridge contract for L2 token used to withdraw back to L1.
60-
* @dev If this mapping isn't set for an L2 token, then the standard bridge will be used to bridge this token.
61-
* @param tokenBridge Address of token bridge
62-
*/
63-
function setTokenBridge(address l2Token, address tokenBridge) public onlyAdmin nonReentrant {
64-
tokenBridges[l2Token] = tokenBridge;
65-
emit SetL2TokenBridge(l2Token, tokenBridge);
66-
}
67-
68-
/**************************************
69-
* DATA WORKER FUNCTIONS *
70-
**************************************/
71-
72-
/**
73-
* @notice Wraps any ETH into WETH before executing base function. This is necessary because SpokePool receives
74-
* ETH over the canonical token bridge instead of WETH.
75-
* @inheritdoc SpokePool
76-
*/
77-
function executeSlowRelayLeaf(
78-
address depositor,
79-
address recipient,
80-
address destinationToken,
81-
uint256 totalRelayAmount,
82-
uint256 originChainId,
83-
uint64 realizedLpFeePct,
84-
uint64 relayerFeePct,
85-
uint32 depositId,
86-
uint32 rootBundleId,
87-
bytes32[] memory proof
88-
) public override(SpokePool) nonReentrant {
89-
if (destinationToken == address(wrappedNativeToken)) _depositEthToWeth();
90-
91-
_executeSlowRelayLeaf(
92-
depositor,
93-
recipient,
94-
destinationToken,
95-
totalRelayAmount,
96-
originChainId,
97-
chainId(),
98-
realizedLpFeePct,
99-
relayerFeePct,
100-
depositId,
101-
rootBundleId,
102-
proof
103-
);
104-
}
105-
106-
/**
107-
* @notice Wraps any ETH into WETH before executing base function. This is necessary because SpokePool receives
108-
* ETH over the canonical token bridge instead of WETH.
109-
* @inheritdoc SpokePool
110-
*/
111-
function executeRelayerRefundLeaf(
112-
uint32 rootBundleId,
113-
SpokePoolInterface.RelayerRefundLeaf memory relayerRefundLeaf,
114-
bytes32[] memory proof
115-
) public override(SpokePool) nonReentrant {
116-
if (relayerRefundLeaf.l2TokenAddress == address(wrappedNativeToken)) _depositEthToWeth();
117-
118-
_executeRelayerRefundLeaf(rootBundleId, relayerRefundLeaf, proof);
119-
}
120-
121-
/**************************************
122-
* INTERNAL FUNCTIONS *
123-
**************************************/
124-
125-
// Wrap any ETH owned by this contract so we can send expected L2 token to recipient. This is necessary because
126-
// this SpokePool will receive ETH from the canonical token bridge instead of WETH. Its not sufficient to execute
127-
// this logic inside a fallback method that executes when this contract receives ETH because ETH is an ERC20
128-
// on the OVM.
129-
function _depositEthToWeth() internal {
130-
if (address(this).balance > 0) wrappedNativeToken.deposit{ value: address(this).balance }();
131-
}
132-
133-
function _bridgeTokensToHubPool(RelayerRefundLeaf memory relayerRefundLeaf) internal override {
134-
// If the token being bridged is WETH then we need to first unwrap it to ETH and then send ETH over the
135-
// canonical bridge. On Optimism, this is address 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000.
136-
if (relayerRefundLeaf.l2TokenAddress == address(wrappedNativeToken)) {
137-
WETH9(relayerRefundLeaf.l2TokenAddress).withdraw(relayerRefundLeaf.amountToReturn); // Unwrap into ETH.
138-
relayerRefundLeaf.l2TokenAddress = l2Eth; // Set the l2TokenAddress to ETH.
139-
}
140-
IL2ERC20Bridge(
141-
tokenBridges[relayerRefundLeaf.l2TokenAddress] == address(0)
142-
? Lib_PredeployAddresses.L2_STANDARD_BRIDGE
143-
: tokenBridges[relayerRefundLeaf.l2TokenAddress]
144-
).withdrawTo(
145-
relayerRefundLeaf.l2TokenAddress, // _l2Token. Address of the L2 token to bridge over.
146-
hubPool, // _to. Withdraw, over the bridge, to the l1 pool contract.
147-
relayerRefundLeaf.amountToReturn, // _amount.
148-
l1Gas, // _l1Gas. Unused, but included for potential forward compatibility considerations
149-
"" // _data. We don't need to send any data for the bridging action.
150-
);
151-
152-
emit OptimismTokensBridged(relayerRefundLeaf.l2TokenAddress, hubPool, relayerRefundLeaf.amountToReturn, l1Gas);
153-
}
154-
155-
// Apply OVM-specific transformation to cross domain admin address on L1.
156-
function _requireAdminSender() internal override onlyFromCrossDomainAccount(crossDomainAdmin) {}
15730
}

contracts/Ovm_SpokePool.sol

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity ^0.8.0;
3+
4+
import "./SpokePool.sol";
5+
import "./interfaces/WETH9.sol";
6+
7+
import "@eth-optimism/contracts/libraries/bridge/CrossDomainEnabled.sol";
8+
import "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
9+
import "@eth-optimism/contracts/L2/messaging/IL2ERC20Bridge.sol";
10+
11+
/**
12+
* @notice OVM specific SpokePool. Uses OVM cross-domain-enabled logic to implement admin only access to functions. * Optimism and Boba each implement this spoke pool and set their chain specific contract addresses for l2Eth and l2Weth.
13+
*/
14+
contract Ovm_SpokePool is CrossDomainEnabled, SpokePool {
15+
// "l1Gas" parameter used in call to bridge tokens from this contract back to L1 via IL2ERC20Bridge. Currently
16+
// unused by bridge but included for future compatibility.
17+
uint32 public l1Gas = 5_000_000;
18+
19+
// ETH is an ERC20 on OVM.
20+
address public immutable l2Eth;
21+
22+
// Stores alternative token bridges to use for L2 tokens that don't go over the standard bridge. This is needed
23+
// to support non-standard ERC20 tokens on Optimism, such as DIA and SNX which both use custom bridges.
24+
mapping(address => address) public tokenBridges;
25+
26+
event OptimismTokensBridged(address indexed l2Token, address target, uint256 numberOfTokensBridged, uint256 l1Gas);
27+
event SetL1Gas(uint32 indexed newL1Gas);
28+
event SetL2TokenBridge(address indexed l2Token, address indexed tokenBridge);
29+
30+
/**
31+
* @notice Construct the OVM SpokePool.
32+
* @param _crossDomainAdmin Cross domain admin to set. Can be changed by admin.
33+
* @param _hubPool Hub pool address to set. Can be changed by admin.
34+
* @param timerAddress Timer address to set.
35+
*/
36+
constructor(
37+
address _crossDomainAdmin,
38+
address _hubPool,
39+
address _l2Eth,
40+
address _wrappedNativeToken,
41+
address timerAddress
42+
)
43+
CrossDomainEnabled(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER)
44+
SpokePool(_crossDomainAdmin, _hubPool, _wrappedNativeToken, timerAddress)
45+
{
46+
l2Eth = _l2Eth;
47+
}
48+
49+
/*******************************************
50+
* OPTIMISM-SPECIFIC ADMIN FUNCTIONS *
51+
*******************************************/
52+
53+
/**
54+
* @notice Change L1 gas limit. Callable only by admin.
55+
* @param newl1Gas New L1 gas limit to set.
56+
*/
57+
function setL1GasLimit(uint32 newl1Gas) public onlyAdmin nonReentrant {
58+
l1Gas = newl1Gas;
59+
emit SetL1Gas(newl1Gas);
60+
}
61+
62+
/**
63+
* @notice Set bridge contract for L2 token used to withdraw back to L1.
64+
* @dev If this mapping isn't set for an L2 token, then the standard bridge will be used to bridge this token.
65+
* @param tokenBridge Address of token bridge
66+
*/
67+
function setTokenBridge(address l2Token, address tokenBridge) public onlyAdmin nonReentrant {
68+
tokenBridges[l2Token] = tokenBridge;
69+
emit SetL2TokenBridge(l2Token, tokenBridge);
70+
}
71+
72+
/**************************************
73+
* DATA WORKER FUNCTIONS *
74+
**************************************/
75+
76+
/**
77+
* @notice Wraps any ETH into WETH before executing base function. This is necessary because SpokePool receives
78+
* ETH over the canonical token bridge instead of WETH.
79+
* @inheritdoc SpokePool
80+
*/
81+
function executeSlowRelayLeaf(
82+
address depositor,
83+
address recipient,
84+
address destinationToken,
85+
uint256 totalRelayAmount,
86+
uint256 originChainId,
87+
uint64 realizedLpFeePct,
88+
uint64 relayerFeePct,
89+
uint32 depositId,
90+
uint32 rootBundleId,
91+
bytes32[] memory proof
92+
) public override(SpokePool) nonReentrant {
93+
if (destinationToken == address(wrappedNativeToken)) _depositEthToWeth();
94+
95+
_executeSlowRelayLeaf(
96+
depositor,
97+
recipient,
98+
destinationToken,
99+
totalRelayAmount,
100+
originChainId,
101+
chainId(),
102+
realizedLpFeePct,
103+
relayerFeePct,
104+
depositId,
105+
rootBundleId,
106+
proof
107+
);
108+
}
109+
110+
/**
111+
* @notice Wraps any ETH into WETH before executing base function. This is necessary because SpokePool receives
112+
* ETH over the canonical token bridge instead of WETH.
113+
* @inheritdoc SpokePool
114+
*/
115+
function executeRelayerRefundLeaf(
116+
uint32 rootBundleId,
117+
SpokePoolInterface.RelayerRefundLeaf memory relayerRefundLeaf,
118+
bytes32[] memory proof
119+
) public override(SpokePool) nonReentrant {
120+
if (relayerRefundLeaf.l2TokenAddress == address(wrappedNativeToken)) _depositEthToWeth();
121+
122+
_executeRelayerRefundLeaf(rootBundleId, relayerRefundLeaf, proof);
123+
}
124+
125+
/**************************************
126+
* INTERNAL FUNCTIONS *
127+
**************************************/
128+
129+
// Wrap any ETH owned by this contract so we can send expected L2 token to recipient. This is necessary because
130+
// this SpokePool will receive ETH from the canonical token bridge instead of WETH. Its not sufficient to execute
131+
// this logic inside a fallback method that executes when this contract receives ETH because ETH is an ERC20
132+
// on the OVM.
133+
function _depositEthToWeth() internal {
134+
if (address(this).balance > 0) wrappedNativeToken.deposit{ value: address(this).balance }();
135+
}
136+
137+
function _bridgeTokensToHubPool(RelayerRefundLeaf memory relayerRefundLeaf) internal override {
138+
// If the token being bridged is WETH then we need to first unwrap it to ETH and then send ETH over the
139+
// canonical bridge. On Optimism, this is address 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000.
140+
if (relayerRefundLeaf.l2TokenAddress == address(wrappedNativeToken)) {
141+
WETH9(relayerRefundLeaf.l2TokenAddress).withdraw(relayerRefundLeaf.amountToReturn); // Unwrap into ETH.
142+
relayerRefundLeaf.l2TokenAddress = l2Eth; // Set the l2TokenAddress to ETH.
143+
}
144+
IL2ERC20Bridge(
145+
tokenBridges[relayerRefundLeaf.l2TokenAddress] == address(0)
146+
? Lib_PredeployAddresses.L2_STANDARD_BRIDGE
147+
: tokenBridges[relayerRefundLeaf.l2TokenAddress]
148+
).withdrawTo(
149+
relayerRefundLeaf.l2TokenAddress, // _l2Token. Address of the L2 token to bridge over.
150+
hubPool, // _to. Withdraw, over the bridge, to the l1 pool contract.
151+
relayerRefundLeaf.amountToReturn, // _amount.
152+
l1Gas, // _l1Gas. Unused, but included for potential forward compatibility considerations
153+
"" // _data. We don't need to send any data for the bridging action.
154+
);
155+
156+
emit OptimismTokensBridged(relayerRefundLeaf.l2TokenAddress, hubPool, relayerRefundLeaf.amountToReturn, l1Gas);
157+
}
158+
159+
// Apply OVM-specific transformation to cross domain admin address on L1.
160+
function _requireAdminSender() internal override onlyFromCrossDomainAccount(crossDomainAdmin) {}
161+
}

0 commit comments

Comments
 (0)