Skip to content

Commit

Permalink
Create a separate contract for EIP7702
Browse files Browse the repository at this point in the history
  • Loading branch information
akshay-ap committed Nov 21, 2024
1 parent a2c8f99 commit b4ca9ed
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 2 deletions.
4 changes: 2 additions & 2 deletions contracts/Safe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ contract Safe is
address paymentToken,
uint256 payment,
address payable paymentReceiver
) external override {
) external virtual override {
// setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
setupOwners(_owners, _threshold);
if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
Expand Down Expand Up @@ -214,7 +214,7 @@ contract Safe is
uint256 gasPrice,
address gasToken,
address payable refundReceiver
) private returns (uint256 payment) {
) internal returns (uint256 payment) {
// solhint-disable-next-line avoid-tx-origin
address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
if (gasToken == address(0)) {
Expand Down
70 changes: 70 additions & 0 deletions contracts/experimental/SafeEIP7702.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

import {ISafe} from "../interfaces/ISafe.sol";

Check warning on line 4 in contracts/experimental/SafeEIP7702.sol

View workflow job for this annotation

GitHub Actions / lint-solidity

imported name ISafe is not used
import {Safe} from "../Safe.sol";

/**
* @title SafeEIP7702 - A multisignature wallet with support for confirmations using signed messages based on EIP-712.
* This contract is designed to be used as a delegation designator for EIP-7702.
* @dev Most important concepts:
* - Threshold: Number of required confirmations for a Safe transaction.
* - Owners: List of addresses that control the Safe. They are the only ones that can add/remove owners, change the threshold and
* approve transactions. Managed in `OwnerManager`.
* - Transaction Hash: Hash of a transaction is calculated using the EIP-712 typed structured data hashing scheme.
* - Nonce: Each transaction should have a different nonce to prevent replay attacks.
* - Signature: A valid signature of an owner of the Safe for a transaction hash.
* - Guards: Guards are contracts that can execute pre- and post- transaction checks. There are two types of guards:
* 1. Transaction Guard: managed in `GuardManager` for transactions executed with `execTransaction`.
* 2. Module Guard: managed in `ModuleManager` for transactions executed with `execTransactionFromModule`
* - Modules: Modules are contracts that can be used to extend the write functionality of a Safe. Managed in `ModuleManager`.
* - Fallback: Fallback handler is a contract that can provide additional read-only functional for Safe. Managed in `FallbackManager`.
* Note: This version of the implementation contract doesn't emit events for the sake of gas efficiency and therefore requires a tracing node for indexing/
* For the events-based implementation see `SafeL2.sol`.
* @author Stefan George - @Georgi87
* @author Richard Meissner - @rmeissner
*/
contract SafeEIP7702 is Safe {
/**
* @inheritdoc ISafe
*/
function setup(
address[] calldata /*_owners*/,
uint256 /*_threshold*/,
address /*to*/,
bytes calldata /*data*/,
address /*fallbackHandler*/,
address /*paymentToken*/,
uint256 /*payment*/,
address payable /*paymentReceiver*/
) external pure override {
revertWithError("GS003");
}

function setupEIP7702(
address to,
bytes calldata data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address payable paymentReceiver,
bytes calldata signature
) external {
address[] memory _owners = new address[](1);
_owners[0] = address(this);

// setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
setupOwners(_owners, 1);
if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
// As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
setupModules(to, data);

if (payment > 0) {
// To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
// baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
}
checkSignatures(keccak256(abi.encode(to, data, fallbackHandler, paymentToken, payment, paymentReceiver)), signature);
emit SafeSetup(msg.sender, _owners, 1, to, fallbackHandler);
}
}
75 changes: 75 additions & 0 deletions contracts/experimental/SafeEIP7702L2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

// The import is used in the @inheritdoc, false positive
// solhint-disable-next-line no-unused-import
import {ModuleManager} from "../base/ModuleManager.sol";
import {Safe, Enum} from "../Safe.sol";

Check warning on line 7 in contracts/experimental/SafeEIP7702L2.sol

View workflow job for this annotation

GitHub Actions / lint-solidity

imported name Safe is not used
import {SafeEIP7702} from "./SafeEIP7702.sol";

/**
* @title SafeL2 - An implementation of the Safe contract that emits additional events on transaction executions.
* @notice For a more complete description of the Safe contract, please refer to the main Safe contract `Safe.sol`.
* @author Stefan George - @Georgi87
* @author Richard Meissner - @rmeissner
*/
contract SafeEIP7702L2 is SafeEIP7702 {
event SafeMultiSigTransaction(
address to,
uint256 value,
bytes data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes signatures,
// We combine nonce, sender and threshold into one to avoid stack too deep
// Dev note: additionalInfo should not contain `bytes`, as this complicates decoding
bytes additionalInfo
);

event SafeModuleTransaction(address module, address to, uint256 value, bytes data, Enum.Operation operation);

/**
* @inheritdoc Safe
*/
function onBeforeExecTransaction(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures
) internal override {
bytes memory additionalInfo;
{
additionalInfo = abi.encode(nonce, msg.sender, threshold);
}
emit SafeMultiSigTransaction(
to,
value,
data,
operation,
safeTxGas,
baseGas,
gasPrice,
gasToken,
refundReceiver,
signatures,
additionalInfo
);
}

/**
* @inheritdoc ModuleManager
*/
function onBeforeExecTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation) internal override {
emit SafeModuleTransaction(msg.sender, to, value, data, operation);
}
}
1 change: 1 addition & 0 deletions docs/error_codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- `GS000`: `Could not finish initialization`
- `GS001`: `Threshold needs to be defined`
- `GS002`: `A call to set up modules couldn't be executed because the destination account was not a contract`
- `GS003`: `Function not supported`

### General gas/ execution related
- `GS010`: `Not enough gas to execute Safe transaction`
Expand Down

0 comments on commit b4ca9ed

Please sign in to comment.