Skip to content

Commit fd1e426

Browse files
pxrlnicholaspai
andauthored
improve: N01-N16 Various Fixes (#23) (#441)
Changes by Nick. Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com>
1 parent f67b64c commit fd1e426

23 files changed

+304
-388
lines changed

contracts/Arbitrum_SpokePool.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ contract Arbitrum_SpokePool is SpokePool, CircleCCTPAdapter {
3939
ITokenMessenger _cctpTokenMessenger
4040
)
4141
SpokePool(_wrappedNativeTokenAddress, _depositQuoteTimeBuffer, _fillDeadlineBuffer)
42-
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, 0)
42+
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, CircleDomainIds.Ethereum)
4343
{} // solhint-disable-line no-empty-blocks
4444

4545
/**

contracts/BondToken.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ interface ExtendedHubPoolInterface is HubPoolInterface {
2424
contract BondToken is WETH9, Ownable {
2525
using Address for address;
2626

27-
ExtendedHubPoolInterface public immutable hubPool;
27+
ExtendedHubPoolInterface public immutable HUB_POOL;
2828

2929
/**
3030
* @notice Addresses that are permitted to make HubPool root bundle proposals.
@@ -43,7 +43,7 @@ contract BondToken is WETH9, Ownable {
4343
constructor(ExtendedHubPoolInterface _hubPool) {
4444
name = "Across Bond Token";
4545
symbol = "ABT";
46-
hubPool = _hubPool;
46+
HUB_POOL = _hubPool;
4747
}
4848

4949
/**
@@ -75,8 +75,8 @@ contract BondToken is WETH9, Ownable {
7575
address dst,
7676
uint256 amt
7777
) public override returns (bool) {
78-
if (dst == address(hubPool)) {
79-
require(proposers[src] || hubPool.rootBundleProposal().proposer != src, "Transfer not permitted");
78+
if (dst == address(HUB_POOL)) {
79+
require(proposers[src] || HUB_POOL.rootBundleProposal().proposer != src, "Transfer not permitted");
8080
}
8181
return super.transferFrom(src, dst, amt);
8282
}

contracts/Linea_SpokePool.sol

Lines changed: 4 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,82 +5,10 @@
55
pragma solidity 0.8.19;
66

77
import "./SpokePool.sol";
8+
import { IMessageService, ITokenBridge, IUSDCBridge } from "./external/interfaces/LineaInterfaces.sol";
89
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
910
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
1011

11-
/**
12-
* @notice Interface of Linea's Canonical Message Service
13-
* See https://github.com/Consensys/linea-contracts/blob/3cf85529fd4539eb06ba998030c37e47f98c528a/contracts/interfaces/IMessageService.sol
14-
*/
15-
interface IMessageService {
16-
/**
17-
* @notice Sends a message for transporting from the given chain.
18-
* @dev This function should be called with a msg.value = _value + _fee. The fee will be paid on the destination chain.
19-
* @param _to The destination address on the destination chain.
20-
* @param _fee The message service fee on the origin chain.
21-
* @param _calldata The calldata used by the destination message service to call the destination contract.
22-
*/
23-
function sendMessage(
24-
address _to,
25-
uint256 _fee,
26-
bytes calldata _calldata
27-
) external payable;
28-
29-
/**
30-
* @notice Returns the original sender of the message on the origin layer.
31-
*/
32-
function sender() external view returns (address);
33-
34-
/**
35-
* @notice Minimum fee to use when sending a message. Currently, only exists on L2MessageService.
36-
* See https://github.com/Consensys/linea-contracts/blob/3cf85529fd4539eb06ba998030c37e47f98c528a/contracts/messageService/l2/L2MessageService.sol#L37
37-
*/
38-
function minimumFeeInWei() external view returns (uint256);
39-
}
40-
41-
/**
42-
* @notice Interface of Linea's Canonical Token Bridge
43-
* See https://github.com/Consensys/linea-contracts/blob/3cf85529fd4539eb06ba998030c37e47f98c528a/contracts/tokenBridge/interfaces/ITokenBridge.sol
44-
*/
45-
interface ITokenBridge {
46-
/**
47-
* @notice This function is the single entry point to bridge tokens to the
48-
* other chain, both for native and already bridged tokens. You can use it
49-
* to bridge any ERC20. If the token is bridged for the first time an ERC20
50-
* (BridgedToken.sol) will be automatically deployed on the target chain.
51-
* @dev User should first allow the bridge to transfer tokens on his behalf.
52-
* Alternatively, you can use `bridgeTokenWithPermit` to do so in a single
53-
* transaction. If you want the transfer to be automatically executed on the
54-
* destination chain. You should send enough ETH to pay the postman fees.
55-
* Note that Linea can reserve some tokens (which use a dedicated bridge).
56-
* In this case, the token cannot be bridged. Linea can only reserve tokens
57-
* that have not been bridged yet.
58-
* Linea can pause the bridge for security reason. In this case new bridge
59-
* transaction would revert.
60-
* @param _token The address of the token to be bridged.
61-
* @param _amount The amount of the token to be bridged.
62-
* @param _recipient The address that will receive the tokens on the other chain.
63-
*/
64-
function bridgeToken(
65-
address _token,
66-
uint256 _amount,
67-
address _recipient
68-
) external payable;
69-
}
70-
71-
interface IUSDCBridge {
72-
function usdc() external view returns (address);
73-
74-
/**
75-
* @dev Sends the sender's USDC from L1 to the recipient on L2, locks the USDC sent
76-
* in this contract and sends a message to the message bridge
77-
* contract to mint the equivalent USDC on L2
78-
* @param amount The amount of USDC to send
79-
* @param to The recipient's address to receive the funds
80-
*/
81-
function depositTo(uint256 amount, address to) external payable;
82-
}
83-
8412
/**
8513
* @notice Linea specific SpokePool.
8614
*/
@@ -153,7 +81,7 @@ contract Linea_SpokePool is SpokePool {
15381
* sending L2->L1 messages.
15482
*/
15583
function minimumFeeInWei() public view returns (uint256) {
156-
return IMessageService(l2MessageService).minimumFeeInWei();
84+
return l2MessageService.minimumFeeInWei();
15785
}
15886

15987
/****************************************
@@ -218,7 +146,7 @@ contract Linea_SpokePool is SpokePool {
218146
// send ETH directly via the Canonical Message Service.
219147
if (l2TokenAddress == address(wrappedNativeToken)) {
220148
WETH9Interface(l2TokenAddress).withdraw(amountToReturn); // Unwrap into ETH.
221-
IMessageService(l2MessageService).sendMessage{ value: amountToReturn + msg.value }(hubPool, msg.value, "");
149+
l2MessageService.sendMessage{ value: amountToReturn + msg.value }(hubPool, msg.value, "");
222150
}
223151
// If the l1Token is USDC, then we need sent it via the USDC Bridge.
224152
else if (l2TokenAddress == l2UsdcBridge.usdc()) {
@@ -234,7 +162,7 @@ contract Linea_SpokePool is SpokePool {
234162

235163
function _requireAdminSender() internal view override {
236164
require(
237-
IMessageService(l2MessageService).sender() == crossDomainAdmin && msg.sender == address(l2MessageService),
165+
l2MessageService.sender() == crossDomainAdmin && msg.sender == address(l2MessageService),
238166
"ONLY_COUNTERPART_GATEWAY"
239167
);
240168
}

contracts/Ovm_SpokePool.sol

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ contract Ovm_SpokePool is SpokePool, CircleCCTPAdapter {
3636
address public l2Eth;
3737

3838
// Address of the Optimism L2 messenger.
39-
address public messenger;
39+
address public constant MESSENGER = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
4040

4141
// Address of custom bridge used to bridge Synthetix-related assets like SNX.
4242
address private constant SYNTHETIX_BRIDGE = 0x136b1EC699c62b0606854056f02dC7Bb80482d63;
@@ -60,7 +60,7 @@ contract Ovm_SpokePool is SpokePool, CircleCCTPAdapter {
6060
ITokenMessenger _cctpTokenMessenger
6161
)
6262
SpokePool(_wrappedNativeTokenAddress, _depositQuoteTimeBuffer, _fillDeadlineBuffer)
63-
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, 0)
63+
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, CircleDomainIds.Ethereum)
6464
{} // solhint-disable-line no-empty-blocks
6565

6666
/**
@@ -80,7 +80,6 @@ contract Ovm_SpokePool is SpokePool, CircleCCTPAdapter {
8080
) public onlyInitializing {
8181
l1Gas = 5_000_000;
8282
__SpokePool_init(_initialDepositId, _crossDomainAdmin, _hubPool);
83-
messenger = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
8483
//slither-disable-next-line missing-zero-check
8584
l2Eth = _l2Eth;
8685
}
@@ -170,7 +169,7 @@ contract Ovm_SpokePool is SpokePool, CircleCCTPAdapter {
170169
// Apply OVM-specific transformation to cross domain admin address on L1.
171170
function _requireAdminSender() internal view override {
172171
require(
173-
LibOptimismUpgradeable.crossChainSender(messenger) == crossDomainAdmin,
172+
LibOptimismUpgradeable.crossChainSender(MESSENGER) == crossDomainAdmin,
174173
"OVM_XCHAIN: wrong sender of cross-domain message"
175174
);
176175
}

contracts/PolygonTokenBridger.sol

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ contract PolygonTokenBridger is Lockable {
4343
using SafeERC20Upgradeable for IERC20Upgradeable;
4444

4545
// Gas token for Polygon.
46-
MaticToken public constant maticToken = MaticToken(0x0000000000000000000000000000000000001010);
46+
MaticToken public constant MATIC = MaticToken(0x0000000000000000000000000000000000001010);
4747

4848
// Should be set to HubPool on Ethereum, or unused on Polygon.
4949
address public immutable destination;
@@ -113,8 +113,7 @@ contract PolygonTokenBridger is Lockable {
113113
token.withdraw(token.balanceOf(address(this)));
114114

115115
// This takes the token that was withdrawn and calls withdraw on the "native" ERC20.
116-
if (address(token) == l2WrappedMatic)
117-
maticToken.withdraw{ value: address(this).balance }(address(this).balance);
116+
if (address(token) == l2WrappedMatic) MATIC.withdraw{ value: address(this).balance }(address(this).balance);
118117
}
119118

120119
/**

contracts/PolygonZkEVM_SpokePool.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ contract PolygonZkEVM_SpokePool is SpokePool, IBridgeMessageReceiver {
3535
IPolygonZkEVMBridge public l2PolygonZkEVMBridge;
3636

3737
// Polygon zkEVM's internal network id for L1.
38-
uint32 public constant l1NetworkId = 0;
38+
uint32 public constant POLYGON_ZKEVM_L1_NETWORK_ID = 0;
3939

4040
// Warning: this variable should _never_ be touched outside of this contract. It is intentionally set to be
4141
// private. Leaving it set to true can permanently disable admin calls.
@@ -136,7 +136,7 @@ contract PolygonZkEVM_SpokePool is SpokePool, IBridgeMessageReceiver {
136136
if (_originAddress != crossDomainAdmin) {
137137
revert OriginSenderNotCrossDomain();
138138
}
139-
if (_originNetwork != l1NetworkId) {
139+
if (_originNetwork != POLYGON_ZKEVM_L1_NETWORK_ID) {
140140
revert SourceChainNotHubChain();
141141
}
142142

@@ -175,7 +175,7 @@ contract PolygonZkEVM_SpokePool is SpokePool, IBridgeMessageReceiver {
175175
if (l2TokenAddress == address(wrappedNativeToken)) {
176176
WETH9Interface(l2TokenAddress).withdraw(amountToReturn); // Unwrap into ETH.
177177
l2PolygonZkEVMBridge.bridgeAsset{ value: amountToReturn }(
178-
l1NetworkId,
178+
POLYGON_ZKEVM_L1_NETWORK_ID,
179179
hubPool,
180180
amountToReturn,
181181
address(0),
@@ -185,7 +185,7 @@ contract PolygonZkEVM_SpokePool is SpokePool, IBridgeMessageReceiver {
185185
} else {
186186
IERC20(l2TokenAddress).safeIncreaseAllowance(address(l2PolygonZkEVMBridge), amountToReturn);
187187
l2PolygonZkEVMBridge.bridgeAsset(
188-
l1NetworkId,
188+
POLYGON_ZKEVM_L1_NETWORK_ID,
189189
hubPool,
190190
amountToReturn,
191191
l2TokenAddress,

contracts/Polygon_SpokePool.sol

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ contract Polygon_SpokePool is IFxMessageProcessor, SpokePool, CircleCCTPAdapter
8181
ITokenMessenger _cctpTokenMessenger
8282
)
8383
SpokePool(_wrappedNativeTokenAddress, _depositQuoteTimeBuffer, _fillDeadlineBuffer)
84-
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, 0)
84+
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, CircleDomainIds.Ethereum)
8585
{} // solhint-disable-line no-empty-blocks
8686

8787
/**
@@ -102,9 +102,9 @@ contract Polygon_SpokePool is IFxMessageProcessor, SpokePool, CircleCCTPAdapter
102102
) public initializer {
103103
callValidated = false;
104104
__SpokePool_init(_initialDepositId, _crossDomainAdmin, _hubPool);
105-
polygonTokenBridger = _polygonTokenBridger;
105+
_setPolygonTokenBridger(payable(_polygonTokenBridger));
106106
//slither-disable-next-line missing-zero-check
107-
fxChild = _fxChild;
107+
_setFxChild(_fxChild);
108108
}
109109

110110
/********************************************************
@@ -116,18 +116,15 @@ contract Polygon_SpokePool is IFxMessageProcessor, SpokePool, CircleCCTPAdapter
116116
* @param newFxChild New FxChild.
117117
*/
118118
function setFxChild(address newFxChild) public onlyAdmin nonReentrant {
119-
//slither-disable-next-line missing-zero-check
120-
fxChild = newFxChild;
121-
emit SetFxChild(newFxChild);
119+
_setFxChild(newFxChild);
122120
}
123121

124122
/**
125123
* @notice Change polygonTokenBridger address. Callable only by admin via processMessageFromRoot.
126124
* @param newPolygonTokenBridger New Polygon Token Bridger contract.
127125
*/
128126
function setPolygonTokenBridger(address payable newPolygonTokenBridger) public onlyAdmin nonReentrant {
129-
polygonTokenBridger = PolygonTokenBridger(newPolygonTokenBridger);
130-
emit SetPolygonTokenBridger(address(newPolygonTokenBridger));
127+
_setPolygonTokenBridger(newPolygonTokenBridger);
131128
}
132129

133130
/**
@@ -216,6 +213,17 @@ contract Polygon_SpokePool is IFxMessageProcessor, SpokePool, CircleCCTPAdapter
216213
* INTERNAL FUNCTIONS *
217214
**************************************/
218215

216+
function _setFxChild(address _fxChild) internal {
217+
//slither-disable-next-line missing-zero-check
218+
fxChild = _fxChild;
219+
emit SetFxChild(_fxChild);
220+
}
221+
222+
function _setPolygonTokenBridger(address payable _polygonTokenBridger) internal {
223+
polygonTokenBridger = PolygonTokenBridger(_polygonTokenBridger);
224+
emit SetPolygonTokenBridger(address(_polygonTokenBridger));
225+
}
226+
219227
function _preExecuteLeafHook(address) internal override {
220228
// Wraps MATIC --> WMATIC before distributing tokens from this contract.
221229
_wrap();

contracts/Scroll_SpokePool.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ contract Scroll_SpokePool is SpokePool {
5757
address _hubPool
5858
) public initializer {
5959
__SpokePool_init(_initialDepositId, _crossDomainAdmin, _hubPool);
60-
l2GatewayRouter = _l2GatewayRouter;
61-
l2ScrollMessenger = _l2ScrollMessenger;
60+
_setL2GatewayRouter(_l2GatewayRouter);
61+
_setL2MessageService(_l2ScrollMessenger);
6262
}
6363

6464
/**

contracts/SpokePool.sol

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ interface AcrossMessageHandler {
4545
* on the destination chain. Locked source chain tokens are later sent over the canonical token bridge to L1 HubPool.
4646
* Relayers are refunded with destination tokens out of this contract after another off-chain actor, a "data worker",
4747
* submits a proof that the relayer correctly submitted a relay on this SpokePool.
48+
* @custom:security-contact bugs@across.to
4849
*/
4950
abstract contract SpokePool is
5051
V3SpokePoolInterface,
@@ -161,6 +162,11 @@ abstract contract SpokePool is
161162
"UpdateDepositDetails(uint32 depositId,uint256 originChainId,uint256 updatedOutputAmount,address updatedRecipient,bytes updatedMessage)"
162163
);
163164

165+
// Default chain Id used to signify that no repayment is requested, for example when executing a slow fill.
166+
uint256 public constant EMPTY_REPAYMENT_CHAIN_ID = 0;
167+
// Default address used to signify that no relayer should be credited with a refund, for example
168+
// when executing a slow fill.
169+
address public constant EMPTY_RELAYER = address(0);
164170
// This is the magic value that signals to the off-chain validator
165171
// that this deposit can never expire. A deposit with this fill deadline should always be eligible for a
166172
// slow fill, meaning that its output token and input token must be "equivalent". Therefore, this value is only
@@ -215,18 +221,6 @@ abstract contract SpokePool is
215221
bytes message,
216222
RelayExecutionInfo updatableRelayData
217223
);
218-
/// @custom:audit FOLLOWING EVENT TO BE DEPRECATED
219-
event RefundRequested(
220-
address indexed relayer,
221-
address refundToken,
222-
uint256 amount,
223-
uint256 indexed originChainId,
224-
uint256 destinationChainId,
225-
int64 realizedLpFeePct,
226-
uint32 indexed depositId,
227-
uint256 fillBlock,
228-
uint256 previousIdenticalRequests
229-
);
230224
event RelayedRootBundle(
231225
uint32 indexed rootBundleId,
232226
bytes32 indexed relayerRefundRoot,
@@ -552,7 +546,7 @@ abstract contract SpokePool is
552546
* @notice This function is intended for multisig depositors who can accept some LP fee uncertainty in order to lift
553547
* the quoteTimestamp buffer constraint.
554548
* @dev Re-orgs may produce invalid fills if the quoteTimestamp moves across a change in HubPool utilisation.
555-
* @dev The existing function modifiers are already enforced by _deposit(), so no additional modifiers are imposed.
549+
* @dev The existing function modifiers are already enforced by deposit(), so no additional modifiers are imposed.
556550
* @param recipient Address to receive funds at on destination chain.
557551
* @param originToken Token to lock into this contract to initiate deposit.
558552
* @param amount Amount of tokens to deposit. Will be amount of tokens to receive less fees.
@@ -589,7 +583,7 @@ abstract contract SpokePool is
589583
* @notice This function is intended for multisig depositors who can accept some LP fee uncertainty in order to lift
590584
* the quoteTimestamp buffer constraint.
591585
* @dev Re-orgs may produce invalid fills if the quoteTimestamp moves across a change in HubPool utilisation.
592-
* @dev The existing function modifiers are already enforced by _deposit(), so no additional modifiers are imposed.
586+
* @dev The existing function modifiers are already enforced by depositFor(), so no additional modifiers are imposed.
593587
* @param depositor Address who is credited for depositing funds on origin chain and can speed up the deposit.
594588
* @param recipient Address to receive funds at on destination chain.
595589
* @param originToken Token to lock into this contract to initiate deposit.
@@ -707,7 +701,7 @@ abstract contract SpokePool is
707701
* ERC20.
708702
* @param inputAmount The amount of input tokens to pull from the caller's account and lock into this contract.
709703
* This amount will be sent to the relayer on their repayment chain of choice as a refund following an optimistic
710-
* challenge window in the HubPool, plus a system fee.
704+
* challenge window in the HubPool, less a system fee.
711705
* @param outputAmount The amount of output tokens that the relayer will send to the recipient on the destination.
712706
* @param destinationChainId The destination chain identifier. Must be enabled along with the input token
713707
* as a valid deposit route from this spoke pool or this transaction will revert.
@@ -1283,13 +1277,13 @@ abstract contract SpokePool is
12831277
updatedOutputAmount: slowFillLeaf.updatedOutputAmount,
12841278
updatedRecipient: relayData.recipient,
12851279
updatedMessage: relayData.message,
1286-
repaymentChainId: 0 // Hardcoded to 0 for slow fills
1280+
repaymentChainId: EMPTY_REPAYMENT_CHAIN_ID // Repayment not relevant for slow fills.
12871281
});
12881282

12891283
_verifyV3SlowFill(relayExecution, rootBundleId, proof);
12901284

1291-
// - 0x0 hardcoded as relayer for slow fill execution.
1292-
_fillRelayV3(relayExecution, address(0), true);
1285+
// - No relayer to refund for slow fill executions.
1286+
_fillRelayV3(relayExecution, EMPTY_RELAYER, true);
12931287
}
12941288

12951289
/**

0 commit comments

Comments
 (0)