Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 3 additions & 0 deletions contracts/src/bridge/IInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ error InsufficientValue(uint256 expected, uint256 actual);
/// @dev submission cost provided isn't enough to create retryable ticket
error InsufficientSubmissionCost(uint256 expected, uint256 actual);

/// @dev address not allowed to interact with the given contract
error NotAllowedOrigin(address origin);

/// @dev used to convey retryable tx data in eth calls without requiring a tx trace
/// this follows a pattern similar to EIP-3668 where reverts surface call information
error RetryableData(
Expand Down
64 changes: 55 additions & 9 deletions contracts/src/bridge/Inbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,40 @@ import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
IBridge public override bridge;

/// ------------------------------------ allow list start ------------------------------------ ///

bool public allowListEnabled;
mapping(address => bool) public isAllowed;

event AllowListAddressSet(address indexed user, bool val);
event AllowListEnabledUpdated(bool isEnabled);

function setAllowList(address[] memory user, bool[] memory val) external onlyOwner {
require(user.length == val.length, "INVALID_INPUT");

for (uint256 i = 0; i < user.length; i++) {
isAllowed[user[i]] = val[i];
emit AllowListAddressSet(user[i], val[i]);
}
}

function setAllowListEnabled(bool _allowListEnabled) external onlyOwner {
require(_allowListEnabled != allowListEnabled, "ALREADY_SET");
allowListEnabled = _allowListEnabled;
emit AllowListEnabledUpdated(_allowListEnabled);
}

/// @dev this modifier checks the tx.origin instead of msg.sender for convenience (ie it allows
/// allowed users to interact with the token bridge without needing the token bridge to be allowList aware).
/// this modifier is not intended to use to be used for security (since this opens the allowList to
/// a smart contract phishing risk).
modifier onlyAllowed() {
if (allowListEnabled && !isAllowed[tx.origin]) revert NotAllowedOrigin(tx.origin);
_;
}

/// ------------------------------------ allow list end ------------------------------------ ///

modifier onlyOwner() {
// whoevever owns the Bridge, also owns the Inbox. this is usually the rollup contract
address bridgeOwner = OwnableUpgradeable(address(bridge)).owner();
Expand All @@ -52,6 +86,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
function initialize(IBridge _bridge) external initializer onlyDelegated {
if (address(bridge) != address(0)) revert AlreadyInit();
bridge = _bridge;
allowListEnabled = false;
__Pausable_init();
}

Expand All @@ -64,6 +99,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
sstore(i, 0)
}
}
allowListEnabled = false;
bridge = _bridge;
}

Expand All @@ -75,6 +111,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
function sendL2MessageFromOrigin(bytes calldata messageData)
external
whenNotPaused
onlyAllowed
returns (uint256)
{
// solhint-disable-next-line avoid-tx-origin
Expand All @@ -95,6 +132,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
external
override
whenNotPaused
onlyAllowed
returns (uint256)
{
return _deliverMessage(L2_MSG, msg.sender, messageData);
Expand All @@ -106,7 +144,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
uint256 nonce,
address to,
bytes calldata data
) external payable virtual override whenNotPaused returns (uint256) {
) external payable virtual override whenNotPaused onlyAllowed returns (uint256) {
return
_deliverMessage(
L1MessageType_L2FundedByL1,
Expand All @@ -128,7 +166,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
uint256 maxFeePerGas,
address to,
bytes calldata data
) external payable virtual override whenNotPaused returns (uint256) {
) external payable virtual override whenNotPaused onlyAllowed returns (uint256) {
return
_deliverMessage(
L1MessageType_L2FundedByL1,
Expand All @@ -151,7 +189,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
address to,
uint256 value,
bytes calldata data
) external virtual override whenNotPaused returns (uint256) {
) external virtual override whenNotPaused onlyAllowed returns (uint256) {
return
_deliverMessage(
L2_MSG,
Expand All @@ -174,7 +212,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
address to,
uint256 value,
bytes calldata data
) external virtual override whenNotPaused returns (uint256) {
) external virtual override whenNotPaused onlyAllowed returns (uint256) {
return
_deliverMessage(
L2_MSG,
Expand Down Expand Up @@ -209,7 +247,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
/// @dev this does not trigger the fallback function when receiving in the L2 side.
/// Look into retryable tickets if you are interested in this functionality.
/// @dev this function should not be called inside contract constructors
function depositEth() public payable override whenNotPaused returns (uint256) {
function depositEth() public payable override whenNotPaused onlyAllowed returns (uint256) {
address sender = msg.sender;

// solhint-disable-next-line avoid-tx-origin
Expand All @@ -232,7 +270,15 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
}

/// @notice deprecated in favour of depositEth with no parameters
function depositEth(uint256) external payable virtual override whenNotPaused returns (uint256) {
function depositEth(uint256)
external
payable
virtual
override
whenNotPaused
onlyAllowed
returns (uint256)
{
return depositEth();
}

Expand All @@ -259,7 +305,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable virtual whenNotPaused returns (uint256) {
) external payable virtual whenNotPaused onlyAllowed returns (uint256) {
return
unsafeCreateRetryableTicket(
to,
Expand Down Expand Up @@ -296,7 +342,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable virtual override whenNotPaused returns (uint256) {
) external payable virtual override whenNotPaused onlyAllowed returns (uint256) {
// ensure the user's deposit alone will make submission succeed
if (msg.value < maxSubmissionCost + l2CallValue)
revert InsufficientValue(maxSubmissionCost + l2CallValue, msg.value);
Expand Down Expand Up @@ -351,7 +397,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) public payable virtual override whenNotPaused returns (uint256) {
) public payable virtual override whenNotPaused onlyAllowed returns (uint256) {
// gas price and limit of 1 should never be a valid input, so instead they are used as
// magic values to trigger a revert in eth calls that surface data without requiring a tx trace
if (gasLimit == 1 || maxFeePerGas == 1)
Expand Down