-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Research Area: ERC20 Token as the Gas Paying Token #67
Comments
An initial + very minimal diff to the diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal.sol b/packages/contracts-bedrock/src/L1/OptimismPortal.sol
index 30e6bd471..b6f511756 100644
--- a/packages/contracts-bedrock/src/L1/OptimismPortal.sol
+++ b/packages/contracts-bedrock/src/L1/OptimismPortal.sol
@@ -14,6 +14,7 @@ import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { ISemver } from "src/universal/ISemver.sol";
import { Constants } from "src/libraries/Constants.sol";
+import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
/// @custom:proxied
/// @title OptimismPortal
@@ -64,6 +65,13 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @custom:network-specific
SystemConfig public systemConfig;
+ /// @notice Address of the gas paying token.
+ /// @custom:network-specific
+ address public gasPayingToken;
+
+ /// @notice The total amount of gas paying asset in the contract.
+ uint256 internal _balance;
+
/// @notice Emitted when a transaction is deposited from L1 to L2.
/// The parameters of this event are read by the rollup node and used to derive deposit
/// transactions on L2.
@@ -100,6 +108,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
_l2Oracle: L2OutputOracle(address(0)),
_systemConfig: SystemConfig(address(0)),
_superchainConfig: SuperchainConfig(address(0))
+ _gasPayingToken: address(0),
});
}
@@ -107,10 +116,13 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @param _l2Oracle Contract of the L2OutputOracle.
/// @param _systemConfig Contract of the SystemConfig.
/// @param _superchainConfig Contract of the SuperchainConfig.
+ /// @param _gasPayingToken ERC20 token used to pay gas on L2.
+ /// Set to `address(0)` for ether.
function initialize(
L2OutputOracle _l2Oracle,
SystemConfig _systemConfig,
- SuperchainConfig _superchainConfig
+ SuperchainConfig _superchainConfig,
+ address _gasPayingToken
)
public
initializer
@@ -118,6 +130,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
l2Oracle = _l2Oracle;
systemConfig = _systemConfig;
superchainConfig = _superchainConfig;
+ gasPayingToken = _gasPayingToken;
if (l2Sender == address(0)) {
l2Sender = Constants.DEFAULT_L2_SENDER;
}
@@ -173,6 +186,15 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
return _byteCount * 16 + 21000;
}
+ /// @notice Retuns the balance of the contract in the smallest units.
+ function balance() public pure returns (uint256) {
+ if (gasPayingToken == address(0)) {
+ return address(this).balance;
+ } else {
+ return _balance;
+ }
+ }
+
/// @notice Accepts value so that users can send ETH directly to this contract and have the
/// funds be deposited to their address on L2. This is intended as a convenience
/// function for EOAs. Contracts should call the depositTransaction() function directly
@@ -344,7 +366,20 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
// 2. The amount of gas provided to the execution context of the target is at least the
// gas limit specified by the user. If there is not enough gas in the current context
// to accomplish this, `callWithMinGas` will revert.
- bool success = SafeCall.callWithMinGas(_tx.target, _tx.gasLimit, _tx.value, _tx.data);
+ bool success = true;
+ // If either is the gas paying asset or if the custom gas token chain withdrawal is a call (no value)
+ if (gasPayingToken == address(0) || _tx.value == 0) {
+ success = SafeCall.callWithMinGas(_tx.target, _tx.gasLimit, _tx.value, _tx.data);
+ } else {
+ _balance -= _tx.value;
+
+ // this reverts on failure
+ SafeTransferLib.safeTransfer({
+ token: gasPayingToken
+ to: _tx.target,
+ value: _tx.value
+ });
+ }
// Reset the l2Sender back to the default value.
l2Sender = Constants.DEFAULT_L2_SENDER;
@@ -361,6 +396,39 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
}
}
+ // This is the entrypoint to depositing an ERC20 token as a custom gas token.
+ function depositERC20Transaction(
+ address _to
+ uint256 _mint,
+ uint256 _value,
+ uint64 _gasLimit,
+ bool _isCreation,
+ bytes memory _data
+ ) public metered(_gasLimit) {
+ // Can only be called if an ERC20 token is used for gas paying on L2
+ require(gasPayingToken != address(0), "OptimismPortal: only custom gas token");
+
+ // Pulls ownership of custom gas token to portal
+ SafeTransferLib.safeTransferFrom2({
+ token: gasPayingToken,
+ from: msg.sender,
+ to: address(this),
+ amount: _mint,
+ });
+
+ // Overflow protection here ensures safety on L2 from overflows in balance
+ _balance += _mint;
+
+ _depositTransaction({
+ _to: _to,
+ _mint: _mint,
+ _value: _value,
+ _gasLimit: _gasLimit,
+ _isCreation: _isCreation,
+ _data: _data
+ });
+ }
+
/// @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in
/// deriving deposit transactions. Note that if a deposit is made by a contract, its
/// address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider
@@ -381,6 +449,28 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
payable
metered(_gasLimit)
{
+ if (gasPayingToken != address(0)) {
+ require(msg.value == 0, "OptimismPortal: cannot send ETH with custom gas token");
+ }
+
+ _depositTransaction({
+ _to: _to,
+ _mint: msg.value,
+ _value: _value,
+ _gasLimit: _gasLimit,
+ _isCreation: _isCreation,
+ _data: _data
+ });
+ }
+
+ function _depositTransaction(
+ address _to,
+ uint256 _mint,
+ uint256 _value,
+ uint64 _gasLimit,
+ bool _isCreation,
+ bytes memory _data
+ ) internal {
// Just to be safe, make sure that people specify address(0) as the target when doing
// contract creations.
if (_isCreation) {
@@ -406,7 +496,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
// Compute the opaque data that will be emitted as part of the TransactionDeposited event.
// We use opaque data so that we can update the TransactionDeposited event in the future
// without breaking the current interface.
- bytes memory opaqueData = abi.encodePacked(msg.value, _value, _gasLimit, _isCreation, _data);
+ bytes memory opaqueData = abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data);
// Emit a TransactionDeposited event so that the rollup node can derive a deposit
// transaction for this deposit. |
+1, need a standard here that is forwards compatible with interop/fault proofs etc |
We've previously deployed an Optimism-based rollup (pre-Bedrock) with ERC-20 as gas token, here is the implementation that we've come up with to keep the diff to a minimum It seems that there might be more changes if we try to keep WETH predeploy address to always be same as L1 native token, in our implementation we avoided that and updates mostly were confined to contracts. Also, we should keep in mind that ERC-20 gas token rollups might be used on top of other L2s so not sure adhering to WETH naming might be good, it's better to have WNATIVE or something similar I'm still not sure how to combine two implementations in one repo painlessly without disrupting existing rollup implementation and reusing existing tests, since really the logics might stay the same but slight differences in contracts' naming would require duplicating the calling logics |
We would like to add support for the ability to use an L1 based ERC20 token as the native gas token on L2. This feature needs to specified before consideration for merge into the OP Stack.
Some considerations:
The text was updated successfully, but these errors were encountered: