Skip to content

Commit

Permalink
refactor custom refund entrypoint to only be available in L1 (Offchai…
Browse files Browse the repository at this point in the history
…nLabs#2376)

* refactor custom refund entrypoint only available in L1

* cleanup interface

* fix: remove IERC165 from IERC721.sol

* test: new interfaces for compatibility

Co-authored-by: gzeon <[email protected]>
  • Loading branch information
fredlacs and gzeoneth authored Jul 25, 2022
1 parent 0b25dce commit afa5ff4
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 97 deletions.
10 changes: 0 additions & 10 deletions packages/arb-bridge-eth/contracts/interfaces/IERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,3 @@ interface IERC721 {
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

interface IERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,6 @@ abstract contract L2ArbitrumGateway is L2ArbitrumMessenger, TokenGateway {
return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data);
}

function outboundTransferCustomRefund(
address _l1Token,
address _to,
address, /* _refundTo */
uint256 _amount,
uint256, /* _maxGas */
uint256, /* _gasPriceBid */
bytes calldata _data
) public payable override returns (bytes memory res) {
return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data);
}

/**
* @notice Initiates a token withdrawal from Arbitrum to Ethereum
* @param _l1Token l1 address of token
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0

/*
* Copyright 2020, Offchain Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

import "../../libraries/gateway/ITokenGateway.sol";
import "../../libraries/IERC165.sol";

/**
* @title Common interface for gatways on L1 messaging to Arbitrum.
*/
interface IL1ArbitrumGateway is ITokenGateway, IERC165 {
/**
* @notice Deposit ERC20 token from Ethereum into Arbitrum. If L2 side hasn't been deployed yet, includes name/symbol/decimals data for initial L2 deploy. Initiate by GatewayRouter.
* @dev L2 address alias will not be applied to the following types of addresses on L1:
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* @param _l1Token L1 address of ERC20
* @param _refundTo Account, or its L2 alias if it have code in L1, to be credited with excess gas refund in L2
* @param _to Account to be credited with the tokens in the L2 (can be the user's L2 account or a contract), not subject to L2 aliasing
This account, or its L2 alias if it have code in L1, will also be able to cancel the retryable ticket and receive callvalue refund
* @param _amount Token Amount
* @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
* @param _gasPriceBid Gas price for L2 execution
* @param _data encoded data from router and user
* @return res abi encoded inbox sequence number
*/
// * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
function outboundTransferCustomRefund(
address _l1Token,
address _refundTo,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) external payable returns (bytes memory);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: Apache-2.0

/*
* Copyright 2020, Offchain Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

import "../../libraries/gateway/ITokenGateway.sol";
import "../../libraries/IERC165.sol";

/**
* @title Handles deposits from Erhereum into Arbitrum. Tokens are routered to their appropriate L1 gateway (Router itself also conforms to the Gateway itnerface).
* @notice Router also serves as an L1-L2 token address oracle.
*/
interface IL1GatewayRouter is ITokenGateway, IERC165 {
/**
* @notice Deposit ERC20 token from Ethereum into Arbitrum using the registered or otherwise default gateway
* @dev Some legacy gateway might not have the outboundTransferCustomRefund method and will revert, in such case use outboundTransfer instead
* L2 address alias will not be applied to the following types of addresses on L1:
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* @param _token L1 address of ERC20
* @param _refundTo Account, or its L2 alias if it have code in L1, to be credited with excess gas refund in L2
* @param _to Account to be credited with the tokens in the L2 (can be the user's L2 account or a contract), not subject to L2 aliasing
This account, or its L2 alias if it have code in L1, will also be able to cancel the retryable ticket and receive callvalue refund
* @param _amount Token Amount
* @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
* @param _gasPriceBid Gas price for L2 execution
* @param _data encoded data from router and user
* @return res abi encoded inbox sequence number
*/
function outboundTransferCustomRefund(
address _token,
address _refundTo,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) external payable returns (bytes memory);

/**
* @notice Allows L1 Token contract to trustlessly register its gateway.
* @param _gateway l1 gateway address
* @param _maxGas max gas for L2 retryable exrecution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost L2 retryable tick3et
* @param _creditBackAddress address for crediting back overpayment of _maxSubmissionCost
* @return Retryable ticket ID
*/
function setGateway(
address _gateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
address _creditBackAddress
) external payable returns (uint256);

/**
* @notice Allows L1 Token contract to trustlessly register its gateway. (other setGateway method allows excess eth recovery from _maxSubmissionCost and is recommended)
* @param _gateway l1 gateway address
* @param _maxGas max gas for L2 retryable exrecution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost L2 retryable tick3et
* @return Retryable ticket ID
*/
function setGateway(
address _gateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost
) external payable returns (uint256);

function owner() external view returns (address);

function inbox() external view returns (address);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,19 @@ import "arb-bridge-eth/contracts/libraries/ProxyUtil.sol";
import "../L1ArbitrumMessenger.sol";
import "../../libraries/gateway/GatewayMessageHandler.sol";
import "../../libraries/gateway/TokenGateway.sol";
import "./IL1ArbitrumGateway.sol";
import "../../libraries/ITransferAndCall.sol";
import "../../libraries/ERC165.sol";

/**
* @title Common interface for gatways on L1 messaging to Arbitrum.
*/
abstract contract L1ArbitrumGateway is L1ArbitrumMessenger, TokenGateway, ERC165 {
abstract contract L1ArbitrumGateway is
L1ArbitrumMessenger,
TokenGateway,
ERC165,
IL1ArbitrumGateway
{
using SafeERC20 for IERC20;
using Address for address;

Expand Down Expand Up @@ -323,7 +329,13 @@ abstract contract L1ArbitrumGateway is L1ArbitrumMessenger, TokenGateway, ERC165
return outboundCalldata;
}

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC165, IERC165)
returns (bool)
{
// registering interfaces that is added after arb-bridge-peripherals >1.0.11
// using function selector instead of single function interfaces to reduce bloat
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,22 @@ import "../L1ArbitrumMessenger.sol";
import "../../libraries/gateway/GatewayRouter.sol";
import "../../arbitrum/gateway/L2GatewayRouter.sol";
import "../../libraries/ERC165.sol";
import "./IL1GatewayRouter.sol";
import "./IL1ArbitrumGateway.sol";

/**
* @title Handles deposits from Erhereum into Arbitrum. Tokens are routered to their appropriate L1 gateway (Router itself also conforms to the Gateway itnerface).
* @notice Router also serves as an L1-L2 token address oracle.
*/
contract L1GatewayRouter is WhitelistConsumer, L1ArbitrumMessenger, GatewayRouter, ERC165 {
address public owner;
address public inbox;
contract L1GatewayRouter is
WhitelistConsumer,
L1ArbitrumMessenger,
GatewayRouter,
ERC165,
IL1GatewayRouter
{
address public override owner;
address public override inbox;

modifier onlyOwner() {
require(msg.sender == owner, "ONLY_OWNER");
Expand Down Expand Up @@ -145,38 +153,37 @@ contract L1GatewayRouter is WhitelistConsumer, L1ArbitrumMessenger, GatewayRoute

/**
* @notice Allows L1 Token contract to trustlessly register its gateway. (other setGateway method allows excess eth recovery from _maxSubmissionCost and is recommended)
* @param _gateway l1 gateway address
* @param _maxGas max gas for L2 retryable exrecution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost L2 retryable tick3et
* @param _maxGas max gas for L2 retryable exrecution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost L2 retryable tick3et
* @return Retryable ticket ID
*/
function setGateway(
address _gateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost
) external payable returns (uint256) {
) external payable override returns (uint256) {
return setGateway(_gateway, _maxGas, _gasPriceBid, _maxSubmissionCost, msg.sender);
}

/**
* @notice Allows L1 Token contract to trustlessly register its gateway.
* param _gateway l1 gateway address
* param _maxGas max gas for L2 retryable exrecution
* param _gasPriceBid gas price for L2 retryable ticket
* param _maxSubmissionCost base submission cost L2 retryable tick3et
* param _creditBackAddress address for crediting back overpayment of _maxSubmissionCost
* return Retryable ticket ID
* @param _gateway l1 gateway address
* @param _maxGas max gas for L2 retryable exrecution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost L2 retryable tick3et
* @param _creditBackAddress address for crediting back overpayment of _maxSubmissionCost
* @return Retryable ticket ID
*/
function setGateway(
address _gateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
address _creditBackAddress
) public payable returns (uint256) {
) public payable override returns (uint256) {
require(
ArbitrumEnabledToken(msg.sender).isArbitrumEnabled() == uint8(0xa4b1),
"NOT_ARB_ENABLED"
Expand Down Expand Up @@ -244,7 +251,7 @@ contract L1GatewayRouter is WhitelistConsumer, L1ArbitrumMessenger, GatewayRoute
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) public payable override returns (bytes memory) {
) public payable override(GatewayRouter, ITokenGateway) returns (bytes memory) {
_outboundTransferChecks(_maxGas, _gasPriceBid, _data);

return super.outboundTransfer(_token, _to, _amount, _maxGas, _gasPriceBid, _data);
Expand Down Expand Up @@ -276,20 +283,24 @@ contract L1GatewayRouter is WhitelistConsumer, L1ArbitrumMessenger, GatewayRoute
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) public payable override returns (bytes memory) {
// _refundTo will be rewritten to L2 alias if the address have code in L1
require(_refundTo != address(0), "INVALID_REFUND_ADDR");
_outboundTransferChecks(_maxGas, _gasPriceBid, _data);
) public payable virtual override returns (bytes memory) {
address gateway = getGateway(_token);
bytes memory gatewayData = GatewayMessageHandler.encodeFromRouterToGateway(
msg.sender,
_data
);

emit TransferRouted(_token, msg.sender, _to, gateway);
// here we use `IL1ArbitrumGateway` since we don't assume all ITokenGateway implements `outboundTransferCustomRefund`
return
super.outboundTransferCustomRefund(
IL1ArbitrumGateway(gateway).outboundTransferCustomRefund{ value: msg.value }(
_token,
_refundTo,
_to,
_amount,
_maxGas,
_gasPriceBid,
_data
gatewayData
);
}

Expand All @@ -299,7 +310,13 @@ contract L1GatewayRouter is WhitelistConsumer, L1ArbitrumMessenger, GatewayRoute
_;
}

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC165, IERC165)
returns (bool)
{
// registering interfaces that is added after arb-bridge-peripherals >1.0.11
// using function selector instead of single function interfaces to reduce bloat
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,46 +103,6 @@ abstract contract GatewayRouter is TokenGateway {
);
}

/**
* @notice Bridge ERC20 token using the registered or otherwise default gateway
* @dev Some legacy gateway might not have the outboundTransferCustomRefund method and will revert, in such case use outboundTransfer instead
* @param _token L1 address of ERC20
* @param _refundTo account to be credited with the excess gas refund in the L2, subject to L2 alias rewrite if its a L1 contract
* @param _to account to be credited with the tokens in the L2 (can be the user's L2 account or a contract)
* @param _amount Token Amount
* @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
* @param _gasPriceBid Gas price for L2 execution
* @param _data encoded data from router and user
* @return res abi encoded inbox sequence number
*/
function outboundTransferCustomRefund(
address _token,
address _refundTo,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) public payable virtual override returns (bytes memory) {
address gateway = getGateway(_token);
bytes memory gatewayData = GatewayMessageHandler.encodeFromRouterToGateway(
msg.sender,
_data
);

emit TransferRouted(_token, msg.sender, _to, gateway);
return
ITokenGateway(gateway).outboundTransferCustomRefund{ value: msg.value }(
_token,
_refundTo,
_to,
_amount,
_maxGas,
_gasPriceBid,
gatewayData
);
}

function getOutboundCalldata(
address _token,
address _from,
Expand Down
Loading

0 comments on commit afa5ff4

Please sign in to comment.