Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,22 @@

A barebones implementation of a gasback contract that implements [RIP-7767](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7767.md).

## Suggested deployment for OP stack chains
## Suggested setup for OP stack chains

### Requirements

- The `baseFeeVault` is deployed at `0x4200000000000000000000000000000000000019`.
- The `WITHDRAWAL_NETWORK` of the `baseFeeVault` is set to `1`.

### Via script

See `script/Delegate7702.s.sol` for an automated script that can help you deploy.

This script requires you to have the private key of the `baseFeeVault` recipient in your environment.

For more information on how to run a foundry script, see `https://getfoundry.sh/guides/scripting-with-solidity`.

### Manual steps

1. Deploy the `gasback` contract which will be used as an implementation via EIP-7702.

Expand All @@ -13,7 +28,7 @@ A barebones implementation of a gasback contract that implements [RIP-7767](http
`900000000000000000`
- `setGasbackMaxBaseFee(uint256)`
`115792089237316195423570985008687907853269984665640564039457584007913129639935`
- `setBaseFeeVault(address)`
- `setbaseFeeVault(address)`
`0x4200000000000000000000000000000000000019`

4. Put or leave some ETH into the EOA `RECIPIENT`, which will be the actual `gasback` contract.
Expand Down
61 changes: 61 additions & 0 deletions script/Delegate7702.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {Script} from "forge-std/Script.sol";
import "../src/Gasback.sol";

contract Delegate7702Script is Script {
function run() external {
uint256 privateKey = uint256(vm.envBytes32("PRIVATE_KEY"));

address gasbackImplementation = _deployWithNicks(type(Gasback).creationCode);
address deployer = vm.addr(privateKey);

vm.signAndAttachDelegation(gasbackImplementation, privateKey);
bytes memory code = deployer.code;
require(code.length > 0, "Not delegation detected");

vm.startBroadcast(privateKey);
Gasback(payable(deployer)).setGasbackRatioNumerator(900000000000000000);
vm.stopBroadcast();

vm.startBroadcast(privateKey);
Gasback(payable(deployer)).setGasbackMaxBaseFee(type(uint256).max);
vm.stopBroadcast();

vm.startBroadcast(privateKey);
Gasback(payable(deployer)).setBaseFeeVault(0x4200000000000000000000000000000000000019);
vm.stopBroadcast();
}

/// @dev Returns the address deployed via Nick's factory with salt `bytes32(0)`.
/// If the contract has already been deployed, skips deploying it.
function _deployWithNicks(bytes memory code) internal returns (address) {
address nicks = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
address instance = _predictDeterministicAddress(keccak256(code), 0, nicks);
if (instance.code.length != 0) return instance;
(bool success,) = nicks.call(abi.encodePacked(bytes32(0), code));
require(success && instance.code.length != 0, "Deployment via Nick's failed.");
return instance;
}

/// @dev Returns the address when a contract with initialization code hash,
/// `hash`, is deployed with `salt`, by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function _predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
/// @solidity memory-safe-assembly
assembly {
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, hash)
mstore(0x01, shl(96, deployer))
mstore(0x15, salt)
predicted := keccak256(0x00, 0x55)
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
}
12 changes: 8 additions & 4 deletions src/Gasback.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,27 +77,31 @@ contract Gasback {
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

/// @dev Withdraws ETH from this contract.
function withdraw(address to, uint256 amount) public onlySystemOrThis {
function withdraw(address to, uint256 amount) public onlySystemOrThis returns (bool) {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, 0x00, 0x00, 0x00, 0x00)) { revert(0x00, 0x00) }
}
return true;
}

/// @dev Sets the numerator for the gasback ratio.
function setGasbackRatioNumerator(uint256 value) public onlySystemOrThis {
function setGasbackRatioNumerator(uint256 value) public onlySystemOrThis returns (bool) {
require(value <= GASBACK_RATIO_DENOMINATOR);
_getGasbackStorage().gasbackRatioNumerator = value;
return true;
}

/// @dev Sets the max base fee.
function setGasbackMaxBaseFee(uint256 value) public onlySystemOrThis {
function setGasbackMaxBaseFee(uint256 value) public onlySystemOrThis returns (bool) {
_getGasbackStorage().gasbackMaxBaseFee = value;
return true;
}

/// @dev Sets the base fee vault.
function setBaseFeeVault(address value) public onlySystemOrThis {
function setBaseFeeVault(address value) public onlySystemOrThis returns (bool) {
_getGasbackStorage().baseFeeVault = value;
return true;
}

/// @dev Guards the function such that it can only be called either by
Expand Down